Skip to content

Commit 284c206

Browse files
committed
introduce new strategy for cartesian trace module layer updates
- make trace module layers 'data-driven', one layer per unique plot module. Use _module.layerName to denote reused plot methods. - pass layer to trace _module.plot (as 3rd arg) to not mutate node data and allow a given _module.plot method to trace in multiple layers - no need for previous `_plotMethods` stash attempt - rename 'imagelayer' -> 'heatmaplayer', 'maplayer' -> 'contourlayer' for consistency for other trace modules
1 parent 400bfe6 commit 284c206

File tree

20 files changed

+128
-118
lines changed

20 files changed

+128
-118
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

+2-9
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

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

+65-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,75 @@ 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 < traceLayerClasses.length; i++) {
193+
var className = traceLayerClasses[i];
194+
195+
for(var j = 0; j < modules.length; j++) {
196+
_module = modules[j];
197+
198+
if(_module.basePlotModule.name === 'cartesian' &&
199+
(_module.layerName || _module.name + 'layer') === className
200+
) {
201+
var plotMethod = _module.plot;
202+
203+
// plot all traces of this type on this subplot at once
204+
cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod);
205+
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];
209+
210+
if(cdModule.length) {
211+
layerData.push({
212+
className: className,
213+
plotMethod: plotMethod,
214+
cdModule: cdModule
215+
});
216+
}
217+
break;
218+
}
197219
}
198220
}
199221

200-
for(i = 0; i < plotMethodsToCall.length; i++) {
201-
plotMethod = plotMethodsToCall[i];
222+
var layers = plotinfo.plot.selectAll('g.mlayer')
223+
.data(layerData, function(d) { return d.className; });
202224

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];
225+
layers.enter().append('g')
226+
.attr('class', function(d) { return d.className; })
227+
.classed('mlayer', true);
209228

210-
plotMethod(gd, plotinfo, cdModule, transitionOpts, makeOnCompleteCallback);
211-
}
229+
layers.exit().remove();
230+
231+
layers.order();
232+
233+
layers.each(function(d) {
234+
var sel = d3.select(this);
235+
var className = d.className;
212236

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;
237+
d.plotMethod(
238+
gd, plotinfo, d.cdModule, sel,
239+
transitionOpts, makeOnCompleteCallback
240+
);
241+
242+
// layers that allow `cliponaxis: false`
243+
if(className !== 'scatterlayer' && className !== 'barlayer') {
244+
Drawing.setClipUrl(sel, plotinfo.layerClipId);
245+
}
246+
});
247+
248+
// call Scattergl.plot separately
249+
if(fullLayout._has('scattergl')) {
250+
_module = Registry.getModule('scattergl');
251+
cdModule = getModuleCalcData(cdSubplot, _module)[0];
252+
_module.plot(gd, plotinfo, cdModule);
253+
}
216254
}
217255

218256
exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
@@ -441,10 +479,6 @@ function makeSubplotLayer(gd, plotinfo) {
441479
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id);
442480
ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id);
443481
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-
}
448482
}
449483

450484
plotinfo.xlines

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

+2-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ module.exports = function plot(gd, plotinfo, cdcarpet) {
3030
}
3131
};
3232

33-
function plotOne(gd, plotinfo, cd) {
33+
function plotOne(gd, plotinfo, cd, carpetLayer) {
3434
var t = cd[0];
3535
var trace = cd[0].trace,
3636
xa = plotinfo.xaxis,
@@ -39,10 +39,9 @@ function plotOne(gd, plotinfo, cd) {
3939
bax = trace.baxis,
4040
fullLayout = gd._fullLayout;
4141

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

45-
var axisLayer = Lib.ensureSingle(gridLayer, 'g', 'carpet' + trace.uid).classed('trace', true);
44+
var axisLayer = Lib.ensureSingle(carpetLayer, 'g', 'carpet' + trace.uid).classed('trace', true);
4645
var minorLayer = Lib.ensureSingle(axisLayer, 'g', 'minorlayer');
4746
var majorLayer = Lib.ensureSingle(axisLayer, 'g', 'majorlayer');
4847
var boundaryLayer = Lib.ensureSingle(axisLayer, 'g', 'boundarylayer');

src/traces/contour/plot.js

+17-28
Original file line numberDiff line numberDiff line change
@@ -33,41 +33,30 @@ exports.plot = function plot(gd, plotinfo, cdcontours) {
3333
}
3434
};
3535

36-
function plotOne(gd, plotinfo, cd) {
37-
var trace = cd[0].trace,
38-
x = cd[0].x,
39-
y = cd[0].y,
40-
contours = trace.contours,
41-
uid = trace.uid,
42-
xa = plotinfo.xaxis,
43-
ya = plotinfo.yaxis,
44-
fullLayout = gd._fullLayout,
45-
id = 'contour' + uid,
46-
pathinfo = emptyPathinfo(contours, plotinfo, cd[0]);
47-
48-
if(trace.visible !== true) {
49-
fullLayout._paper.selectAll('.' + id + ',.hm' + uid).remove();
50-
fullLayout._infolayer.selectAll('.cb' + uid).remove();
51-
return;
52-
}
36+
function plotOne(gd, plotinfo, cd, contourLayer) {
37+
var trace = cd[0].trace;
38+
var x = cd[0].x;
39+
var y = cd[0].y;
40+
var contours = trace.contours;
41+
var id = 'contour' + trace.uid;
42+
var xa = plotinfo.xaxis;
43+
var ya = plotinfo.yaxis;
44+
var fullLayout = gd._fullLayout;
45+
var pathinfo = emptyPathinfo(contours, plotinfo, cd[0]);
5346

5447
// use a heatmap to fill - draw it behind the lines
48+
var heatmapColoringLayer = Lib.ensureSingle(contourLayer, 'g', 'heatmapcoloring');
49+
var cdheatmaps = [];
5550
if(contours.coloring === 'heatmap') {
5651
if(trace.zauto && (trace.autocontour === false)) {
5752
trace._input.zmin = trace.zmin =
5853
contours.start - contours.size / 2;
5954
trace._input.zmax = trace.zmax =
6055
trace.zmin + pathinfo.length * contours.size;
6156
}
62-
63-
heatmapPlot(gd, plotinfo, [cd]);
64-
}
65-
// in case this used to be a heatmap (or have heatmap fill)
66-
else {
67-
fullLayout._paper.selectAll('.hm' + uid).remove();
68-
fullLayout._infolayer.selectAll('g.rangeslider-container')
69-
.selectAll('.hm' + uid).remove();
57+
cdheatmaps = [cd];
7058
}
59+
heatmapPlot(gd, plotinfo, cdheatmaps, heatmapColoringLayer);
7160

7261
makeCrossings(pathinfo);
7362
findAllPaths(pathinfo);
@@ -90,15 +79,15 @@ function plotOne(gd, plotinfo, cd) {
9079
}
9180

9281
// draw everything
93-
var plotGroup = exports.makeContourGroup(plotinfo, cd, id);
82+
var plotGroup = exports.makeContourGroup(contourLayer, cd, id);
9483
makeBackground(plotGroup, perimeter, contours);
9584
makeFills(plotGroup, fillPathinfo, perimeter, contours);
9685
makeLinesAndLabels(plotGroup, pathinfo, gd, cd[0], contours, perimeter);
9786
clipGaps(plotGroup, plotinfo, fullLayout._clips, cd[0], perimeter);
9887
}
9988

100-
exports.makeContourGroup = function(plotinfo, cd, id) {
101-
var plotgroup = plotinfo.plot.select('.maplayer')
89+
exports.makeContourGroup = function(layer, cd, id) {
90+
var plotgroup = layer
10291
.selectAll('g.contour.' + id)
10392
.data(cd);
10493

src/traces/contourcarpet/plot.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module.exports = function plot(gd, plotinfo, cdcontours) {
3232
}
3333
};
3434

35-
function plotOne(gd, plotinfo, cd) {
35+
function plotOne(gd, plotinfo, cd, contourcarpetLayer) {
3636
var trace = cd[0].trace;
3737

3838
var carpet = trace._carpetTrace = lookupCarpet(gd, trace);
@@ -96,7 +96,7 @@ function plotOne(gd, plotinfo, cd) {
9696
mapPathinfo(pathinfo, ab2p);
9797

9898
// draw everything
99-
var plotGroup = contourPlot.makeContourGroup(plotinfo, cd, id);
99+
var plotGroup = contourPlot.makeContourGroup(contourcarpetLayer, cd, id);
100100

101101
// Compute the boundary path
102102
var seg, xp, yp, i;

src/traces/heatmap/plot.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module.exports = function(gd, plotinfo, cdheatmaps) {
2525
}
2626
};
2727

28-
function plotOne(gd, plotinfo, cd) {
28+
function plotOne(gd, plotinfo, cd, heatmapLayer) {
2929
var cd0 = cd[0];
3030
var trace = cd0.trace;
3131
var uid = trace.uid;
@@ -137,8 +137,7 @@ function plotOne(gd, plotinfo, cd) {
137137
// if image is entirely off-screen, don't even draw it
138138
var isOffScreen = (imageWidth <= 0 || imageHeight <= 0);
139139

140-
var plotgroup = plotinfo.plot.select('.imagelayer')
141-
.selectAll('g.hm.' + id)
140+
var plotgroup = heatmapLayer.selectAll('g.hm.' + id)
142141
.data(isOffScreen ? [] : [0]);
143142

144143
plotgroup.enter().append('g')

src/traces/histogram/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Histogram.supplyLayoutDefaults = require('../bar/layout_defaults');
3232
Histogram.calc = require('./calc');
3333
Histogram.setPositions = require('../bar/set_positions');
3434
Histogram.plot = require('../bar/plot');
35+
Histogram.layerName = 'barlayer';
3536
Histogram.style = require('../bar/style');
3637
Histogram.colorbar = require('../scatter/colorbar');
3738
Histogram.hoverPoints = require('./hover');

src/traces/histogram2d/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Histogram2D.attributes = require('./attributes');
1515
Histogram2D.supplyDefaults = require('./defaults');
1616
Histogram2D.calc = require('../heatmap/calc');
1717
Histogram2D.plot = require('../heatmap/plot');
18+
Histogram2D.layerName = 'heatmaplayer';
1819
Histogram2D.colorbar = require('../heatmap/colorbar');
1920
Histogram2D.style = require('../heatmap/style');
2021
Histogram2D.hoverPoints = require('./hover');

src/traces/histogram2dcontour/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Histogram2dContour.attributes = require('./attributes');
1515
Histogram2dContour.supplyDefaults = require('./defaults');
1616
Histogram2dContour.calc = require('../contour/calc');
1717
Histogram2dContour.plot = require('../contour/plot').plot;
18+
Histogram2dContour.layerName = 'contourlayer';
1819
Histogram2dContour.style = require('../contour/style');
1920
Histogram2dContour.colorbar = require('../contour/colorbar');
2021
Histogram2dContour.hoverPoints = require('../contour/hover');

0 commit comments

Comments
 (0)