diff --git a/src/plots/cartesian/autorange.js b/src/plots/cartesian/autorange.js index 2b8a176d747..0843b24666b 100644 --- a/src/plots/cartesian/autorange.js +++ b/src/plots/cartesian/autorange.js @@ -298,6 +298,8 @@ function doAutoRange(gd, ax) { * (unless one end is overridden by tozero) * tozero: (boolean) make sure to include zero if axis is linear, * and make it a tight bound if possible + * vpadLinearized: (boolean) whether or not vpad (or vpadplus/vpadminus) + * is linearized (for log scale axes) * * @return {object} * - min {array of objects} @@ -320,6 +322,7 @@ function findExtremes(ax, data, opts) { var tozero = opts.tozero && (ax.type === 'linear' || ax.type === '-'); var isLog = ax.type === 'log'; var hasArrayOption = false; + var vpadLinearized = opts.vpadLinearized || false; var i, v, di, dmin, dmax, ppadiplus, ppadiminus, vmin, vmax; function makePadAccessor(item) { @@ -371,16 +374,22 @@ function findExtremes(ax, data, opts) { if(!isNumeric(di)) return; ppadiplus = ppadplus(i); ppadiminus = ppadminus(i); - vmin = di - vpadminus(i); - vmax = di + vpadplus(i); - // special case for log axes: if vpad makes this object span - // more than an order of mag, clip it to one order. This is so - // we don't have non-positive errors or absurdly large lower - // range due to rounding errors - if(isLog && vmin < vmax / 10) vmin = vmax / 10; - - dmin = ax.c2l(vmin); - dmax = ax.c2l(vmax); + + if(vpadLinearized) { + dmin = ax.c2l(di) - vpadminus(i); + dmax = ax.c2l(di) + vpadplus(i); + } else { + vmin = di - vpadminus(i); + vmax = di + vpadplus(i); + // special case for log axes: if vpad makes this object span + // more than an order of mag, clip it to one order. This is so + // we don't have non-positive errors or absurdly large lower + // range due to rounding errors + if(isLog && vmin < vmax / 10) vmin = vmax / 10; + + dmin = ax.c2l(vmin); + dmax = ax.c2l(vmax); + } if(tozero) { dmin = Math.min(0, dmin); diff --git a/src/traces/box/cross_trace_calc.js b/src/traces/box/cross_trace_calc.js index a13f701c47b..a56cc156f8a 100644 --- a/src/traces/box/cross_trace_calc.js +++ b/src/traces/box/cross_trace_calc.js @@ -60,7 +60,7 @@ function setPositionOffset(traceType, gd, boxList, posAxis) { for(i = 0; i < boxList.length; i++) { calcTrace = calcdata[boxList[i]]; for(j = 0; j < calcTrace.length; j++) { - pointList.push(calcTrace[j].pos); + pointList.push(posAxis.c2l(calcTrace[j].pos, true)); shownPts += (calcTrace[j].pts2 || []).length; } } @@ -213,6 +213,7 @@ function setPositionOffset(traceType, gd, boxList, posAxis) { padded: padded, vpadminus: vpadminus, vpadplus: vpadplus, + vpadLinearized: true, // N.B. SVG px-space positive/negative ppadminus: {x: ppadminus, y: ppadplus}[axLetter], ppadplus: {x: ppadplus, y: ppadminus}[axLetter], diff --git a/src/traces/box/hover.js b/src/traces/box/hover.js index 0eac65f7669..8e40188841b 100644 --- a/src/traces/box/hover.js +++ b/src/traces/box/hover.js @@ -60,7 +60,7 @@ function hoverOnBoxes(pointData, xval, yval, hovermode) { var boxDelta = t.bdPos; var boxDeltaPos, boxDeltaNeg; var posAcceptance = t.wHover; - var shiftPos = function(di) { return di.pos + t.bPos - pVal; }; + var shiftPos = function(di) { return pAxis.c2l(di.pos) + t.bPos - pAxis.c2l(pVal); }; if(isViolin && trace.side !== 'both') { if(trace.side === 'positive') { diff --git a/src/traces/box/plot.js b/src/traces/box/plot.js index 3a1eefdfa85..e6e6bec935e 100644 --- a/src/traces/box/plot.js +++ b/src/traces/box/plot.js @@ -86,14 +86,14 @@ function plotBoxAndWhiskers(sel, axes, trace, t) { paths.each(function(d) { if(d.empty) return 'M0,0Z'; - var pos = d.pos; - var posc = posAxis.c2p(pos + bPos, true) + bPosPxOffset; - var pos0 = posAxis.c2p(pos + bPos - bdPos0, true) + bPosPxOffset; - var pos1 = posAxis.c2p(pos + bPos + bdPos1, true) + bPosPxOffset; - var posw0 = posAxis.c2p(pos + bPos - wdPos, true) + bPosPxOffset; - var posw1 = posAxis.c2p(pos + bPos + wdPos, true) + bPosPxOffset; - var posm0 = posAxis.c2p(pos + bPos - bdPos0 * nw, true) + bPosPxOffset; - var posm1 = posAxis.c2p(pos + bPos + bdPos1 * nw, true) + bPosPxOffset; + var lcenter = posAxis.c2l(d.pos + bPos, true); + var posc = posAxis.l2p(lcenter) + bPosPxOffset; + var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset; + var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset; + var posw0 = posAxis.l2p(lcenter - wdPos) + bPosPxOffset; + var posw1 = posAxis.l2p(lcenter + wdPos) + bPosPxOffset; + var posm0 = posAxis.l2p(lcenter - bdPos0 * nw) + bPosPxOffset; + var posm1 = posAxis.l2p(lcenter + bdPos1 * nw) + bPosPxOffset; var q1 = valAxis.c2p(d.q1, true); var q3 = valAxis.c2p(d.q3, true); // make sure median isn't identical to either of the @@ -288,9 +288,10 @@ function plotBoxMean(sel, axes, trace, t) { paths.exit().remove(); paths.each(function(d) { - var posc = posAxis.c2p(d.pos + bPos, true) + bPosPxOffset; - var pos0 = posAxis.c2p(d.pos + bPos - bdPos0, true) + bPosPxOffset; - var pos1 = posAxis.c2p(d.pos + bPos + bdPos1, true) + bPosPxOffset; + var lcenter = posAxis.c2l(d.pos + bPos, true); + var posc = posAxis.l2p(lcenter) + bPosPxOffset; + var pos0 = posAxis.l2p(lcenter - bdPos0) + bPosPxOffset; + var pos1 = posAxis.l2p(lcenter + bdPos1) + bPosPxOffset; var m = valAxis.c2p(d.mean, true); var sl = valAxis.c2p(d.mean - d.sd, true); var sh = valAxis.c2p(d.mean + d.sd, true); diff --git a/src/traces/scatter/line_points.js b/src/traces/scatter/line_points.js index 2943f2673bb..89964c92ccd 100644 --- a/src/traces/scatter/line_points.js +++ b/src/traces/scatter/line_points.js @@ -67,8 +67,8 @@ module.exports = function linePoints(d, opts) { function getPt(index) { var di = d[index]; if(!di) return false; - var x = xa.c2p(di.x); - var y = ya.c2p(di.y); + var x = opts.linearized ? xa.l2p(di.x) : xa.c2p(di.x); + var y = opts.linearized ? ya.l2p(di.y) : ya.c2p(di.y); // if non-positive log values, set them VERY far off-screen // so the line looks essentially straight from the previous point. diff --git a/src/traces/violin/plot.js b/src/traces/violin/plot.js index a109fd6cab9..f6ca9ac738a 100644 --- a/src/traces/violin/plot.js +++ b/src/traces/violin/plot.js @@ -28,7 +28,8 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { connectGaps: true, baseTolerance: 0.75, shape: 'spline', - simplify: true + simplify: true, + linearized: true }); return Drawing.smoothopen(segments[0], 1); } @@ -64,8 +65,8 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { var pathSel = d3.select(this); var density = d.density; var len = density.length; - var posCenter = d.pos + bPos; - var posCenterPx = posAxis.c2p(posCenter); + var posCenter = posAxis.c2l(d.pos + bPos, true); + var posCenterPx = posAxis.l2p(posCenter); var scale; if(trace.width) { @@ -85,7 +86,7 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { for(i = 0; i < len; i++) { pt = pts[i] = {}; pt[t.posLetter] = posCenter + (density[i].v / scale); - pt[t.valLetter] = density[i].t; + pt[t.valLetter] = valAxis.c2l(density[i].t, true); } pathPos = makePath(pts); } @@ -95,7 +96,7 @@ module.exports = function plot(gd, plotinfo, cdViolins, violinLayer) { for(k = 0, i = len - 1; k < len; k++, i--) { pt = pts[k] = {}; pt[t.posLetter] = posCenter - (density[i].v / scale); - pt[t.valLetter] = density[i].t; + pt[t.valLetter] = valAxis.c2l(density[i].t, true); } pathNeg = makePath(pts); } diff --git a/test/image/baselines/box_log_scale.png b/test/image/baselines/box_log_scale.png new file mode 100644 index 00000000000..c6f6995db35 Binary files /dev/null and b/test/image/baselines/box_log_scale.png differ diff --git a/test/image/baselines/violin_log_scale.png b/test/image/baselines/violin_log_scale.png new file mode 100644 index 00000000000..774b9627036 Binary files /dev/null and b/test/image/baselines/violin_log_scale.png differ diff --git a/test/image/mocks/box_log_scale.json b/test/image/mocks/box_log_scale.json new file mode 100644 index 00000000000..e9160f2e346 --- /dev/null +++ b/test/image/mocks/box_log_scale.json @@ -0,0 +1,94 @@ +{ + "data": [ + { + "y": [ + 1.0, + 1.1, + 1.0, + 0.9, + 1.2, + 2.0, + 1.5, + 2.3, + 1.7, + 2.2, + 1.0, + 1.1, + 1.2, + 0.9, + 1.1 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 10, + 10, + 10, + 10, + 10, + 100, + 100, + 100, + 100, + 100 + ], + "type": "box" + }, + { + "y": [ + 2.2, + 2.3, + 2.0, + 2.5, + 2.1, + 0.1, + 0.5, + 0.8, + 0.3, + 0.3, + 1.1, + 1.2, + 0.9, + 1.0, + 1.0, + 2.4, + 2.0, + 1.5, + 1.6, + 1.9 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 10, + 10, + 10, + 10, + 10, + 50, + 50, + 50, + 50, + 50, + 100, + 100, + 100, + 100, + 100 + ], + "type": "box", + "boxmean": true + } + ], + "layout": { + "xaxis": { + "type": "log" + } + } +} diff --git a/test/image/mocks/violin_log_scale.json b/test/image/mocks/violin_log_scale.json new file mode 100644 index 00000000000..08fc932aa8f --- /dev/null +++ b/test/image/mocks/violin_log_scale.json @@ -0,0 +1,94 @@ +{ + "data": [ + { + "y": [ + 1.0, + 1.1, + 1.0, + 0.9, + 1.2, + 2.0, + 1.5, + 2.3, + 1.7, + 2.2, + 1.0, + 1.1, + 1.2, + 0.9, + 1.1 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 10, + 10, + 10, + 10, + 10, + 100, + 100, + 100, + 100, + 100 + ], + "type": "violin", + "box": { + "visible": true + }, + "meanline": { + "visible": true + }, + "side": "negative" + }, + { + "y": [ + 2.2, + 2.3, + 2.0, + 2.5, + 2.1, + 0.1, + 0.5, + 0.8, + 0.3, + 0.3, + 2.4, + 2.0, + 1.5, + 1.6, + 1.9 + ], + "x": [ + 1, + 1, + 1, + 1, + 1, + 10, + 10, + 10, + 10, + 10, + 100, + 100, + 100, + 100, + 100 + ], + "type": "violin", + "meanline": { + "visible": true + }, + "side": "positive" + } + ], + "layout": { + "xaxis": { + "type": "log" + } + } +}