From f0134268ee39d129b66f78f778f97f194dffd322 Mon Sep 17 00:00:00 2001 From: hannahker Date: Thu, 15 Sep 2022 13:43:02 -0400 Subject: [PATCH 1/3] Add mock and exploratory code --- src/plot_api/subroutines.js | 12 ++++ src/plots/cartesian/axes.js | 21 +++++-- test/image/mocks/multiple_axes_multiple.json | 12 ++-- test/image/mocks/z-multiple-yaxes-layout.json | 56 +++++++++++++++++++ 4 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 test/image/mocks/z-multiple-yaxes-layout.json diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 03e775879a7..992e86d63fa 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -713,3 +713,15 @@ exports.drawMarginPushers = function(gd) { Registry.getComponentMethod('updatemenus', 'draw')(gd); Registry.getComponentMethod('colorbar', 'draw')(gd); }; + +// function getAxDepth(ax) { +// var depth = null; +// if (ax.type == 'multicategory') { +// depth = majorTickSigns[4] * (getLabelLevelBbox('tick2')[ax.side] - mainLinePosition); + +// } else if(ax.title.hasOwnProperty('standoff')) { +// depth = majorTickSigns[4] * (getLabelLevelBbox()[ax.side] - mainLinePosition); +// } + +// } + diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 4a5bed02b15..47107a9229a 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -2248,16 +2248,23 @@ axes.draw = function(gd, arg, opts) { var axList = (!arg || arg === 'redraw') ? axes.listIds(gd) : arg; + var allDepths = [0] // Or dict w keys/values? + return Lib.syncOrAsync(axList.map(function(axId) { return function() { if(!axId) return; var ax = axes.getFromId(gd, axId); - var axDone = axes.drawOne(gd, ax, opts); + var axDone = axes.drawOne(gd, ax, opts, allDepths); + var depth = ax._depth + console.log(depth) + //var depthPrev = allDepths.pop + allDepths.push(depth) + ax._r = ax.range.slice(); ax._rl = Lib.simpleMap(ax._r, ax.r2l); - + //debugger; return axDone; }; })); @@ -2290,11 +2297,12 @@ axes.draw = function(gd, arg, opts) { * - ax._depth (when required only): * - and calls ax.setScale */ -axes.drawOne = function(gd, ax, opts) { +axes.drawOne = function(gd, ax, opts, allDepths) { + //debugger; opts = opts || {}; var i, sp, plotinfo; - + console.log(allDepths) ax.setScale(); var fullLayout = gd._fullLayout; @@ -2541,6 +2549,7 @@ axes.drawOne = function(gd, ax, opts) { var s = ax.side.charAt(0); var sMirror = OPPOSITE_SIDE[ax.side].charAt(0); var pos = axes.getPxPosition(gd, ax); + console.log(pos) var outsideTickLen = outsideTicks ? ax.ticklen : 0; var llbbox; @@ -2646,7 +2655,7 @@ axes.drawOne = function(gd, ax, opts) { ) { seq.push(function() { return drawTitle(gd, ax); }); } - + console.log(seq) return Lib.syncOrAsync(seq); }; @@ -3764,7 +3773,7 @@ function drawDividers(gd, ax, opts) { * - {number} position * @return {number} */ -axes.getPxPosition = function(gd, ax) { +axes.getPxPosition = function(gd, ax, offset) { var gs = gd._fullLayout._size; var axLetter = ax._id.charAt(0); var side = ax.side; diff --git a/test/image/mocks/multiple_axes_multiple.json b/test/image/mocks/multiple_axes_multiple.json index 4afadf4ce6a..3ab5872527a 100644 --- a/test/image/mocks/multiple_axes_multiple.json +++ b/test/image/mocks/multiple_axes_multiple.json @@ -80,7 +80,8 @@ "color": "#1f77b4" }, "text": "yaxis title" - } + }, + "automargin": true }, "yaxis2": { "title": { @@ -95,7 +96,8 @@ "anchor": "free", "side": "left", "position": 0.15, - "overlaying": "y" + "overlaying": "y", + "automargin": true }, "yaxis3": { "title": { @@ -109,7 +111,8 @@ }, "anchor": "x", "side": "right", - "overlaying": "y" + "overlaying": "y", + "automargin": true }, "yaxis4": { "title": { @@ -124,7 +127,8 @@ "anchor": "free", "side": "right", "position": 0.85, - "overlaying": "y" + "overlaying": "y", + "automargin": true } } } diff --git a/test/image/mocks/z-multiple-yaxes-layout.json b/test/image/mocks/z-multiple-yaxes-layout.json new file mode 100644 index 00000000000..29a096ed9b0 --- /dev/null +++ b/test/image/mocks/z-multiple-yaxes-layout.json @@ -0,0 +1,56 @@ +{ + "data": [ + { + "x": [ + 1, + 2, + 3 + ], + "y": [ + 4, + 5, + 6 + ], + "name": "yaxis1 data", + "type": "scatter" + }, + { + "x": [ + 2, + 3, + 4 + ], + "y": [ + 40, + 50, + 60 + ], + "name": "yaxis2 data", + "yaxis": "y2", + "type": "scatter" + } + ], + "layout": { + "title": { + "text": "multiple y-axes example" + }, + "width": 800, + "xaxis": { + "domain": [ + 0.5, + 1 + ] + }, + "yaxis": { + "title": {"text": "yaxis title"}, + "automargin": true, + "anchor": "free" + }, + "yaxis2": { + "title": {"text": "yaxis2 title"}, + "anchor": "free", + "overlaying": "y", + "automargin": true + } + } +} From 7e45c3b69ef6874d37a915b7e31706f8151cfc41 Mon Sep 17 00:00:00 2001 From: hannahker Date: Sun, 18 Sep 2022 17:10:33 -0700 Subject: [PATCH 2/3] MVP with hard coded title offset --- src/plot_api/subroutines.js | 3 +- src/plots/cartesian/axes.js | 27 ++++++------ test/image/mocks/z-multiple-yaxes-layout.json | 42 +++++++++++++++++++ 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 992e86d63fa..c222fb0306e 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -69,6 +69,7 @@ function lsInner(gd) { function getLinePosition(ax, counterAx, side) { var lwHalf = ax._lw / 2; + var xshift = ax.position > 0 ? 0 : ax._xshift; if(ax._id.charAt(0) === 'x') { if(!counterAx) return gs.t + gs.h * (1 - (ax.position || 0)) + (lwHalf % 1); @@ -76,7 +77,7 @@ function lsInner(gd) { return counterAx._offset + counterAx._length + pad + lwHalf; } - if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1); + if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1) + xshift; else if(side === 'right') return counterAx._offset + counterAx._length + pad + lwHalf; return counterAx._offset - pad - lwHalf; } diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 47107a9229a..1a8b783951d 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -2257,14 +2257,11 @@ axes.draw = function(gd, arg, opts) { var ax = axes.getFromId(gd, axId); var axDone = axes.drawOne(gd, ax, opts, allDepths); - var depth = ax._depth - console.log(depth) - //var depthPrev = allDepths.pop + var depth = ax._depth > 0 ? (ax._depth + 50) : ax._depth; // Add offset to account for title size allDepths.push(depth) ax._r = ax.range.slice(); ax._rl = Lib.simpleMap(ax._r, ax.r2l); - //debugger; return axDone; }; })); @@ -2302,9 +2299,11 @@ axes.drawOne = function(gd, ax, opts, allDepths) { opts = opts || {}; var i, sp, plotinfo; - console.log(allDepths) + //('All depths:') + //console.log(allDepths) + ax.setScale(); - + var fullLayout = gd._fullLayout; var axId = ax._id; var axLetter = axId.charAt(0); @@ -2338,6 +2337,8 @@ axes.drawOne = function(gd, ax, opts, allDepths) { // (touching either the tick label or ticks) // depth can be expansive to compute, so we only do so when required ax._depth = null; + // Shift the sum of existing axes depth + ax._xshift = allDepths.reduce((a, b) => a + b) // calcLabelLevelBbox can be expensive, // so make sure to not call it twice during the same Axes.drawOne call @@ -2549,7 +2550,6 @@ axes.drawOne = function(gd, ax, opts, allDepths) { var s = ax.side.charAt(0); var sMirror = OPPOSITE_SIDE[ax.side].charAt(0); var pos = axes.getPxPosition(gd, ax); - console.log(pos) var outsideTickLen = outsideTicks ? ax.ticklen : 0; var llbbox; @@ -2655,7 +2655,6 @@ axes.drawOne = function(gd, ax, opts, allDepths) { ) { seq.push(function() { return drawTitle(gd, ax); }); } - console.log(seq) return Lib.syncOrAsync(seq); }; @@ -3773,12 +3772,14 @@ function drawDividers(gd, ax, opts) { * - {number} position * @return {number} */ -axes.getPxPosition = function(gd, ax, offset) { +axes.getPxPosition = function(gd, ax) { + var gs = gd._fullLayout._size; var axLetter = ax._id.charAt(0); var side = ax.side; var anchorAxis; + var xshift = ax.position > 0 ? 0 : ax._xshift; if(ax.anchor !== 'free') { anchorAxis = ax._anchorAxis; } else if(axLetter === 'x') { @@ -3788,16 +3789,15 @@ axes.getPxPosition = function(gd, ax, offset) { }; } else if(axLetter === 'y') { anchorAxis = { - _offset: gs.l + (ax.position || 0) * gs.w, + _offset: (gs.l + (ax.position || 0) * gs.w) + xshift, _length: 0 }; } - if(side === 'top' || side === 'left') { return anchorAxis._offset; } else if(side === 'bottom' || side === 'right') { return anchorAxis._offset + anchorAxis._length; - } + } }; /** @@ -3841,6 +3841,7 @@ function approxTitleDepth(ax) { * - {boolean} showticklabels */ function drawTitle(gd, ax) { + debugger; var fullLayout = gd._fullLayout; var axId = ax._id; var axLetter = axId.charAt(0); @@ -3889,7 +3890,7 @@ function drawTitle(gd, ax) { x = (ax.side === 'right') ? pos + titleStandoff : pos - titleStandoff; transform = {rotate: '-90', offset: 0}; } - + debugger; var avoid; if(ax.type !== 'multicategory') { diff --git a/test/image/mocks/z-multiple-yaxes-layout.json b/test/image/mocks/z-multiple-yaxes-layout.json index 29a096ed9b0..f0115ebee85 100644 --- a/test/image/mocks/z-multiple-yaxes-layout.json +++ b/test/image/mocks/z-multiple-yaxes-layout.json @@ -28,6 +28,36 @@ "name": "yaxis2 data", "yaxis": "y2", "type": "scatter" + }, + { + "x": [ + 3, + 4, + 5 + ], + "y": [ + 400, + 500, + 600 + ], + "name": "yaxis3 data", + "yaxis": "y3", + "type": "scatter" + }, + { + "x": [ + 4, + 5, + 6 + ], + "y": [ + 4000, + 5000, + 6000 + ], + "name": "yaxis4 data", + "yaxis": "y4", + "type": "scatter" } ], "layout": { @@ -51,6 +81,18 @@ "anchor": "free", "overlaying": "y", "automargin": true + }, + "yaxis3": { + "title": {"text": "yaxis3 title"}, + "anchor": "free", + "overlaying": "y", + "automargin": true + }, + "yaxis4": { + "title": {"text": "yaxis4 title"}, + "anchor": "free", + "overlaying": "y", + "automargin": true } } } From 68b8f8fdc8735a00e1582c3bd71b613a3917879d Mon Sep 17 00:00:00 2001 From: hannahker Date: Mon, 19 Sep 2022 13:53:06 -0700 Subject: [PATCH 3/3] Add more complex mock and clean up code --- src/plots/cartesian/axes.js | 27 +++-- .../image/mocks/z-multiple-yaxes-complex.json | 112 ++++++++++++++++++ ...yout.json => z-multiple-yaxes-simple.json} | 18 ++- 3 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 test/image/mocks/z-multiple-yaxes-complex.json rename test/image/mocks/{z-multiple-yaxes-layout.json => z-multiple-yaxes-simple.json} (85%) diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 1a8b783951d..71b4ff2a68b 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -2248,7 +2248,7 @@ axes.draw = function(gd, arg, opts) { var axList = (!arg || arg === 'redraw') ? axes.listIds(gd) : arg; - var allDepths = [0] // Or dict w keys/values? + var allDepths = [] return Lib.syncOrAsync(axList.map(function(axId) { return function() { @@ -2257,8 +2257,11 @@ axes.draw = function(gd, arg, opts) { var ax = axes.getFromId(gd, axId); var axDone = axes.drawOne(gd, ax, opts, allDepths); - var depth = ax._depth > 0 ? (ax._depth + 50) : ax._depth; // Add offset to account for title size - allDepths.push(depth) + // If we've just drawn a y axis, then keep track of its width so that we can push + // out additional y axes if needed + if (ax._id.charAt(0) == 'y') { + allDepths.push(ax._depth + ax._titleDepth); + } ax._r = ax.range.slice(); ax._rl = Lib.simpleMap(ax._r, ax.r2l); @@ -2295,12 +2298,9 @@ axes.draw = function(gd, arg, opts) { * - and calls ax.setScale */ axes.drawOne = function(gd, ax, opts, allDepths) { - //debugger; opts = opts || {}; var i, sp, plotinfo; - //('All depths:') - //console.log(allDepths) ax.setScale(); @@ -2337,8 +2337,15 @@ axes.drawOne = function(gd, ax, opts, allDepths) { // (touching either the tick label or ticks) // depth can be expansive to compute, so we only do so when required ax._depth = null; - // Shift the sum of existing axes depth - ax._xshift = allDepths.reduce((a, b) => a + b) + // If drawing another y-axis, then look at the sum of the depths of existing axes + // to determine how much to shift this one out by + // TODO: Also need to account for the expected depth of the current axis + // (if drawing from the left inwards) + if (axLetter == 'y' & allDepths.length > 0) { + ax._xshift = allDepths.reduce((a, b) => a + b); + } else { + ax._xshift = null; + } // calcLabelLevelBbox can be expensive, // so make sure to not call it twice during the same Axes.drawOne call @@ -3841,7 +3848,6 @@ function approxTitleDepth(ax) { * - {boolean} showticklabels */ function drawTitle(gd, ax) { - debugger; var fullLayout = gd._fullLayout; var axId = ax._id; var axLetter = axId.charAt(0); @@ -3878,7 +3884,7 @@ function drawTitle(gd, ax) { } } } - + ax._titleDepth = titleStandoff var pos = axes.getPxPosition(gd, ax); var transform, x, y; @@ -3890,7 +3896,6 @@ function drawTitle(gd, ax) { x = (ax.side === 'right') ? pos + titleStandoff : pos - titleStandoff; transform = {rotate: '-90', offset: 0}; } - debugger; var avoid; if(ax.type !== 'multicategory') { diff --git a/test/image/mocks/z-multiple-yaxes-complex.json b/test/image/mocks/z-multiple-yaxes-complex.json new file mode 100644 index 00000000000..f3317a478b6 --- /dev/null +++ b/test/image/mocks/z-multiple-yaxes-complex.json @@ -0,0 +1,112 @@ +{ + "data": [ + { + "x": [ + 1, + 2, + 3 + ], + "y": [ + 4, + 5, + 6 + ], + "name": "yaxis1 data", + "type": "scatter" + }, + { + "x": [ + 2, + 3, + 4 + ], + "y": [ + 40, + 50, + 60 + ], + "name": "yaxis2 data", + "yaxis": "y2", + "type": "scatter" + }, + { + "x": [ + 3, + 4, + 5 + ], + "y": [ + 400, + 500, + 600 + ], + "name": "yaxis3 data", + "yaxis": "y3", + "type": "scatter" + }, + { + "x": [ + 4, + 5, + 6 + ], + "y": [ + 4000, + 5000, + 6000 + ], + "name": "yaxis4 data", + "yaxis": "y4", + "type": "scatter" + } + ], + "layout": { + "title": { + "text": "multiple y-axes example - complex formatting" + }, + "width": 800, + "xaxis": { + "domain": [ + 0.5, + 1 + ] + }, + + "yaxis": { + "title": { + "text": "yaxis title" + + }, + "automargin": true, + "anchor": "free" + }, + "yaxis2": { + "title": {"text": "yaxis2 title"}, + "anchor": "free", + "overlaying": "y", + "automargin": true, + "ticklen":25 + + }, + "yaxis3": { + "title": { + "text": "yaxis3 title", + "font": {"size": 28} + }, + "tickfont": {"size": 28}, + "anchor": "free", + "overlaying": "y", + "automargin": true + }, + "yaxis4": { + "anchor": "free", + "title": { + "text": "yaxis4 title", + "font": {"size": 8} + }, + "tickfont": {"size": 8}, + "overlaying": "y", + "automargin": true + } + } +} diff --git a/test/image/mocks/z-multiple-yaxes-layout.json b/test/image/mocks/z-multiple-yaxes-simple.json similarity index 85% rename from test/image/mocks/z-multiple-yaxes-layout.json rename to test/image/mocks/z-multiple-yaxes-simple.json index f0115ebee85..d42d7388971 100644 --- a/test/image/mocks/z-multiple-yaxes-layout.json +++ b/test/image/mocks/z-multiple-yaxes-simple.json @@ -62,7 +62,7 @@ ], "layout": { "title": { - "text": "multiple y-axes example" + "text": "multiple y-axes example - uniform formatting" }, "width": 800, "xaxis": { @@ -71,8 +71,12 @@ 1 ] }, + "yaxis": { - "title": {"text": "yaxis title"}, + "title": { + "text": "yaxis title" + + }, "automargin": true, "anchor": "free" }, @@ -83,14 +87,20 @@ "automargin": true }, "yaxis3": { - "title": {"text": "yaxis3 title"}, + "title": { + "text": "yaxis3 title" + + }, "anchor": "free", "overlaying": "y", "automargin": true }, "yaxis4": { - "title": {"text": "yaxis4 title"}, "anchor": "free", + "title": { + "text": "yaxis4 title" + + }, "overlaying": "y", "automargin": true }