Skip to content

Commit f771310

Browse files
authored
Merge pull request #2579 from plotly/uid-trace-removal
Even better cartesian trace updates and removal
2 parents e5c860a + 8c9a7bf commit f771310

File tree

31 files changed

+335
-230
lines changed

31 files changed

+335
-230
lines changed

src/components/rangeslider/draw.js

+1-6
Original file line numberDiff line numberDiff line change
@@ -456,8 +456,7 @@ function drawRangePlot(rangeSlider, gd, axisOpts, opts) {
456456
plotgroup: plotgroup,
457457
xaxis: xa,
458458
yaxis: ya,
459-
isRangePlot: true,
460-
plotMethods: opts._plotMethods
459+
isRangePlot: true
461460
};
462461

463462
if(isMainPlot) mainplotinfo = plotinfo;
@@ -467,10 +466,6 @@ function drawRangePlot(rangeSlider, gd, axisOpts, opts) {
467466
}
468467

469468
Cartesian.rangePlot(gd, plotinfo, filterRangePlotCalcData(calcData, id));
470-
471-
// stash list of plot methods on range-plot for later,
472-
// so that they can be called to clear traces of 'gone' modules
473-
opts._plotMethods = plotinfo.plotMethods;
474469
});
475470
}
476471

src/plot_api/subroutines.js

+5-33
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ var Titles = require('../components/titles');
2121
var ModeBar = require('../components/modebar');
2222

2323
var Axes = require('../plots/cartesian/axes');
24-
var cartesianConstants = require('../plots/cartesian/constants');
2524
var alignmentConstants = require('../constants/alignment');
2625
var axisConstraints = require('../plots/cartesian/constraints');
2726
var enforceAxisConstraints = axisConstraints.enforce;
@@ -199,15 +198,9 @@ exports.lsInner = function(gd) {
199198

200199
Drawing.setClipUrl(plotinfo.plot, plotClipId);
201200

202-
for(i = 0; i < cartesianConstants.traceLayerClasses.length; i++) {
203-
var layer = cartesianConstants.traceLayerClasses[i];
204-
if(layer !== 'scatterlayer' && layer !== 'barlayer') {
205-
plotinfo.plot.selectAll('g.' + layer).call(Drawing.setClipUrl, layerClipId);
206-
}
207-
}
208-
209201
// stash layer clipId value (null or same as clipId)
210-
// to DRY up Drawing.setClipUrl calls downstream
202+
// to DRY up Drawing.setClipUrl calls on trace-module and trace layers
203+
// downstream
211204
plotinfo.layerClipId = layerClipId;
212205

213206
// figure out extra axis line and tick positions as needed
@@ -495,34 +488,13 @@ exports.doCamera = function(gd) {
495488
exports.drawData = function(gd) {
496489
var fullLayout = gd._fullLayout;
497490
var calcdata = gd.calcdata;
498-
var rangesliderContainers = fullLayout._infolayer.selectAll('g.rangeslider-container');
499491
var i;
500492

501-
// in case of traces that were heatmaps or contour maps
502-
// previously, remove them and their colorbars explicitly
493+
// remove old colorbars explicitly
503494
for(i = 0; i < calcdata.length; i++) {
504495
var trace = calcdata[i][0].trace;
505-
var isVisible = (trace.visible === true);
506-
var uid = trace.uid;
507-
508-
if(!isVisible || !Registry.traceIs(trace, '2dMap')) {
509-
var query = (
510-
'.hm' + uid +
511-
',.contour' + uid +
512-
',#clip' + uid
513-
);
514-
515-
fullLayout._paper
516-
.selectAll(query)
517-
.remove();
518-
519-
rangesliderContainers
520-
.selectAll(query)
521-
.remove();
522-
}
523-
524-
if(!isVisible || !trace._module.colorbar) {
525-
fullLayout._infolayer.selectAll('.cb' + uid).remove();
496+
if(trace.visible !== true || !trace._module.colorbar) {
497+
fullLayout._infolayer.select('.cb' + trace.uid).remove();
526498
}
527499
}
528500

src/plots/cartesian/constants.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,16 @@ module.exports = {
6161
DFLTRANGEY: [-1, 4],
6262

6363
// Layers to keep trace types in the right order
64+
// N.B. each 'unique' plot method must have its own layer
6465
traceLayerClasses: [
65-
'imagelayer',
66-
'maplayer',
66+
'heatmaplayer',
67+
'contourcarpetlayer', 'contourlayer',
6768
'barlayer',
6869
'carpetlayer',
6970
'violinlayer',
7071
'boxlayer',
7172
'ohlclayer',
72-
'scatterlayer'
73+
'scattercarpetlayer', 'scatterlayer'
7374
],
7475

7576
layerValue2layerClass: {

src/plots/cartesian/index.js

+63-31
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
'use strict';
1111

1212
var d3 = require('d3');
13+
14+
var Registry = require('../../registry');
1315
var Lib = require('../../lib');
1416
var Plots = require('../plots');
15-
var getModuleCalcData = require('../get_data').getModuleCalcData;
17+
var Drawing = require('../../components/drawing');
1618

19+
var getModuleCalcData = require('../get_data').getModuleCalcData;
1720
var axisIds = require('./axis_ids');
1821
var constants = require('./constants');
1922
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
@@ -179,40 +182,73 @@ exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
179182
};
180183

181184
function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback) {
185+
var traceLayerClasses = constants.traceLayerClasses;
182186
var fullLayout = gd._fullLayout;
183187
var modules = fullLayout._modules;
184-
// list of plot methods to call (this list includes plot methods of 'gone' modules)
185-
var plotMethodsToCall = plotinfo.plotMethods || [];
186-
// list of plot methods of visible module on this subplot
187-
var plotMethods = [];
188-
var i, plotMethod;
189-
190-
for(i = 0; i < modules.length; i++) {
191-
var _module = modules[i];
192-
193-
if(_module.basePlotModule.name === 'cartesian') {
194-
plotMethod = _module.plot;
195-
Lib.pushUnique(plotMethodsToCall, plotMethod);
196-
Lib.pushUnique(plotMethods, plotMethod);
188+
var _module, cdModuleAndOthers, cdModule;
189+
190+
var layerData = [];
191+
192+
for(var i = 0; i < modules.length; i++) {
193+
_module = modules[i];
194+
var name = _module.name;
195+
196+
if(Registry.modules[name].categories.svg) {
197+
var className = (_module.layerName || name + 'layer');
198+
var plotMethod = _module.plot;
199+
200+
// plot all traces of this type on this subplot at once
201+
cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod);
202+
cdModule = cdModuleAndOthers[0];
203+
// don't need to search the found traces again - in fact we need to NOT
204+
// so that if two modules share the same plotter we don't double-plot
205+
cdSubplot = cdModuleAndOthers[1];
206+
207+
if(cdModule.length) {
208+
layerData.push({
209+
i: traceLayerClasses.indexOf(className),
210+
className: className,
211+
plotMethod: plotMethod,
212+
cdModule: cdModule
213+
});
214+
}
197215
}
198216
}
199217

200-
for(i = 0; i < plotMethodsToCall.length; i++) {
201-
plotMethod = plotMethodsToCall[i];
218+
layerData.sort(function(a, b) { return a.i - b.i; });
202219

203-
// plot all traces of this type on this subplot at once
204-
var cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod);
205-
var cdModule = cdModuleAndOthers[0];
206-
// don't need to search the found traces again - in fact we need to NOT
207-
// so that if two modules share the same plotter we don't double-plot
208-
cdSubplot = cdModuleAndOthers[1];
220+
var layers = plotinfo.plot.selectAll('g.mlayer')
221+
.data(layerData, function(d) { return d.className; });
209222

210-
plotMethod(gd, plotinfo, cdModule, transitionOpts, makeOnCompleteCallback);
211-
}
223+
layers.enter().append('g')
224+
.attr('class', function(d) { return d.className; })
225+
.classed('mlayer', true);
226+
227+
layers.exit().remove();
228+
229+
layers.order();
230+
231+
layers.each(function(d) {
232+
var sel = d3.select(this);
233+
var className = d.className;
212234

213-
// save list of plot methods on subplot for later,
214-
// so that they can be called to clear traces of 'gone' modules
215-
plotinfo.plotMethods = plotMethods;
235+
d.plotMethod(
236+
gd, plotinfo, d.cdModule, sel,
237+
transitionOpts, makeOnCompleteCallback
238+
);
239+
240+
// layers that allow `cliponaxis: false`
241+
if(className !== 'scatterlayer' && className !== 'barlayer') {
242+
Drawing.setClipUrl(sel, plotinfo.layerClipId);
243+
}
244+
});
245+
246+
// call Scattergl.plot separately
247+
if(fullLayout._has('scattergl')) {
248+
_module = Registry.getModule('scattergl');
249+
cdModule = getModuleCalcData(cdSubplot, _module)[0];
250+
_module.plot(gd, plotinfo, cdModule);
251+
}
216252
}
217253

218254
exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
@@ -441,10 +477,6 @@ function makeSubplotLayer(gd, plotinfo) {
441477
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id);
442478
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id);
443479
plotinfo.gridlayer.selectAll('g').sort(axisIds.idSort);
444-
445-
for(var i = 0; i < constants.traceLayerClasses.length; i++) {
446-
ensureSingle(plotinfo.plot, 'g', constants.traceLayerClasses[i]);
447-
}
448480
}
449481

450482
plotinfo.xlines

src/plots/get_data.js

+17
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,20 @@ exports.getSubplotData = function getSubplotData(data, type, subplotId) {
123123

124124
return subplotData;
125125
};
126+
127+
/**
128+
* Get a lookup object of trace uids corresponding in a given calcdata array.
129+
*
130+
* @param {array} calcdata: as in gd.calcdata (or a subset)
131+
* @return {object} lookup object of uids (`uid: 1`)
132+
*/
133+
exports.getUidsFromCalcData = function(calcdata) {
134+
var out = {};
135+
136+
for(var i = 0; i < calcdata.length; i++) {
137+
var trace = calcdata[i][0].trace;
138+
out[trace.uid] = 1;
139+
}
140+
141+
return out;
142+
};

src/plots/plots.js

+4-21
Original file line numberDiff line numberDiff line change
@@ -672,8 +672,6 @@ plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayou
672672
}
673673
}
674674

675-
var hasPaper = !!oldFullLayout._paper;
676-
var hasInfoLayer = !!oldFullLayout._infolayer;
677675
var hadGl = oldFullLayout._has && oldFullLayout._has('gl');
678676
var hasGl = newFullLayout._has && newFullLayout._has('gl');
679677

@@ -684,6 +682,8 @@ plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayou
684682
}
685683
}
686684

685+
var hasInfoLayer = !!oldFullLayout._infolayer;
686+
687687
oldLoop:
688688
for(i = 0; i < oldFullData.length; i++) {
689689
var oldTrace = oldFullData[i],
@@ -695,26 +695,9 @@ plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayou
695695
if(oldUid === newTrace.uid) continue oldLoop;
696696
}
697697

698-
var query = (
699-
'.hm' + oldUid +
700-
',.contour' + oldUid +
701-
',.carpet' + oldUid +
702-
',#clip' + oldUid +
703-
',.trace' + oldUid
704-
);
705-
706-
// clean old heatmap, contour traces and clip paths
707-
// that rely on uid identifiers
708-
if(hasPaper) {
709-
oldFullLayout._paper.selectAll(query).remove();
710-
}
711-
712-
// clean old colorbars and range slider plot
698+
// clean old colorbars
713699
if(hasInfoLayer) {
714-
oldFullLayout._infolayer.selectAll('.cb' + oldUid).remove();
715-
716-
oldFullLayout._infolayer.selectAll('g.rangeslider-container')
717-
.selectAll(query).remove();
700+
oldFullLayout._infolayer.select('.cb' + oldUid).remove();
718701
}
719702
}
720703

src/traces/bar/plot.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,12 @@ var attributes = require('./attributes'),
3030
// padding in pixels around text
3131
var TEXTPAD = 3;
3232

33-
module.exports = function plot(gd, plotinfo, cdbar) {
34-
var xa = plotinfo.xaxis,
35-
ya = plotinfo.yaxis,
36-
fullLayout = gd._fullLayout;
33+
module.exports = function plot(gd, plotinfo, cdbar, barLayer) {
34+
var xa = plotinfo.xaxis;
35+
var ya = plotinfo.yaxis;
36+
var fullLayout = gd._fullLayout;
3737

38-
var bartraces = plotinfo.plot.select('.barlayer')
39-
.selectAll('g.trace.bars')
38+
var bartraces = barLayer.selectAll('g.trace.bars')
4039
.data(cdbar, function(d) { return d[0].trace.uid; });
4140

4241
bartraces.enter().append('g')

src/traces/box/plot.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@ var Drawing = require('../../components/drawing');
1717
var JITTERCOUNT = 5; // points either side of this to include
1818
var JITTERSPREAD = 0.01; // fraction of IQR to count as "dense"
1919

20-
function plot(gd, plotinfo, cdbox) {
20+
function plot(gd, plotinfo, cdbox, boxLayer) {
2121
var fullLayout = gd._fullLayout;
2222
var xa = plotinfo.xaxis;
2323
var ya = plotinfo.yaxis;
2424

25-
var boxtraces = plotinfo.plot.select('.boxlayer')
26-
.selectAll('g.trace.boxes')
25+
var boxtraces = boxLayer.selectAll('g.trace.boxes')
2726
.data(cdbox, function(d) { return d[0].trace.uid; });
2827

2928
boxtraces.enter().append('g')

src/traces/candlestick/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module.exports = {
3636
supplyDefaults: require('./defaults'),
3737
calc: require('./calc'),
3838
plot: require('../box/plot').plot,
39+
layerName: 'boxlayer',
3940
style: require('../box/style'),
4041
hoverPoints: require('../ohlc/hover'),
4142
selectPoints: require('../ohlc/select')

src/traces/carpet/plot.js

+15-10
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,26 @@ var orientText = require('./orient_text');
1717
var svgTextUtils = require('../../lib/svg_text_utils');
1818
var Lib = require('../../lib');
1919
var alignmentConstants = require('../../constants/alignment');
20+
var getUidsFromCalcData = require('../../plots/get_data').getUidsFromCalcData;
2021

21-
module.exports = function plot(gd, plotinfo, cdcarpet) {
22-
if(!cdcarpet.length) {
23-
plotinfo.plot.select('.carpetlayer')
24-
.selectAll('g.trace')
25-
.remove();
26-
}
22+
module.exports = function plot(gd, plotinfo, cdcarpet, carpetLayer) {
23+
var uidLookup = getUidsFromCalcData(cdcarpet);
24+
25+
carpetLayer.selectAll('g.trace').each(function() {
26+
var classString = d3.select(this).attr('class');
27+
var oldUid = classString.split('carpet')[1].split(/\s/)[0];
28+
29+
if(!uidLookup[oldUid]) {
30+
d3.select(this).remove();
31+
}
32+
});
2733

2834
for(var i = 0; i < cdcarpet.length; i++) {
29-
plotOne(gd, plotinfo, cdcarpet[i]);
35+
plotOne(gd, plotinfo, cdcarpet[i], carpetLayer);
3036
}
3137
};
3238

33-
function plotOne(gd, plotinfo, cd) {
39+
function plotOne(gd, plotinfo, cd, carpetLayer) {
3440
var t = cd[0];
3541
var trace = cd[0].trace,
3642
xa = plotinfo.xaxis,
@@ -39,10 +45,9 @@ function plotOne(gd, plotinfo, cd) {
3945
bax = trace.baxis,
4046
fullLayout = gd._fullLayout;
4147

42-
var gridLayer = plotinfo.plot.select('.carpetlayer');
4348
var clipLayer = fullLayout._clips;
4449

45-
var axisLayer = Lib.ensureSingle(gridLayer, 'g', 'carpet' + trace.uid).classed('trace', true);
50+
var axisLayer = Lib.ensureSingle(carpetLayer, 'g', 'carpet' + trace.uid).classed('trace', true);
4651
var minorLayer = Lib.ensureSingle(axisLayer, 'g', 'minorlayer');
4752
var majorLayer = Lib.ensureSingle(axisLayer, 'g', 'majorlayer');
4853
var boundaryLayer = Lib.ensureSingle(axisLayer, 'g', 'boundarylayer');

0 commit comments

Comments
 (0)