diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 03e775879a7..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; } @@ -713,3 +714,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..71b4ff2a68b 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 = [] + 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); + // 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); - return axDone; }; })); @@ -2290,13 +2297,13 @@ 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) { opts = opts || {}; var i, sp, plotinfo; - + ax.setScale(); - + var fullLayout = gd._fullLayout; var axId = ax._id; var axLetter = axId.charAt(0); @@ -2330,6 +2337,15 @@ axes.drawOne = function(gd, ax, opts) { // (touching either the tick label or ticks) // depth can be expansive to compute, so we only do so when required ax._depth = null; + // 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 @@ -2646,7 +2662,6 @@ axes.drawOne = function(gd, ax, opts) { ) { seq.push(function() { return drawTitle(gd, ax); }); } - return Lib.syncOrAsync(seq); }; @@ -3765,11 +3780,13 @@ function drawDividers(gd, ax, opts) { * @return {number} */ 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') { @@ -3779,16 +3796,15 @@ axes.getPxPosition = function(gd, ax) { }; } 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; - } + } }; /** @@ -3868,7 +3884,7 @@ function drawTitle(gd, ax) { } } } - + ax._titleDepth = titleStandoff var pos = axes.getPxPosition(gd, ax); var transform, x, y; @@ -3880,7 +3896,6 @@ function drawTitle(gd, ax) { x = (ax.side === 'right') ? pos + titleStandoff : pos - titleStandoff; transform = {rotate: '-90', offset: 0}; } - var avoid; if(ax.type !== 'multicategory') { 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-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-simple.json b/test/image/mocks/z-multiple-yaxes-simple.json new file mode 100644 index 00000000000..d42d7388971 --- /dev/null +++ b/test/image/mocks/z-multiple-yaxes-simple.json @@ -0,0 +1,108 @@ +{ + "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 - uniform 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 + }, + "yaxis3": { + "title": { + "text": "yaxis3 title" + + }, + "anchor": "free", + "overlaying": "y", + "automargin": true + }, + "yaxis4": { + "anchor": "free", + "title": { + "text": "yaxis4 title" + + }, + "overlaying": "y", + "automargin": true + } + } +}