Skip to content

Commit 067ee11

Browse files
authored
Merge pull request #6918 from plotly/trace-zindex
Add z-index stacking attribute to cartesian SVG traces
2 parents 51ef6c1 + efcd49f commit 067ee11

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+732
-48
lines changed

Diff for: draftlogs/6918_add.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Add a new attribute `zorder` to SVG based Cartesian traces (not to WebGL traces). Traces with higher `zorder` values are drawn in front of traces with lower `zorder` values. This feature was anonymously sponsored: thank you to our sponsor!

Diff for: src/plots/cartesian/index.js

+55-31
Original file line numberDiff line numberDiff line change
@@ -130,21 +130,27 @@ exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
130130
var calcdata = gd.calcdata;
131131
var i;
132132

133+
// Traces is a list of trace indices to (re)plot. If it's not provided,
134+
// then it's a complete replot so we create a new list and add all trace indices
135+
// which are in calcdata.
136+
133137
if(!Array.isArray(traces)) {
134138
// If traces is not provided, then it's a complete replot and missing
135139
// traces are removed
136140
traces = [];
137141
for(i = 0; i < calcdata.length; i++) traces.push(i);
138142
}
139143

144+
// For each subplot
140145
for(i = 0; i < subplots.length; i++) {
141146
var subplot = subplots[i];
142147
var subplotInfo = fullLayout._plots[subplot];
143148

144-
// Get all calcdata for this subplot:
149+
// Get all calcdata (traces) for this subplot:
145150
var cdSubplot = [];
146151
var pcd;
147152

153+
// For each trace
148154
for(var j = 0; j < calcdata.length; j++) {
149155
var cd = calcdata[j];
150156
var trace = cd[0].trace;
@@ -178,7 +184,7 @@ exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
178184
pcd = cd;
179185
}
180186
}
181-
187+
// Plot the traces for this subplot
182188
plotOne(gd, subplotInfo, cdSubplot, transitionOpts, makeOnCompleteCallback);
183189
}
184190
};
@@ -189,41 +195,60 @@ function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback
189195
var modules = fullLayout._modules;
190196
var _module, cdModuleAndOthers, cdModule;
191197

198+
// Separate traces by zorder and plot each zorder group separately
199+
// TODO: Performance
200+
var traceZorderGroups = {};
201+
for(var t = 0; t < cdSubplot.length; t++) {
202+
var trace = cdSubplot[t][0].trace;
203+
var zi = trace.zorder || 0;
204+
if(!traceZorderGroups[zi]) traceZorderGroups[zi] = [];
205+
traceZorderGroups[zi].push(cdSubplot[t]);
206+
}
207+
192208
var layerData = [];
193209
var zoomScaleQueryParts = [];
194210

195-
for(var i = 0; i < modules.length; i++) {
196-
_module = modules[i];
197-
var name = _module.name;
198-
var categories = Registry.modules[name].categories;
199-
200-
if(categories.svg) {
201-
var className = (_module.layerName || name + 'layer');
202-
var plotMethod = _module.plot;
203-
204-
// plot all visible traces of this type on this subplot at once
205-
cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod);
206-
cdModule = cdModuleAndOthers[0];
207-
// don't need to search the found traces again - in fact we need to NOT
208-
// so that if two modules share the same plotter we don't double-plot
209-
cdSubplot = cdModuleAndOthers[1];
210-
211-
if(cdModule.length) {
212-
layerData.push({
213-
i: traceLayerClasses.indexOf(className),
214-
className: className,
215-
plotMethod: plotMethod,
216-
cdModule: cdModule
217-
});
218-
}
211+
// Plot each zorder group in ascending order
212+
var zindices = Object.keys(traceZorderGroups)
213+
.map(Number)
214+
.sort(Lib.sorterAsc);
215+
for(var z = 0; z < zindices.length; z++) {
216+
var zorder = zindices[z];
217+
// For each "module" (trace type)
218+
for(var i = 0; i < modules.length; i++) {
219+
_module = modules[i];
220+
var name = _module.name;
221+
var categories = Registry.modules[name].categories;
222+
223+
if(categories.svg) {
224+
var className = (_module.layerName || name + 'layer') + (z ? Number(z) + 1 : '');
225+
var plotMethod = _module.plot;
226+
227+
// plot all visible traces of this type on this subplot at once
228+
cdModuleAndOthers = getModuleCalcData(cdSubplot, plotMethod, zorder);
229+
cdModule = cdModuleAndOthers[0];
230+
// don't need to search the found traces again - in fact we need to NOT
231+
// so that if two modules share the same plotter we don't double-plot
232+
cdSubplot = cdModuleAndOthers[1];
233+
234+
if(cdModule.length) {
235+
layerData.push({
236+
i: traceLayerClasses.indexOf(className),
237+
zorder: z,
238+
className: className,
239+
plotMethod: plotMethod,
240+
cdModule: cdModule
241+
});
242+
}
219243

220-
if(categories.zoomScale) {
221-
zoomScaleQueryParts.push('.' + className);
244+
if(categories.zoomScale) {
245+
zoomScaleQueryParts.push('.' + className);
246+
}
222247
}
223248
}
224249
}
225-
226-
layerData.sort(function(a, b) { return a.i - b.i; });
250+
// Sort the layers primarily by z, then by i
251+
layerData.sort(function(a, b) { return (a.zorder || 0) - (b.zorder || 0) || a.i - b.i; });
227252

228253
var layers = plotinfo.plot.selectAll('g.mlayer')
229254
.data(layerData, function(d) { return d.className; });
@@ -434,7 +459,6 @@ function makeSubplotData(gd) {
434459
}
435460
subplotData[i] = d;
436461
}
437-
438462
return subplotData;
439463
}
440464

Diff for: src/plots/get_data.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ exports.getSubplotCalcData = function(calcData, type, subplotId) {
3939
* @param {array} calcdata: as in gd.calcdata
4040
* @param {object|string|fn} arg1:
4141
* the plotting module, or its name, or its plot method
42-
*
42+
* @param {int} arg2: (optional) zorder to filter on
4343
* @return {array[array]} [foundCalcdata, remainingCalcdata]
4444
*/
45-
exports.getModuleCalcData = function(calcdata, arg1) {
45+
exports.getModuleCalcData = function(calcdata, arg1, arg2) {
4646
var moduleCalcData = [];
4747
var remainingCalcData = [];
4848

@@ -57,10 +57,12 @@ exports.getModuleCalcData = function(calcdata, arg1) {
5757
if(!plotMethod) {
5858
return [moduleCalcData, calcdata];
5959
}
60+
var zorder = arg2;
6061

6162
for(var i = 0; i < calcdata.length; i++) {
6263
var cd = calcdata[i];
6364
var trace = cd[0].trace;
65+
var filterByZ = (trace.zorder !== undefined);
6466
// N.B.
6567
// - 'legendonly' traces do not make it past here
6668
// - skip over 'visible' traces that got trimmed completely during calc transforms
@@ -70,7 +72,7 @@ exports.getModuleCalcData = function(calcdata, arg1) {
7072
// would suggest), but by 'module plot method' so that if some traces
7173
// share the same module plot method (e.g. bar and histogram), we
7274
// only call it one!
73-
if(trace._module && trace._module.plot === plotMethod) {
75+
if(trace._module && trace._module.plot === plotMethod && (!filterByZ || trace.zorder === zorder)) {
7476
moduleCalcData.push(cd);
7577
} else {
7678
remainingCalcData.push(cd);

Diff for: src/traces/bar/attributes.js

+1
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ module.exports = {
226226
textfont: scatterAttrs.unselected.textfont,
227227
editType: 'style'
228228
},
229+
zorder: scatterAttrs.zorder,
229230

230231
_deprecated: {
231232
bardir: {

Diff for: src/traces/bar/defaults.js

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
2929
coerce('xhoverformat');
3030
coerce('yhoverformat');
3131

32+
coerce('zorder');
33+
3234
coerce('orientation', (traceOut.x && !traceOut.y) ? 'h' : 'v');
3335
coerce('base');
3436
coerce('offset');

Diff for: src/traces/bar/style.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ var attributeOutsideTextFont = attributes.outsidetextfont;
1414
var helpers = require('./helpers');
1515

1616
function style(gd) {
17-
var s = d3.select(gd).selectAll('g.barlayer').selectAll('g.trace');
17+
var s = d3.select(gd).selectAll('g[class^="barlayer"]').selectAll('g.trace');
1818
resizeText(gd, s, 'bar');
1919

2020
var barcount = s.size();

Diff for: src/traces/box/attributes.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -451,5 +451,6 @@ module.exports = {
451451
'Do the hover effects highlight individual boxes ',
452452
'or sample points or both?'
453453
].join(' ')
454-
}
454+
},
455+
zorder: scatterAttrs.zorder
455456
};

Diff for: src/traces/box/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
6767
if(notched) coerce('notchwidth');
6868

6969
handlePointsDefaults(traceIn, traceOut, coerce, {prefix: 'box'});
70+
coerce('zorder');
7071
}
7172

7273
function handleSampleDefaults(traceIn, traceOut, coerce, layout) {

Diff for: src/traces/candlestick/attributes.js

+1
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,5 @@ module.exports = {
5353
whiskerwidth: extendFlat({}, boxAttrs.whiskerwidth, { dflt: 0 }),
5454

5555
hoverlabel: OHLCattrs.hoverlabel,
56+
zorder: boxAttrs.zorder
5657
};

Diff for: src/traces/candlestick/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
3131
coerce('whiskerwidth');
3232

3333
layout._requestRangeslider[traceOut.xaxis] = true;
34+
coerce('zorder');
3435
};
3536

3637
function handleDirection(traceIn, traceOut, coerce, direction) {

Diff for: src/traces/carpet/attributes.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ var carpetFont = fontAttrs({
88
editType: 'calc',
99
description: 'The default font used for axis & tick labels on this carpet'
1010
});
11+
12+
var zorder = require('../scatter/attributes').zorder;
13+
1114
// TODO: inherit from global font
1215
carpetFont.family.dflt = '"Open Sans", verdana, arial, sans-serif';
1316
carpetFont.size.dflt = 12;
@@ -112,5 +115,6 @@ module.exports = {
112115
'Individual pieces can override this.'
113116
].join(' ')
114117
},
115-
transforms: undefined
118+
transforms: undefined,
119+
zorder: zorder
116120
};

Diff for: src/traces/carpet/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, dfltColor, fullLayou
4545
if(traceOut._cheater) {
4646
coerce('cheaterslope');
4747
}
48+
coerce('zorder');
4849
};

Diff for: src/traces/contour/attributes.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,8 @@ module.exports = extendFlat({
265265
].join(' ')
266266
}),
267267
editType: 'plot'
268-
}
268+
},
269+
zorder: scatterAttrs.zorder
269270
},
270271
colorScaleAttrs('', {
271272
cLetter: 'z',

Diff for: src/traces/contour/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
5151
) {
5252
handleHeatmapLabelDefaults(coerce, layout);
5353
}
54+
coerce('zorder');
5455
};

Diff for: src/traces/contourcarpet/attributes.js

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ module.exports = extendFlat({
7070
editType: 'plot'
7171
},
7272

73+
zorder: contourAttrs.zorder,
7374
transforms: undefined
7475
},
7576

Diff for: src/traces/contourcarpet/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
5555
traceOut._defaultColor = defaultColor;
5656
traceOut._length = null;
5757
}
58+
coerce('zorder');
5859
};

Diff for: src/traces/funnel/attributes.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ module.exports = {
109109
},
110110

111111
offsetgroup: barAttrs.offsetgroup,
112-
alignmentgroup: barAttrs.alignmentgroup
112+
alignmentgroup: barAttrs.alignmentgroup,
113+
zorder: barAttrs.zorder
113114
};
114115

115116
function funnelMarker() {

Diff for: src/traces/funnel/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
6161
coerce('connector.line.dash');
6262
}
6363
}
64+
coerce('zorder');
6465
}
6566

6667
function defaultFillColor(markerColor) {

Diff for: src/traces/funnel/style.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ var resizeText = require('../bar/uniform_text').resizeText;
1010
var styleTextPoints = barStyle.styleTextPoints;
1111

1212
function style(gd, cd, sel) {
13-
var s = sel ? sel : d3.select(gd).selectAll('g.funnellayer').selectAll('g.trace');
13+
var s = sel ? sel : d3.select(gd).selectAll('g[class^="funnellayer"]').selectAll('g.trace');
1414
resizeText(gd, s, 'funnel');
1515

1616
s.style('opacity', function(d) { return d[0].trace.opacity; });

Diff for: src/traces/heatmap/attributes.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ module.exports = extendFlat({
132132
description: 'Sets the text font.'
133133
}),
134134

135-
showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false})
135+
showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}),
136+
zorder: scatterAttrs.zorder
136137
}, {
137138
transforms: undefined
138139
},

Diff for: src/traces/heatmap/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
3636
coerce('connectgaps', Lib.isArray1D(traceOut.z) && (traceOut.zsmooth !== false));
3737

3838
colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'});
39+
coerce('zorder');
3940
};

Diff for: src/traces/histogram/attributes.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -248,5 +248,7 @@ module.exports = {
248248

249249
_deprecated: {
250250
bardir: barAttrs._deprecated.bardir
251-
}
251+
},
252+
253+
zorder: barAttrs.zorder
252254
};

Diff for: src/traces/histogram/defaults.js

+2
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
7474
var errorBarsSupplyDefaults = Registry.getComponentMethod('errorbars', 'supplyDefaults');
7575
errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'y'});
7676
errorBarsSupplyDefaults(traceIn, traceOut, lineColor || Color.defaultLine, {axis: 'x', inherit: 'y'});
77+
78+
coerce('zorder');
7779
};

Diff for: src/traces/image/attributes.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
var baseAttrs = require('../../plots/attributes');
4+
var zorder = require('../scatter/attributes').zorder;
45
var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs;
56
var extendFlat = require('../../lib/extend').extendFlat;
67
var colormodel = require('./constants').colormodel;
@@ -133,5 +134,6 @@ module.exports = extendFlat({
133134
keys: ['z', 'color', 'colormodel']
134135
}),
135136

137+
zorder: zorder,
136138
transforms: undefined
137139
});

Diff for: src/traces/image/defaults.js

+2
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,6 @@ module.exports = function supplyDefaults(traceIn, traceOut) {
4545
coerce('hovertemplate');
4646

4747
traceOut._length = null;
48+
49+
coerce('zorder');
4850
};

Diff for: src/traces/ohlc/attributes.js

+2
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,6 @@ module.exports = {
133133
].join(' ')
134134
}
135135
}),
136+
137+
zorder: scatterAttrs.zorder
136138
};

Diff for: src/traces/ohlc/defaults.js

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
3131
coerce('tickwidth');
3232

3333
layout._requestRangeslider[traceOut.xaxis] = true;
34+
35+
coerce('zorder');
3436
};
3537

3638
function handleDirection(traceIn, traceOut, coerce, direction) {

Diff for: src/traces/scatter/attributes.js

+10
Original file line numberDiff line numberDiff line change
@@ -686,4 +686,14 @@ module.exports = {
686686
arrayOk: true,
687687
description: 'Sets the text font.'
688688
}),
689+
zorder: {
690+
valType: 'integer',
691+
dflt: 0,
692+
editType: 'plot',
693+
description: [
694+
'Sets the layer on which this trace is displayed, relative to',
695+
'other SVG traces on the same subplot. SVG traces with higher `zorder`',
696+
'appear in front of those with lower `zorder`.'
697+
].join(' ')
698+
}
689699
};

0 commit comments

Comments
 (0)