diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 03e775879a7..7f23a87c7e1 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -69,16 +69,17 @@ function lsInner(gd) { function getLinePosition(ax, counterAx, side) { var lwHalf = ax._lw / 2; + var xshift = ax.position > 0 ? 0 : ax._xshift; + xshift = (xshift == undefined) ? 0 : xshift; if(ax._id.charAt(0) === 'x') { if(!counterAx) return gs.t + gs.h * (1 - (ax.position || 0)) + (lwHalf % 1); else if(side === 'top') return counterAx._offset - pad - lwHalf; return counterAx._offset + counterAx._length + pad + lwHalf; } - - if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1); - else if(side === 'right') return counterAx._offset + counterAx._length + pad + lwHalf; - return counterAx._offset - pad - lwHalf; + if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1) + xshift; + else if(side === 'right') return counterAx._offset + counterAx._length + pad + lwHalf + xshift; + return counterAx._offset - pad - lwHalf - xshift; } // some preparation of axis position info @@ -713,3 +714,14 @@ 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..21e3b1de9a4 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 multAxisDepths = {'left': 0, 'right': 0}; + 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, multAxisDepths); + + // 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') { + multAxisDepths[ax.side] += 75; + } ax._r = ax.range.slice(); ax._rl = Lib.simpleMap(ax._r, ax.r2l); - return axDone; }; })); @@ -2290,7 +2297,7 @@ 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; @@ -2307,7 +2314,6 @@ axes.drawOne = function(gd, ax, opts) { if(!mainPlotinfo) return; var mainAxLayer = mainPlotinfo[axLetter + 'axislayer']; - var mainLinePosition = ax._mainLinePosition; var mainMirrorPosition = ax._mainMirrorPosition; var vals = ax._vals = axes.calcTicks(ax); @@ -2330,6 +2336,32 @@ 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) + console.log("New axis!!") + console.log(ax._id) + console.log(ax.overlaying) + if(axLetter === 'y' & !(ax.position > 0) & ax.overlaying) { + console.log('here') + ax._xshift = allDepths[ax.side]; + } else { + ax._xshift = 0; + } + + var mainLinePosition; + + if(ax._xshift > 0 & ax.anchor !== 'free') { + // Calculate main line position from function + mainLinePosition = getLinePosition(ax, ax._anchorAxis, ax.side); + } else { + mainLinePosition = ax._mainLinePosition; + } + + + console.log(ax._xshift) + console.log(mainLinePosition) // calcLabelLevelBbox can be expensive, // so make sure to not call it twice during the same Axes.drawOne call @@ -2340,7 +2372,6 @@ axes.drawOne = function(gd, ax, opts) { if(!llbboxes[cls]) llbboxes[cls] = calcLabelLevelBbox(ax, cls); return llbboxes[cls]; } - if(!ax.visible) return; var transTickFn = axes.makeTransTickFn(ax); @@ -2640,13 +2671,11 @@ axes.drawOne = function(gd, ax, opts) { Plots.autoMargin(gd, axMirrorAutoMarginID(ax), mirrorPush); Plots.autoMargin(gd, rangeSliderAutoMarginID(ax), rangeSliderPush); }); - if(!opts.skipTitle && !(hasRangeSlider && ax.side === 'bottom') ) { seq.push(function() { return drawTitle(gd, ax); }); } - return Lib.syncOrAsync(seq); }; @@ -3770,8 +3799,20 @@ axes.getPxPosition = function(gd, ax) { var side = ax.side; var anchorAxis; + // Shift in the opposite direction depending on which side the axis is on + // But don't shift if 'position' is specified + var xshift = ax.position > 0 ? 0 : ax._xshift; + xshift = side === 'left' ? xshift : -xshift; + if(ax.anchor !== 'free') { - anchorAxis = ax._anchorAxis; + if(axLetter === 'y') { + anchorAxis = { + _offset: ax._anchorAxis._offset - xshift, + _length: ax._anchorAxis._length + }; + } else { + anchorAxis = ax._anchorAxis; + } } else if(axLetter === 'x') { anchorAxis = { _offset: gs.t + (1 - (ax.position || 0)) * gs.h, @@ -3779,7 +3820,7 @@ 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 }; } @@ -3868,7 +3909,7 @@ function drawTitle(gd, ax) { } } } - + ax._titleDepth = titleStandoff; var pos = axes.getPxPosition(gd, ax); var transform, x, y; @@ -3880,7 +3921,6 @@ function drawTitle(gd, ax) { x = (ax.side === 'right') ? pos + titleStandoff : pos - titleStandoff; transform = {rotate: '-90', offset: 0}; } - var avoid; if(ax.type !== 'multicategory') { @@ -4211,3 +4251,21 @@ function hideCounterAxisInsideTickLabels(ax, opts) { } } } + +// Copied over from subroutines.js since I want to calculate the line position when +// drawing each y-axis if there is an xshift to be applied +// TODO: Possible to reference this from subroutines.js directly or put the function in some general utils file? +function getLinePosition(ax, counterAx, side) { + var lwHalf = ax._lw / 2; + var xshift = ax.position > 0 ? 0 : ax._xshift; + xshift = (xshift == undefined) ? 0 : xshift; + + if(ax._id.charAt(0) === 'x') { + if(!counterAx) return gs.t + gs.h * (1 - (ax.position || 0)) + (lwHalf % 1); + else if(side === 'top') return counterAx._offset - lwHalf; + return counterAx._offset + counterAx._length + lwHalf; + } + if(!counterAx) return gs.l + gs.w * (ax.position || 0) + (lwHalf % 1) + xshift; + else if(side === 'right') return counterAx._offset + counterAx._length + lwHalf + xshift; + return counterAx._offset - lwHalf - xshift; +} diff --git a/test/image/mocks/multiple_axes_multiple.json b/test/image/mocks/multiple_axes_multiple.json index 4afadf4ce6a..3047188cfaa 100644 --- a/test/image/mocks/multiple_axes_multiple.json +++ b/test/image/mocks/multiple_axes_multiple.json @@ -21,9 +21,9 @@ 4 ], "y": [ - 40, - 50, - 60 + 40000, + 50000, + 60000 ], "name": "yaxis2 data", "yaxis": "y2", @@ -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..641ec747756 --- /dev/null +++ b/test/image/mocks/z-multiple-yaxes-complex.json @@ -0,0 +1,105 @@ +{ + "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 + }, + "yaxis2": { + "title": {"text": "yaxis2 title"}, + "overlaying": "y", + "ticklen":25 + + }, + "yaxis3": { + "title": { + "text": "yaxis3 title", + "font": {"size": 28} + }, + "tickfont": {"size": 28}, + "overlaying": "y" + }, + "yaxis4": { + "title": { + "text": "yaxis4 title", + "font": {"size": 8} + }, + "tickfont": {"size": 8}, + "overlaying": "y" + } + } +} 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..64e67f26b1c --- /dev/null +++ b/test/image/mocks/z-multiple-yaxes-simple.json @@ -0,0 +1,128 @@ +{ + "data": [ + { + "x": [ + 1, + 2, + 3 + ], + "y": [ + 4, + 5, + 6 + ], + "name": "yaxis1 data", + "type": "scatter" + }, + { + "x": [ + 2, + 3, + 4 + ], + "y": [ + 33, + 67, + 92 + ], + "name": "yaxis2 data", + "yaxis": "y2", + "type": "scatter" + }, + { + "x": [ + 3, + 4, + 5 + ], + "y": [ + 22, + 23, + 24 + ], + "name": "yaxis3 data", + "yaxis": "y3", + "type": "scatter" + }, + { + "x": [ + 4, + 5, + 6 + ], + "y": [ + 40.5, + 41, + 46 + ], + "name": "yaxis4 data", + "yaxis": "y4", + "type": "scatter" + }, + { + "x": [ + 4, + 5, + 6 + ], + "y": [ + 2, + 10, + 100 + ], + "name": "yaxis5 data", + "yaxis": "y5", + "type": "scatter" + } + ], + "layout": { + "title": { + "text": "multiple y-axes example - uniform formatting" + }, + "width": 800, + "xaxis": { + "domain": [ + 0.25, + 0.75 + ] + }, + "yaxis": { + "title": { + "text": "yaxis title" + + }, + "showline": true + }, + "yaxis2": { + "title": {"text": "yaxis2 title"}, + "overlaying": "y", + "showline": true + }, + "yaxis3": { + "title": { + "text": "yaxis3 title" + + }, + "overlaying": "y", + "showline": true + }, + "yaxis4": { + "title": { + "text": "yaxis4 title" + + }, + "overlaying": "y", + "side": "right", + "showline": true + }, + "yaxis5": { + "title": { + "text": "yaxis5 title" + + }, + "overlaying": "y", + "side": "right", + "showline": true + } + } +}