From c80571362ade9579da291e07f841067141841c17 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 17 Jun 2024 16:08:55 -0400 Subject: [PATCH 1/7] put main subplot plot inside overplot --- src/plots/cartesian/index.js | 3 ++- test/jasmine/tests/animate_test.js | 2 +- test/jasmine/tests/bar_test.js | 2 +- test/jasmine/tests/cartesian_interact_test.js | 2 +- test/jasmine/tests/cartesian_test.js | 10 +++++----- test/jasmine/tests/click_test.js | 4 ++-- test/jasmine/tests/scatter_test.js | 2 +- test/jasmine/tests/select_test.js | 2 +- test/jasmine/tests/toimage_test.js | 5 ++++- 9 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 2a52d2628e0..82cde3551e7 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -507,8 +507,9 @@ function makeSubplotLayer(gd, plotinfo) { ensureSingle(plotgroup, 'g', 'yaxislayer-below'); plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below'); - plotinfo.plot = ensureSingle(plotgroup, 'g', 'plot'); + ensureSingle(plotgroup, 'g', 'plot'); plotinfo.overplot = ensureSingle(plotgroup, 'g', 'overplot'); + plotinfo.plot = ensureSingle(plotinfo.overplot, 'g', id); plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above'); plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above'); diff --git a/test/jasmine/tests/animate_test.js b/test/jasmine/tests/animate_test.js index 1e7e01d199a..cba22278fcc 100644 --- a/test/jasmine/tests/animate_test.js +++ b/test/jasmine/tests/animate_test.js @@ -1008,7 +1008,7 @@ describe('animating scatter traces', function() { // assert what Cartesian.transitionAxes does function getSubplotTranslate() { - var sp = d3Select(gd).select('.subplot.xy > .plot'); + var sp = d3Select(gd).select('.subplot.xy > .overplot').select('.xy'); return sp.attr('transform') .split('translate(')[1].split(')')[0] .split(',') diff --git a/test/jasmine/tests/bar_test.js b/test/jasmine/tests/bar_test.js index 2d6a20d9bb6..4ddd88c65e8 100644 --- a/test/jasmine/tests/bar_test.js +++ b/test/jasmine/tests/bar_test.js @@ -2087,7 +2087,7 @@ describe('A bar plot', function() { } function _assert(layerClips, barDisplays, barTextDisplays, barClips) { - var subplotLayer = d3Select('.plot'); + var subplotLayer = d3Select('.overplot').select('.xy'); var barLayer = subplotLayer.select('.barlayer'); _assertClip(subplotLayer, layerClips[0], 1, 'subplot layer'); diff --git a/test/jasmine/tests/cartesian_interact_test.js b/test/jasmine/tests/cartesian_interact_test.js index c2b97c67071..5332f9237c9 100644 --- a/test/jasmine/tests/cartesian_interact_test.js +++ b/test/jasmine/tests/cartesian_interact_test.js @@ -1592,7 +1592,7 @@ describe('axis zoom/pan and main plot zoom', function() { [['yaxis'], [-0.318, 3.318]], [['xaxis2', 'yaxis2'], [-0.588, 8.824]] ]); - x2y2 = d3Select('.subplot.x2y2 .plot'); + x2y2 = d3Select('.subplot.x2y2 .overplot').select('.x2y2'); expect(x2y2.attr('transform')).toBe('translate(50,50)'); mx = gd._fullLayout.xaxis._m; my = gd._fullLayout.yaxis._m; diff --git a/test/jasmine/tests/cartesian_test.js b/test/jasmine/tests/cartesian_test.js index b31d331d998..a21349f9663 100644 --- a/test/jasmine/tests/cartesian_test.js +++ b/test/jasmine/tests/cartesian_test.js @@ -609,7 +609,7 @@ describe('subplot creation / deletion:', function() { var fig = Lib.extendDeep({}, require('../../image/mocks/overlaying-axis-lines.json')); function _assert(xyCnt, x2y2Cnt) { - expect(d3Select('.subplot.xy').select('.plot').selectAll('.trace').size()) + expect(d3Select('.subplot.xy').select('.overplot').select('.xy').selectAll('.trace').size()) .toBe(xyCnt, 'has correct xy subplot trace count'); expect(d3Select('.overplot').select('.x2y2').selectAll('.trace').size()) .toBe(x2y2Cnt, 'has correct x2y2 oveylaid subplot trace count'); @@ -759,7 +759,7 @@ describe('subplot creation / deletion:', function() { _assert('x2/y2 both overlays', { xtickParent: 'xaxislayer-above', x2tickParent: 'x2y2-x', - trace0Parent: 'plot', + trace0Parent: 'xy', trace1Parent: 'x2y2' }); }) @@ -770,8 +770,8 @@ describe('subplot creation / deletion:', function() { _assert('x2 free / y2 overlaid', { xtickParent: 'xaxislayer-above', x2tickParent: 'xaxislayer-above', - trace0Parent: 'plot', - trace1Parent: 'plot' + trace0Parent: 'xy', + trace1Parent: 'x2y2' }); }) .then(function() { @@ -781,7 +781,7 @@ describe('subplot creation / deletion:', function() { _assert('back to x2/y2 both overlays', { xtickParent: 'xaxislayer-above', x2tickParent: 'x2y2-x', - trace0Parent: 'plot', + trace0Parent: 'xy', trace1Parent: 'x2y2' }); }) diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js index 1ce7cddb122..2312b7fee41 100644 --- a/test/jasmine/tests/click_test.js +++ b/test/jasmine/tests/click_test.js @@ -1182,14 +1182,14 @@ describe('dragbox', function() { var pos = getRectCenter(node); var fns = drag.makeFns({pos0: pos, dpos: [50, 0]}); - assertScale(d3Select('.plot').node(), 1, 1); + assertScale(d3Select('.overplot').select('.xy').node(), 1, 1); d3SelectAll('.point').each(function() { assertScale(this, 1, 1); }); fns.start().then(function() { - assertScale(d3Select('.plot').node(), 1.14, 1); + assertScale(d3Select('.overplot').select('.xy').node(), 1.14, 1); d3Select('.scatterlayer').selectAll('.point').each(function() { assertScale(this, 0.87, 1); diff --git a/test/jasmine/tests/scatter_test.js b/test/jasmine/tests/scatter_test.js index 4b191bc2fd2..f07536631ce 100644 --- a/test/jasmine/tests/scatter_test.js +++ b/test/jasmine/tests/scatter_test.js @@ -2053,7 +2053,7 @@ describe('Test scatter *clipnaxis*:', function() { } function _assert(layerClips, nodeDisplays, errorBarClips, lineClips) { - var subplotLayer = d3Select('.plot'); + var subplotLayer = d3Select('.overplot').select('.xy'); var scatterLayer = subplotLayer.select('.scatterlayer'); _assertClip(subplotLayer, layerClips[0], 1, 'subplot layer'); diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 4e18bde72ba..18622cbabce 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -3112,7 +3112,7 @@ describe('Test select box and lasso per trace:', function() { var assertSelectedPoints = makeAssertSelectedPoints(); function assertFillOpacity(exp, msg) { - var txtPts = d3Select(gd).select('g.plot').selectAll('text'); + var txtPts = d3Select(gd).select('g.overplot').select('.xy').selectAll('text'); expect(txtPts.size()).toBe(exp.length, '# of text nodes: ' + msg); diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index da4ffd5aba6..366ae0d5884 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -240,7 +240,10 @@ describe('Plotly.toImage', function() { }) .then(function(svg) { var svgDOM = parser.parseFromString(svg, 'image/svg+xml'); - var gSubplot = svgDOM.getElementsByClassName('plot')[0]; + var gSubplot = svgDOM + .getElementsByClassName('overplot')[0] + .getElementsByClassName('xy')[0]; + var clipPath = gSubplot.getAttribute('clip-path'); var len = clipPath.length; From 9bb0c533608491df77965333960ff8660d939c47 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 18 Jun 2024 12:03:42 -0400 Subject: [PATCH 2/7] remove now unused empty plot container --- src/plots/cartesian/index.js | 1 - test/jasmine/tests/splom_test.js | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 82cde3551e7..a8af2d7efd6 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -507,7 +507,6 @@ function makeSubplotLayer(gd, plotinfo) { ensureSingle(plotgroup, 'g', 'yaxislayer-below'); plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below'); - ensureSingle(plotgroup, 'g', 'plot'); plotinfo.overplot = ensureSingle(plotgroup, 'g', 'overplot'); plotinfo.plot = ensureSingle(plotinfo.overplot, 'g', id); diff --git a/test/jasmine/tests/splom_test.js b/test/jasmine/tests/splom_test.js index 1702d63e2f5..b85c14882bd 100644 --- a/test/jasmine/tests/splom_test.js +++ b/test/jasmine/tests/splom_test.js @@ -825,7 +825,7 @@ describe('Test splom interactions:', function() { .then(function() { _assert({ subplotCnt: 25, - innerSubplotNodeCnt: 19, + innerSubplotNodeCnt: 18, hasSplomGrid: false, bgCnt: 0 }); @@ -845,7 +845,7 @@ describe('Test splom interactions:', function() { // grid layer would be above xaxis layer, // if we didn't clear subplot children. expect(gridIndex).toBe(2, ' index'); - expect(xaxisIndex).toBe(16, ' index'); + expect(xaxisIndex).toBe(15, ' index'); return Plotly.restyle(gd, 'dimensions', [dimsLarge]); }) @@ -857,7 +857,7 @@ describe('Test splom interactions:', function() { // new subplots though have reduced number of children. innerSubplotNodeCnt: function(d) { var p = d.match(SUBPLOT_PATTERN); - return (p[1] > 5 || p[2] > 5) ? 4 : 19; + return (p[1] > 5 || p[2] > 5) ? 4 : 18; }, hasSplomGrid: true, bgCnt: 0 From 10acb04ce04d6536a6f01c90327cbf2b32a4f961 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 18 Jun 2024 12:51:39 -0400 Subject: [PATCH 3/7] add axis layers after subplots --- src/plots/cartesian/index.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index a8af2d7efd6..8af75abe99c 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -459,10 +459,18 @@ function makeSubplotData(gd) { // use info about axis layer and overlaying pattern // to clean what need to be cleaned up in exit selection - var d = [id, xa.layer, ya.layer, xa.overlaying || '', ya.overlaying || '']; + var d = [id]; for(j = 0; j < plotinfo.overlays.length; j++) { d.push(plotinfo.overlays[j].id); } + + d = d.concat([ + xa.layer, + ya.layer, + xa.overlaying || '', + ya.overlaying || '' + ]); + subplotData[i] = d; } return subplotData; From 8ed564cabad1da77ff89652255718f17ffff26b8 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 18 Jun 2024 13:45:29 -0400 Subject: [PATCH 4/7] handle zorder between overlayed cartesian subplots - by adding zindex layers e.g. xyz2, x2yz2, xy2z2, x2y2z2, etc. - move zorder logic from plotOne subplot to plot all subplots - add test --- src/plot_api/subroutines.js | 4 +- src/plots/cartesian/constants.js | 4 +- src/plots/cartesian/index.js | 318 +++++++++++------- .../zz-zorder-overlayed-subplots.png | Bin 0 -> 36772 bytes .../mocks/zz-zorder-overlayed-subplots.json | 100 ++++++ 5 files changed, 307 insertions(+), 119 deletions(-) create mode 100644 test/image/baselines/zz-zorder-overlayed-subplots.png create mode 100644 test/image/mocks/zz-zorder-overlayed-subplots.json diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 487decf74f9..c25cb511053 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -24,6 +24,8 @@ var SVG_TEXT_ANCHOR_START = 'start'; var SVG_TEXT_ANCHOR_MIDDLE = 'middle'; var SVG_TEXT_ANCHOR_END = 'end'; +var zindexSeparator = require('../plots/cartesian/constants').zindexSeparator; + exports.layoutStyles = function(gd) { return Lib.syncOrAsync([Plots.doAutoMargin, lsInner], gd); }; @@ -135,7 +137,7 @@ function lsInner(gd) { var yDomain = plotinfo.yaxis.domain; var plotgroup = plotinfo.plotgroup; - if(overlappingDomain(xDomain, yDomain, lowerDomains)) { + if(overlappingDomain(xDomain, yDomain, lowerDomains) && subplot.indexOf(zindexSeparator) === -1) { var pgNode = plotgroup.node(); var plotgroupBg = plotinfo.bg = Lib.ensureSingle(plotgroup, 'rect', 'bg'); pgNode.insertBefore(plotgroupBg.node(), pgNode.childNodes[0]); diff --git a/src/plots/cartesian/constants.js b/src/plots/cartesian/constants.js index f741ecd5599..22d3070b03c 100644 --- a/src/plots/cartesian/constants.js +++ b/src/plots/cartesian/constants.js @@ -66,5 +66,7 @@ module.exports = { layerValue2layerClass: { 'above traces': 'above', 'below traces': 'below' - } + }, + + zindexSeparator: 'z', // used for zindex of cartesian subplots e.g. xy, xyz2, xyz3, etc. }; diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 8af75abe99c..72189538b91 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -20,6 +20,8 @@ function ensureSingleAndAddDatum(parent, nodeType, className) { }); } +var zindexSeparator = constants.zindexSeparator; + exports.name = 'cartesian'; exports.attr = ['xaxis', 'yaxis']; @@ -141,77 +143,82 @@ exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) { for(i = 0; i < calcdata.length; i++) traces.push(i); } - // For each subplot - for(i = 0; i < subplots.length; i++) { - var subplot = subplots[i]; - var subplotInfo = fullLayout._plots[subplot]; - - // Get all calcdata (traces) for this subplot: - var cdSubplot = []; - var pcd; - - // For each trace - for(var j = 0; j < calcdata.length; j++) { - var cd = calcdata[j]; - var trace = cd[0].trace; - - // Skip trace if whitelist provided and it's not whitelisted: - // if (Array.isArray(traces) && traces.indexOf(i) === -1) continue; - if(trace.xaxis + trace.yaxis === subplot) { - // XXX: Should trace carpet dependencies. Only replot all carpet plots if the carpet - // axis has actually changed: - // - // If this trace is specifically requested, add it to the list: - if(traces.indexOf(trace.index) !== -1 || trace.carpet) { - // Okay, so example: traces 0, 1, and 2 have fill = tonext. You animate - // traces 0 and 2. Trace 1 also needs to be updated, otherwise its fill - // is outdated. So this retroactively adds the previous trace if the - // traces are interdependent. - if( - pcd && - pcd[0].trace.xaxis + pcd[0].trace.yaxis === subplot && - ['tonextx', 'tonexty', 'tonext'].indexOf(trace.fill) !== -1 && - cdSubplot.indexOf(pcd) === -1 - ) { - cdSubplot.push(pcd); + var zindices = fullLayout._zindices; + // Plot each zorder group in ascending order + for(var z = 0; z < zindices.length; z++) { + var zorder = zindices[z]; + + // For each subplot + for(i = 0; i < subplots.length; i++) { + var subplot = subplots[i]; + var subplotInfo = fullLayout._plots[subplot]; + + if(z > 0) { + var idWithZ = subplotInfo.id + zindexSeparator + (z + 1); + subplotInfo = Lib.extendFlat({}, subplotInfo, { + id: idWithZ, + plot: fullLayout._cartesianlayer.selectAll('.subplot').select('.' + idWithZ) + }); + } + + // Get all calcdata (traces) for this subplot: + var cdSubplot = []; + var pcd; + + // For each trace + for(var j = 0; j < calcdata.length; j++) { + var cd = calcdata[j]; + var trace = cd[0].trace; + + if(zorder !== (trace.zorder || 0)) continue; + + // Skip trace if whitelist provided and it's not whitelisted: + // if (Array.isArray(traces) && traces.indexOf(i) === -1) continue; + if(trace.xaxis + trace.yaxis === subplot) { + // XXX: Should trace carpet dependencies. Only replot all carpet plots if the carpet + // axis has actually changed: + // + // If this trace is specifically requested, add it to the list: + if(traces.indexOf(trace.index) !== -1 || trace.carpet) { + // Okay, so example: traces 0, 1, and 2 have fill = tonext. You animate + // traces 0 and 2. Trace 1 also needs to be updated, otherwise its fill + // is outdated. So this retroactively adds the previous trace if the + // traces are interdependent. + if( + pcd && + pcd[0].trace.xaxis + pcd[0].trace.yaxis === subplot && + ['tonextx', 'tonexty', 'tonext'].indexOf(trace.fill) !== -1 && + cdSubplot.indexOf(pcd) === -1 + ) { + cdSubplot.push(pcd); + } + + cdSubplot.push(cd); } - cdSubplot.push(cd); + // Track the previous trace on this subplot for the retroactive-add step + // above: + pcd = cd; } - - // Track the previous trace on this subplot for the retroactive-add step - // above: - pcd = cd; } + // Plot the traces for this subplot + plotOne(gd, subplotInfo, cdSubplot, transitionOpts, makeOnCompleteCallback); } - // Plot the traces for this subplot - plotOne(gd, subplotInfo, cdSubplot, transitionOpts, makeOnCompleteCallback); } }; function plotOne(gd, plotinfo, cdSubplot, transitionOpts, makeOnCompleteCallback) { var traceLayerClasses = constants.traceLayerClasses; var fullLayout = gd._fullLayout; + var zindices = fullLayout._zindices; + var modules = fullLayout._modules; var _module, cdModuleAndOthers, cdModule; - // Separate traces by zorder and plot each zorder group separately - // TODO: Performance - var traceZorderGroups = {}; - for(var t = 0; t < cdSubplot.length; t++) { - var trace = cdSubplot[t][0].trace; - var zi = trace.zorder || 0; - if(!traceZorderGroups[zi]) traceZorderGroups[zi] = []; - traceZorderGroups[zi].push(cdSubplot[t]); - } - var layerData = []; var zoomScaleQueryParts = []; // Plot each zorder group in ascending order - var zindices = Object.keys(traceZorderGroups) - .map(Number) - .sort(Lib.sorterAsc); for(var z = 0; z < zindices.length; z++) { var zorder = zindices[z]; // For each "module" (trace type) @@ -374,11 +381,50 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) exports.drawFramework = function(gd) { var fullLayout = gd._fullLayout; - var subplotData = makeSubplotData(gd); + var calcdata = gd.calcdata; + var i; + + // Separate traces by zorder and plot each zorder group separately + var traceZorderGroups = {}; + for(i = 0; i < calcdata.length; i++) { + var cdi = calcdata[i][0]; + var trace = cdi.trace; + + var zi = trace.zorder || 0; + if(!traceZorderGroups[zi]) traceZorderGroups[zi] = []; + traceZorderGroups[zi].push(cdi); + } + + // Group by zorder group in ascending order + var zindices = Object.keys(traceZorderGroups) + .map(Number) + .sort(Lib.sorterAsc); + + if(!zindices.length) zindices = [0]; + + fullLayout._zindices = zindices; + + var initialSubplotData = makeSubplotData(gd); + + var len = initialSubplotData.length; + var subplotData = []; + for(i = 0; i < len; i++) { + subplotData[i] = initialSubplotData[i].slice(); + } + + for(var z = 1; z < zindices.length; z++) { + var newSubplotData = []; + for(i = 0; i < len; i++) { + newSubplotData[i] = initialSubplotData[i].slice(); + newSubplotData[i][0] += zindexSeparator + (z + 1); + } + subplotData = subplotData.concat(newSubplotData); + } var subplotLayers = fullLayout._cartesianlayer.selectAll('.subplot') .data(subplotData, String); + subplotLayers.enter().append('g') .attr('class', function(d) { return 'subplot ' + d[0]; }); @@ -389,15 +435,34 @@ exports.drawFramework = function(gd) { subplotLayers.each(function(d) { var id = d[0]; + var posZ = id.indexOf(zindexSeparator); + var hasZ = posZ !== -1; + var idWithoutZ = hasZ ? + id.slice(0, posZ) : + id; + var plotinfo = fullLayout._plots[id]; + if(!plotinfo) { + plotinfo = Lib.extendFlat({}, fullLayout._plots[idWithoutZ]); + + if(plotinfo) { + plotinfo.id = id; + fullLayout._plots[id] = plotinfo; + fullLayout._subplots.cartesian.push(id); + } + } - plotinfo.plotgroup = d3.select(this); - makeSubplotLayer(gd, plotinfo); + if(plotinfo) { + plotinfo.plotgroup = d3.select(this); + makeSubplotLayer(gd, plotinfo); - // make separate drag layers for each subplot, - // but append them to paper rather than the plot groups, - // so they end up on top of the rest - plotinfo.draglayer = ensureSingle(fullLayout._draggers, 'g', id); + if(!hasZ) { + // make separate drag layers for each subplot, + // but append them to paper rather than the plot groups, + // so they end up on top of the rest + plotinfo.draglayer = ensureSingle(fullLayout._draggers, 'g', id); + } + } }); }; @@ -409,6 +474,8 @@ exports.rangePlot = function(gd, plotinfo, cdSubplot) { function makeSubplotData(gd) { var fullLayout = gd._fullLayout; + var numZ = fullLayout._zindices.length; + var ids = fullLayout._subplots.cartesian; var len = ids.length; var i, j, id, plotinfo, xa, ya; @@ -449,7 +516,7 @@ function makeSubplotData(gd) { // put 'regular' subplot data before 'overlaying' var subplotIds = regulars.concat(overlays); - var subplotData = new Array(len); + var subplotData = []; for(i = 0; i < len; i++) { id = subplotIds[i]; @@ -457,11 +524,18 @@ function makeSubplotData(gd) { xa = plotinfo.xaxis; ya = plotinfo.yaxis; - // use info about axis layer and overlaying pattern - // to clean what need to be cleaned up in exit selection - var d = [id]; - for(j = 0; j < plotinfo.overlays.length; j++) { - d.push(plotinfo.overlays[j].id); + var d = []; + + for(var z = 1; z <= numZ; z++) { + var zStr = ''; + if(z > 1) zStr += zindexSeparator + z; + + // use info about axis layer and overlaying pattern + // to clean what need to be cleaned up in exit selection + d.push(id + zStr); + for(j = 0; j < plotinfo.overlays.length; j++) { + d.push(plotinfo.overlays[j].id + zStr); + } } d = d.concat([ @@ -471,7 +545,7 @@ function makeSubplotData(gd) { ya.overlaying || '' ]); - subplotData[i] = d; + subplotData.push(d); } return subplotData; } @@ -479,6 +553,10 @@ function makeSubplotData(gd) { function makeSubplotLayer(gd, plotinfo) { var plotgroup = plotinfo.plotgroup; var id = plotinfo.id; + + var posZ = id.indexOf(zindexSeparator); + var hasZ = posZ !== -1; + var xLayer = constants.layerValue2layerClass[plotinfo.xaxis.layer]; var yLayer = constants.layerValue2layerClass[plotinfo.yaxis.layer]; var hasOnlyLargeSploms = gd._fullLayout._hasOnlyLargeSploms; @@ -495,42 +573,46 @@ function makeSubplotLayer(gd, plotinfo) { plotinfo.xaxislayer = ensureSingle(plotgroup, 'g', 'xaxislayer-above'); plotinfo.yaxislayer = ensureSingle(plotgroup, 'g', 'yaxislayer-above'); } else { - var backLayer = ensureSingle(plotgroup, 'g', 'layer-subplot'); - plotinfo.shapelayer = ensureSingle(backLayer, 'g', 'shapelayer'); - plotinfo.imagelayer = ensureSingle(backLayer, 'g', 'imagelayer'); - - plotinfo.minorGridlayer = ensureSingle(plotgroup, 'g', 'minor-gridlayer'); - plotinfo.gridlayer = ensureSingle(plotgroup, 'g', 'gridlayer'); - plotinfo.zerolinelayer = ensureSingle(plotgroup, 'g', 'zerolinelayer'); - - var betweenLayer = ensureSingle(plotgroup, 'g', 'layer-between'); - plotinfo.shapelayerBetween = ensureSingle(betweenLayer, 'g', 'shapelayer'); - plotinfo.imagelayerBetween = ensureSingle(betweenLayer, 'g', 'imagelayer'); - - ensureSingle(plotgroup, 'path', 'xlines-below'); - ensureSingle(plotgroup, 'path', 'ylines-below'); - plotinfo.overlinesBelow = ensureSingle(plotgroup, 'g', 'overlines-below'); - - ensureSingle(plotgroup, 'g', 'xaxislayer-below'); - ensureSingle(plotgroup, 'g', 'yaxislayer-below'); - plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below'); + if(!hasZ) { + var backLayer = ensureSingle(plotgroup, 'g', 'layer-subplot'); + plotinfo.shapelayer = ensureSingle(backLayer, 'g', 'shapelayer'); + plotinfo.imagelayer = ensureSingle(backLayer, 'g', 'imagelayer'); + + plotinfo.minorGridlayer = ensureSingle(plotgroup, 'g', 'minor-gridlayer'); + plotinfo.gridlayer = ensureSingle(plotgroup, 'g', 'gridlayer'); + plotinfo.zerolinelayer = ensureSingle(plotgroup, 'g', 'zerolinelayer'); + + var betweenLayer = ensureSingle(plotgroup, 'g', 'layer-between'); + plotinfo.shapelayerBetween = ensureSingle(betweenLayer, 'g', 'shapelayer'); + plotinfo.imagelayerBetween = ensureSingle(betweenLayer, 'g', 'imagelayer'); + + ensureSingle(plotgroup, 'path', 'xlines-below'); + ensureSingle(plotgroup, 'path', 'ylines-below'); + plotinfo.overlinesBelow = ensureSingle(plotgroup, 'g', 'overlines-below'); + + ensureSingle(plotgroup, 'g', 'xaxislayer-below'); + ensureSingle(plotgroup, 'g', 'yaxislayer-below'); + plotinfo.overaxesBelow = ensureSingle(plotgroup, 'g', 'overaxes-below'); + } plotinfo.overplot = ensureSingle(plotgroup, 'g', 'overplot'); plotinfo.plot = ensureSingle(plotinfo.overplot, 'g', id); - plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above'); - plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above'); - plotinfo.overlinesAbove = ensureSingle(plotgroup, 'g', 'overlines-above'); + if(!hasZ) { + plotinfo.xlines = ensureSingle(plotgroup, 'path', 'xlines-above'); + plotinfo.ylines = ensureSingle(plotgroup, 'path', 'ylines-above'); + plotinfo.overlinesAbove = ensureSingle(plotgroup, 'g', 'overlines-above'); - ensureSingle(plotgroup, 'g', 'xaxislayer-above'); - ensureSingle(plotgroup, 'g', 'yaxislayer-above'); - plotinfo.overaxesAbove = ensureSingle(plotgroup, 'g', 'overaxes-above'); + ensureSingle(plotgroup, 'g', 'xaxislayer-above'); + ensureSingle(plotgroup, 'g', 'yaxislayer-above'); + plotinfo.overaxesAbove = ensureSingle(plotgroup, 'g', 'overaxes-above'); - // set refs to correct layers as determined by 'axis.layer' - plotinfo.xlines = plotgroup.select('.xlines-' + xLayer); - plotinfo.ylines = plotgroup.select('.ylines-' + yLayer); - plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer); - plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer); + // set refs to correct layers as determined by 'axis.layer' + plotinfo.xlines = plotgroup.select('.xlines-' + xLayer); + plotinfo.ylines = plotgroup.select('.ylines-' + yLayer); + plotinfo.xaxislayer = plotgroup.select('.xaxislayer-' + xLayer); + plotinfo.yaxislayer = plotgroup.select('.yaxislayer-' + yLayer); + } } } else { var mainplotinfo = plotinfo.mainplotinfo; @@ -566,29 +648,31 @@ function makeSubplotLayer(gd, plotinfo) { plotinfo.yaxislayer = mainplotgroup.select('.overaxes-' + yLayer).select('.' + yId); } - // common attributes for all subplots, overlays or not + if(!hasZ) { + // common attributes for all subplots, overlays or not + + if(!hasOnlyLargeSploms) { + ensureSingleAndAddDatum(plotinfo.minorGridlayer, 'g', plotinfo.xaxis._id); + ensureSingleAndAddDatum(plotinfo.minorGridlayer, 'g', plotinfo.yaxis._id); + plotinfo.minorGridlayer.selectAll('g') + .map(function(d) { return d[0]; }) + .sort(axisIds.idSort); + + ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id); + ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id); + plotinfo.gridlayer.selectAll('g') + .map(function(d) { return d[0]; }) + .sort(axisIds.idSort); + } - if(!hasOnlyLargeSploms) { - ensureSingleAndAddDatum(plotinfo.minorGridlayer, 'g', plotinfo.xaxis._id); - ensureSingleAndAddDatum(plotinfo.minorGridlayer, 'g', plotinfo.yaxis._id); - plotinfo.minorGridlayer.selectAll('g') - .map(function(d) { return d[0]; }) - .sort(axisIds.idSort); + plotinfo.xlines + .style('fill', 'none') + .classed('crisp', true); - ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.xaxis._id); - ensureSingleAndAddDatum(plotinfo.gridlayer, 'g', plotinfo.yaxis._id); - plotinfo.gridlayer.selectAll('g') - .map(function(d) { return d[0]; }) - .sort(axisIds.idSort); + plotinfo.ylines + .style('fill', 'none') + .classed('crisp', true); } - - plotinfo.xlines - .style('fill', 'none') - .classed('crisp', true); - - plotinfo.ylines - .style('fill', 'none') - .classed('crisp', true); } function purgeSubplotLayers(layers, fullLayout) { diff --git a/test/image/baselines/zz-zorder-overlayed-subplots.png b/test/image/baselines/zz-zorder-overlayed-subplots.png new file mode 100644 index 0000000000000000000000000000000000000000..bc6d9c1b15ae01df37c8024d3169c68c0002ae61 GIT binary patch literal 36772 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 Date: Mon, 24 Jun 2024 11:06:40 -0400 Subject: [PATCH 5/7] fix hover and click selection on subplots with zorder --- src/components/fx/hover.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index b3032744510..6739142c4e4 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -15,6 +15,7 @@ var Drawing = require('../drawing'); var Color = require('../color'); var dragElement = require('../dragelement'); var Axes = require('../../plots/cartesian/axes'); +var zindexSeparator = require('../../plots/cartesian/constants').zindexSeparator; var Registry = require('../../registry'); var helpers = require('./helpers'); @@ -261,6 +262,11 @@ exports.loneHover = function loneHover(hoverItems, opts) { function _hover(gd, evt, subplot, noHoverEvent, eventTarget) { if(!subplot) subplot = 'xy'; + if(typeof subplot === 'string') { + // drop zindex from subplot id + subplot = subplot.split(zindexSeparator)[0]; + } + // if the user passed in an array of subplots, // use those instead of finding overlayed plots var subplots = Array.isArray(subplot) ? subplot : [subplot]; From d36364c09ca7fabd336c175e66293e54c1671970 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 24 Jun 2024 12:02:04 -0400 Subject: [PATCH 6/7] ensure z is not added to subplots multiple times --- src/plots/cartesian/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index 72189538b91..f2f2fadf436 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -154,7 +154,9 @@ exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) { var subplotInfo = fullLayout._plots[subplot]; if(z > 0) { - var idWithZ = subplotInfo.id + zindexSeparator + (z + 1); + var idWithZ = subplotInfo.id; + if(idWithZ.indexOf(zindexSeparator) !== -1) continue; + idWithZ += zindexSeparator + (z + 1); subplotInfo = Lib.extendFlat({}, subplotInfo, { id: idWithZ, plot: fullLayout._cartesianlayer.selectAll('.subplot').select('.' + idWithZ) From 3150069c8375ae344e19495ce9e6af49b572f0e4 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 24 Jun 2024 12:22:38 -0400 Subject: [PATCH 7/7] fix relayout removing zindexed subplots --- src/plots/cartesian/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plots/cartesian/index.js b/src/plots/cartesian/index.js index f2f2fadf436..3540c58d901 100644 --- a/src/plots/cartesian/index.js +++ b/src/plots/cartesian/index.js @@ -372,6 +372,10 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) for(i = 0; i < oldSubplotList.cartesian.length; i++) { var oldSubplotId = oldSubplotList.cartesian[i]; + + // skip zindex layes in this process + if(oldSubplotId.indexOf(zindexSeparator) !== -1) continue; + if(!newPlots[oldSubplotId]) { var selector = '.' + oldSubplotId + ',.' + oldSubplotId + '-x,.' + oldSubplotId + '-y'; oldFullLayout._cartesianlayer.selectAll(selector).remove();