diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 08bfda6f367..363034b2150 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -90,6 +90,18 @@ function expandRange(range) { ]; } +function cleanLinearTickValue(ax, x, dtick) { + if(ax.type !== 'linear' || !isNumeric(dtick)) return x; + + var tolerance = Math.abs(dtick) * 1e-10; + var r2l = ax.r2l || Number; + var tick0 = r2l(ax.tick0); + + if(Math.abs(x - tick0) < tolerance) return tick0; + if(Math.abs(x) < tolerance) return 0; + return x; +} + /* * find the list of possible axes to reference with an xref or yref attribute * and coerce it to that list @@ -1105,6 +1117,8 @@ axes.calcTicks = function calcTicks(ax, opts) { } } + x = cleanLinearTickValue(mockAx, x, dtick); + // prevent infinite loops - no more than one tick per pixel, // and make sure each value is different from the previous if(tickVals.length > maxTicks || x === prevX) break; diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index 3d1e962312f..1721e254a63 100644 --- a/test/jasmine/tests/axes_test.js +++ b/test/jasmine/tests/axes_test.js @@ -3440,6 +3440,21 @@ describe('Test axes', function() { }); } + it('snaps ticks close to tick0 before formatting', function() { + var textOut = mockCalc({ + type: 'linear', + tickmode: 'linear', + tickformat: '~r', + tick0: 0, + dtick: 0.2, + range: [-0.6, 0.6] + }); + + expect(textOut).toEqual([ + '−0.6', '−0.4', '−0.2', '0', '0.2', '0.4', '0.6' + ]); + }); + it('reverts to "power" for SI/B exponentformat beyond the prefix range (linear case)', function() { var textOut = mockCalc({ type: 'linear',