-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
fix logic for hiding zero lines when they conflict with axis lines #2936
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ef53e31
545ad47
bcd7c06
0f59ca2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2146,6 +2146,51 @@ axes.doTicksSingle = function(gd, arg, skipTitle) { | |||||||||||||||||||||||||||
return trace.fill && trace.fill.charAt(trace.fill.length - 1) === axLetter; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
function lineNearZero(ax2, position) { | ||||||||||||||||||||||||||||
if(!ax2.showline || !ax2.linewidth) return false; | ||||||||||||||||||||||||||||
var tolerance = Math.max((ax2.linewidth + ax.zerolinewidth) / 2, 1); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
function closeEnough(pos2) { | ||||||||||||||||||||||||||||
return typeof pos2 === 'number' && Math.abs(pos2 - position) < tolerance; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
if(closeEnough(ax2._mainLinePosition) || closeEnough(ax2._mainMirrorPosition)) { | ||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
var linePositions = ax2._linepositions || {}; | ||||||||||||||||||||||||||||
for(var k in linePositions) { | ||||||||||||||||||||||||||||
if(closeEnough(linePositions[k][0]) || closeEnough(linePositions[k][1])) { | ||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
function anyCounterAxLineAtZero(counterAxis, rng) { | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very nice function name. |
||||||||||||||||||||||||||||
var mainCounterAxis = counterAxis._mainAxis; | ||||||||||||||||||||||||||||
if(!mainCounterAxis) return; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
var zeroPosition = ax._offset + ( | ||||||||||||||||||||||||||||
((Math.abs(rng[0]) < Math.abs(rng[1])) === (axLetter === 'x')) ? | ||||||||||||||||||||||||||||
0 : ax._length | ||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
var plotinfo = fullLayout._plots[counterAxis._mainSubplot]; | ||||||||||||||||||||||||||||
if(!(plotinfo.mainplotinfo || plotinfo).overlays.length) { | ||||||||||||||||||||||||||||
return lineNearZero(counterAxis, zeroPosition); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
var counterLetterAxes = axes.list(gd, counterLetter); | ||||||||||||||||||||||||||||
for(var i = 0; i < counterLetterAxes.length; i++) { | ||||||||||||||||||||||||||||
var counterAxis2 = counterLetterAxes[i]; | ||||||||||||||||||||||||||||
if( | ||||||||||||||||||||||||||||
counterAxis2._mainAxis === mainCounterAxis && | ||||||||||||||||||||||||||||
lineNearZero(counterAxis2, zeroPosition) | ||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||
return true; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
function drawGrid(plotinfo, counteraxis, subplot) { | ||||||||||||||||||||||||||||
if(fullLayout._hasOnlyLargeSploms) return; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
|
@@ -2182,12 +2227,19 @@ axes.doTicksSingle = function(gd, arg, skipTitle) { | |||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
var rng = Lib.simpleMap(ax.range, ax.r2l), | ||||||||||||||||||||||||||||
showZl = (rng[0] * rng[1] <= 0) && ax.zeroline && | ||||||||||||||||||||||||||||
var rng = Lib.simpleMap(ax.range, ax.r2l); | ||||||||||||||||||||||||||||
var zlData = {x: 0, id: axid}; | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need this do in this PR (opening a new issue would be fine), but we'll need to propagate these changes to large-sploms' regl-based grid line logic: plotly.js/src/traces/splom/base_plot.js Lines 171 to 183 in b5f8b23
where I didn't just reuse the So it would be worth trying to expose and ♻️ this logic here for large-sploms. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, thanks for pointing that out! #2938 |
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
var showZl = (rng[0] * rng[1] <= 0) && ax.zeroline && | ||||||||||||||||||||||||||||
(ax.type === 'linear' || ax.type === '-') && gridvals.length && | ||||||||||||||||||||||||||||
(hasBarsOrFill || clipEnds({x: 0}) || !ax.showline); | ||||||||||||||||||||||||||||
( | ||||||||||||||||||||||||||||
hasBarsOrFill || | ||||||||||||||||||||||||||||
clipEnds(zlData) || | ||||||||||||||||||||||||||||
!anyCounterAxLineAtZero(counteraxis, rng) | ||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
var zl = zlcontainer.selectAll('path.' + zcls) | ||||||||||||||||||||||||||||
.data(showZl ? [{x: 0, id: axid}] : []); | ||||||||||||||||||||||||||||
.data(showZl ? [zlData] : []); | ||||||||||||||||||||||||||||
zl.enter().append('path').classed(zcls, 1).classed('zl', 1) | ||||||||||||||||||||||||||||
.classed('crisp', 1) | ||||||||||||||||||||||||||||
.attr('d', gridpath) | ||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2783,6 +2783,148 @@ describe('Test axes', function() { | |
|
||
}); | ||
}); | ||
|
||
describe('zeroline visibility logic', function() { | ||
var gd; | ||
beforeEach(function() { | ||
gd = createGraphDiv(); | ||
}); | ||
afterEach(destroyGraphDiv); | ||
|
||
function assertZeroLines(expectedIDs) { | ||
var sortedIDs = expectedIDs.slice().sort(); | ||
var zlIDs = []; | ||
d3.select(gd).selectAll('.zl').each(function() { | ||
var cls = d3.select(this).attr('class'); | ||
var clsMatch = cls.match(/[xy]\d*(?=zl)/g)[0]; | ||
zlIDs.push(clsMatch); | ||
}); | ||
zlIDs.sort(); | ||
expect(zlIDs).toEqual(sortedIDs); | ||
} | ||
|
||
it('works with a single subplot', function(done) { | ||
Plotly.newPlot(gd, [{x: [1, 2, 3], y: [1, 2, 3]}], { | ||
xaxis: {range: [0, 4], showzeroline: true, showline: true}, | ||
yaxis: {range: [0, 4], showzeroline: true, showline: true}, | ||
width: 600, | ||
height: 600 | ||
}) | ||
.then(function() { | ||
assertZeroLines([]); | ||
return Plotly.relayout(gd, {'xaxis.showline': false}); | ||
}) | ||
.then(function() { | ||
assertZeroLines(['y']); | ||
return Plotly.relayout(gd, {'xaxis.showline': true, 'yaxis.showline': false}); | ||
}) | ||
.then(function() { | ||
assertZeroLines(['x']); | ||
return Plotly.relayout(gd, {'yaxis.showline': true, 'yaxis.range': [4, 0]}); | ||
}) | ||
.then(function() { | ||
assertZeroLines(['y']); | ||
return Plotly.relayout(gd, {'xaxis.range': [4, 0], 'xaxis.side': 'top'}); | ||
}) | ||
.then(function() { | ||
assertZeroLines(['x']); | ||
return Plotly.relayout(gd, {'yaxis.side': 'right', 'xaxis.anchor': 'free', 'xaxis.position': 1}); | ||
}) | ||
.then(function() { | ||
assertZeroLines([]); | ||
return Plotly.relayout(gd, {'xaxis.range': [0, 4], 'yaxis.range': [0, 4]}); | ||
}) | ||
.then(function() { | ||
assertZeroLines(['x', 'y']); | ||
return Plotly.relayout(gd, {'xaxis.mirror': 'all', 'yaxis.mirror': true}); | ||
}) | ||
.then(function() { | ||
assertZeroLines([]); | ||
return Plotly.relayout(gd, {'xaxis.range': [-0.1, 4], 'yaxis.range': [-0.1, 4]}); | ||
}) | ||
.then(function() { | ||
assertZeroLines(['x', 'y']); | ||
}) | ||
.catch(failTest) | ||
.then(done); | ||
}); | ||
|
||
it('works with multiple coupled subplots', function(done) { | ||
Plotly.newPlot(gd, [ | ||
{x: [1, 2, 3], y: [1, 2, 3]}, | ||
{x: [1, 2, 3], y: [1, 2, 3], xaxis: 'x2'}, | ||
{x: [1, 2, 3], y: [1, 2, 3], yaxis: 'y2'} | ||
], { | ||
xaxis: {range: [0, 4], showzeroline: true, domain: [0, 0.4]}, | ||
yaxis: {range: [0, 4], showzeroline: true, domain: [0, 0.4]}, | ||
xaxis2: {range: [0, 4], showzeroline: true, domain: [0.6, 1]}, | ||
yaxis2: {range: [0, 4], showzeroline: true, domain: [0.6, 1]}, | ||
width: 600, | ||
height: 600 | ||
}) | ||
.then(function() { | ||
assertZeroLines(['x', 'x', 'y', 'y', 'x2', 'y2']); | ||
return Plotly.relayout(gd, {'xaxis.showline': true, 'xaxis.mirror': 'all'}); | ||
}) | ||
.then(function() { | ||
assertZeroLines(['x', 'x', 'y', 'x2']); | ||
return Plotly.relayout(gd, {'yaxis.showline': true, 'yaxis.mirror': 'all'}); | ||
}) | ||
.then(function() { | ||
// x axis still has a zero line on xy2, and y on x2y | ||
// all the others have disappeared now | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for the 📚 |
||
assertZeroLines(['x', 'y']); | ||
return Plotly.relayout(gd, {'xaxis.mirror': 'allticks', 'yaxis.mirror': 'allticks'}); | ||
}) | ||
.then(function() { | ||
// allticks works the same as all | ||
assertZeroLines(['x', 'y']); | ||
}) | ||
.catch(failTest) | ||
.then(done); | ||
}); | ||
|
||
it('works with multiple overlaid subplots', function(done) { | ||
Plotly.newPlot(gd, [ | ||
{x: [1, 2, 3], y: [1, 2, 3]}, | ||
{x: [1, 2, 3], y: [1, 2, 3], xaxis: 'x2', yaxis: 'y2'} | ||
], { | ||
xaxis: {range: [0, 4], showzeroline: true}, | ||
yaxis: {range: [0, 4], showzeroline: true}, | ||
xaxis2: {range: [0, 4], showzeroline: true, side: 'top', overlaying: 'x'}, | ||
yaxis2: {range: [0, 4], showzeroline: true, side: 'right', overlaying: 'y'}, | ||
width: 600, | ||
height: 600 | ||
}) | ||
.then(function() { | ||
assertZeroLines(['x', 'y', 'x2', 'y2']); | ||
return Plotly.relayout(gd, {'xaxis.showline': true, 'yaxis.showline': true}); | ||
}) | ||
.then(function() { | ||
assertZeroLines([]); | ||
return Plotly.relayout(gd, { | ||
'xaxis.range': [4, 0], | ||
'yaxis.range': [4, 0], | ||
'xaxis2.range': [4, 0], | ||
'yaxis2.range': [4, 0] | ||
}); | ||
}) | ||
.then(function() { | ||
assertZeroLines(['x', 'y', 'x2', 'y2']); | ||
return Plotly.relayout(gd, { | ||
'xaxis.showline': false, | ||
'yaxis.showline': false, | ||
'xaxis2.showline': true, | ||
'yaxis2.showline': true | ||
}); | ||
}) | ||
.then(function() { | ||
assertZeroLines([]); | ||
}) | ||
.catch(failTest) | ||
.then(done); | ||
}); | ||
}); | ||
}); | ||
|
||
function getZoomInButton(gd) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice find! Is there a test 🔒 ing this down?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ha, there was a test (the subplot case) locking down the changes but not the parts that remained -> added these in 545ad47