Skip to content

Commit ef53e31

Browse files
committed
fix logic for hiding zero lines when they conflict with axis lines
1 parent b5f8b23 commit ef53e31

File tree

4 files changed

+155
-10
lines changed

4 files changed

+155
-10
lines changed

src/plot_api/subroutines.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ function lsInner(gd) {
8989

9090
// figure out the main axis line and main mirror line position.
9191
// it's easier to follow the logic if we handle these separately from
92-
// ax._linepositions, which are really only used by mirror=allticks
93-
// for the non-main-subplot ticks.
92+
// ax._linepositions, which are only used by mirror=allticks
93+
// for non-main-subplot ticks, and mirror=all(ticks)? for zero line
94+
// hiding logic
9495
ax._mainLinePosition = getLinePosition(ax, counterAx, ax.side);
9596
ax._mainMirrorPosition = (ax.mirror && counterAx) ?
9697
getLinePosition(ax, counterAx,
@@ -270,7 +271,7 @@ function lsInner(gd) {
270271
// each subplot that gets ticks from "allticks" gets an entry:
271272
// [left or bottom, right or top]
272273
extraSubplot = (!xa._anchorAxis || subplot !== xa._mainSubplot);
273-
if(extraSubplot && xa.ticks && xa.mirror === 'allticks') {
274+
if(extraSubplot && (xa.mirror === 'allticks' || xa.mirror === 'all')) {
274275
xa._linepositions[subplot] = [xLinesYBottom, xLinesYTop];
275276
}
276277

@@ -306,8 +307,8 @@ function lsInner(gd) {
306307
yLinesXLeft = getLinePosition(ya, xa, 'left');
307308
yLinesXRight = getLinePosition(ya, xa, 'right');
308309

309-
extraSubplot = (!ya._anchorAxis || subplot !== xa._mainSubplot);
310-
if(extraSubplot && ya.ticks && ya.mirror === 'allticks') {
310+
extraSubplot = (!ya._anchorAxis || subplot !== ya._mainSubplot);
311+
if(extraSubplot && (ya.mirror === 'allticks' || ya.mirror === 'all')) {
311312
ya._linepositions[subplot] = [yLinesXLeft, yLinesXRight];
312313
}
313314

src/plots/cartesian/axes.js

+51-4
Original file line numberDiff line numberDiff line change
@@ -2146,6 +2146,46 @@ axes.doTicksSingle = function(gd, arg, skipTitle) {
21462146
return trace.fill && trace.fill.charAt(trace.fill.length - 1) === axLetter;
21472147
}
21482148

2149+
function lineNearZero(ax2, position) {
2150+
if(!ax2.showline || !ax2.linewidth) return false;
2151+
var tolerance = Math.max((ax2.linewidth + ax.zerolinewidth) / 2, 1);
2152+
2153+
function closeEnough(pos2) {
2154+
return typeof pos2 === 'number' && Math.abs(pos2 - position) < tolerance;
2155+
}
2156+
2157+
if(closeEnough(ax2._mainLinePosition) || closeEnough(ax2._mainMirrorPosition)) {
2158+
return true;
2159+
}
2160+
var linePositions = ax2._linepositions || {};
2161+
for(var k in linePositions) {
2162+
if(closeEnough(linePositions[k][0]) || closeEnough(linePositions[k][1])) {
2163+
return true;
2164+
}
2165+
}
2166+
}
2167+
2168+
function anyCounterAxLineAtZero(counterAxis, rng) {
2169+
var mainCounterAxis = counterAxis._mainAxis;
2170+
if(!mainCounterAxis) return;
2171+
2172+
var zeroPosition = ax._offset + (
2173+
((Math.abs(rng[0]) < Math.abs(rng[1])) === (axLetter === 'x')) ?
2174+
0 : ax._length
2175+
);
2176+
2177+
var counterLetterAxes = axes.list(gd, counterLetter);
2178+
for(var i = 0; i < counterLetterAxes.length; i++) {
2179+
var counterAxis2 = counterLetterAxes[i];
2180+
if(
2181+
counterAxis2._mainAxis === mainCounterAxis &&
2182+
lineNearZero(counterAxis2, zeroPosition)
2183+
) {
2184+
return true;
2185+
}
2186+
}
2187+
}
2188+
21492189
function drawGrid(plotinfo, counteraxis, subplot) {
21502190
if(fullLayout._hasOnlyLargeSploms) return;
21512191

@@ -2182,12 +2222,19 @@ axes.doTicksSingle = function(gd, arg, skipTitle) {
21822222
break;
21832223
}
21842224
}
2185-
var rng = Lib.simpleMap(ax.range, ax.r2l),
2186-
showZl = (rng[0] * rng[1] <= 0) && ax.zeroline &&
2225+
var rng = Lib.simpleMap(ax.range, ax.r2l);
2226+
var zlData = {x: 0, id: axid};
2227+
2228+
var showZl = (rng[0] * rng[1] <= 0) && ax.zeroline &&
21872229
(ax.type === 'linear' || ax.type === '-') && gridvals.length &&
2188-
(hasBarsOrFill || clipEnds({x: 0}) || !ax.showline);
2230+
(
2231+
hasBarsOrFill ||
2232+
clipEnds(zlData) ||
2233+
!anyCounterAxLineAtZero(counteraxis, rng)
2234+
);
2235+
21892236
var zl = zlcontainer.selectAll('path.' + zcls)
2190-
.data(showZl ? [{x: 0, id: axid}] : []);
2237+
.data(showZl ? [zlData] : []);
21912238
zl.enter().append('path').classed(zcls, 1).classed('zl', 1)
21922239
.classed('crisp', 1)
21932240
.attr('d', gridpath)

src/plots/cartesian/layout_attributes.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ module.exports = {
573573
valType: 'boolean',
574574
dflt: false,
575575
role: 'style',
576-
editType: 'layoutstyle',
576+
editType: 'ticks+layoutstyle',
577577
description: [
578578
'Determines whether or not a line bounding this axis is drawn.'
579579
].join(' ')

test/jasmine/tests/axes_test.js

+97
Original file line numberDiff line numberDiff line change
@@ -2783,6 +2783,103 @@ describe('Test axes', function() {
27832783

27842784
});
27852785
});
2786+
2787+
describe('zeroline visibility logic', function() {
2788+
var gd;
2789+
beforeEach(function() {
2790+
gd = createGraphDiv();
2791+
});
2792+
afterEach(destroyGraphDiv);
2793+
2794+
function assertZeroLines(expectedIDs) {
2795+
var sortedIDs = expectedIDs.slice().sort();
2796+
var zlIDs = [];
2797+
d3.select(gd).selectAll('.zl').each(function() {
2798+
var cls = d3.select(this).attr('class');
2799+
var clsMatch = cls.match(/[xy]\d*(?=zl)/g)[0];
2800+
zlIDs.push(clsMatch);
2801+
});
2802+
zlIDs.sort();
2803+
expect(zlIDs).toEqual(sortedIDs);
2804+
}
2805+
2806+
it('works with a single subplot', function(done) {
2807+
Plotly.newPlot(gd, [{x: [1, 2, 3], y: [1, 2, 3]}], {
2808+
xaxis: {range: [0, 4], showzeroline: true, showline: true},
2809+
yaxis: {range: [0, 4], showzeroline: true, showline: true},
2810+
width: 600,
2811+
height: 600
2812+
})
2813+
.then(function() {
2814+
assertZeroLines([]);
2815+
return Plotly.relayout(gd, {'xaxis.showline': false});
2816+
})
2817+
.then(function() {
2818+
assertZeroLines(['y']);
2819+
return Plotly.relayout(gd, {'xaxis.showline': true, 'yaxis.showline': false});
2820+
})
2821+
.then(function() {
2822+
assertZeroLines(['x']);
2823+
return Plotly.relayout(gd, {'yaxis.showline': true, 'yaxis.range': [4, 0]});
2824+
})
2825+
.then(function() {
2826+
assertZeroLines(['y']);
2827+
return Plotly.relayout(gd, {'xaxis.range': [4, 0], 'xaxis.side': 'top'});
2828+
})
2829+
.then(function() {
2830+
assertZeroLines(['x']);
2831+
return Plotly.relayout(gd, {'yaxis.side': 'right', 'xaxis.anchor': 'free', 'xaxis.position': 1});
2832+
})
2833+
.then(function() {
2834+
assertZeroLines([]);
2835+
return Plotly.relayout(gd, {'xaxis.range': [0, 4], 'yaxis.range': [0, 4]});
2836+
})
2837+
.then(function() {
2838+
assertZeroLines(['x', 'y']);
2839+
return Plotly.relayout(gd, {'xaxis.mirror': 'all', 'yaxis.mirror': true});
2840+
})
2841+
.then(function() {
2842+
assertZeroLines([]);
2843+
return Plotly.relayout(gd, {'xaxis.range': [-0.1, 4], 'yaxis.range': [-0.1, 4]});
2844+
})
2845+
.then(function() {
2846+
assertZeroLines(['x', 'y']);
2847+
})
2848+
.catch(failTest)
2849+
.then(done);
2850+
});
2851+
2852+
it('works with multiple subplots', function(done) {
2853+
Plotly.newPlot(gd, [
2854+
{x: [1, 2, 3], y: [1, 2, 3]},
2855+
{x: [1, 2, 3], y: [1, 2, 3], xaxis: 'x2'},
2856+
{x: [1, 2, 3], y: [1, 2, 3], yaxis: 'y2'}
2857+
], {
2858+
xaxis: {range: [0, 4], showzeroline: true, domain: [0, 0.4]},
2859+
yaxis: {range: [0, 4], showzeroline: true, domain: [0, 0.4]},
2860+
xaxis2: {range: [0, 4], showzeroline: true, domain: [0.6, 1]},
2861+
yaxis2: {range: [0, 4], showzeroline: true, domain: [0.6, 1]},
2862+
width: 600,
2863+
height: 600
2864+
})
2865+
.then(function() {
2866+
assertZeroLines(['x', 'x', 'y', 'y', 'x2', 'y2']);
2867+
return Plotly.relayout(gd, {'xaxis.showline': true, 'xaxis.mirror': 'all'});
2868+
})
2869+
.then(function() {
2870+
assertZeroLines(['x', 'x', 'y', 'x2']);
2871+
return Plotly.relayout(gd, {'yaxis.showline': true, 'yaxis.mirror': 'all'});
2872+
})
2873+
.then(function() {
2874+
// x axis still has a zero line on xy2, and y on x2y
2875+
// all the others have disappeared now
2876+
assertZeroLines(['x', 'y']);
2877+
return Plotly.relayout(gd, {'xaxis.showline': true, 'xaxis.mirror': 'all'});
2878+
})
2879+
.catch(failTest)
2880+
.then(done);
2881+
});
2882+
});
27862883
});
27872884

27882885
function getZoomInButton(gd) {

0 commit comments

Comments
 (0)