Skip to content

Commit 09a6d8c

Browse files
authored
Merge pull request #4639 from plotly/axis-breaks-improvements
Fix reverse autorange on cartesian axes with breaks
2 parents 7ca1777 + aa1143e commit 09a6d8c

File tree

6 files changed

+357
-77
lines changed

6 files changed

+357
-77
lines changed

src/plots/cartesian/autorange.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,11 @@ function getAutoRange(gd, ax) {
101101
if(ax.breaks) {
102102
var breaksOut = ax.locateBreaks(v0, v1);
103103
for(var i = 0; i < breaksOut.length; i++) {
104-
lBreaks += (breaksOut[i].max - breaksOut[i].min);
104+
var brk = breaksOut[i];
105+
lBreaks += brk.max - brk.min;
105106
}
106107
}
107-
return lBreaks;
108+
return (axReverse ? -1 : 1) * lBreaks;
108109
};
109110

110111
var mbest = 0;

src/plots/cartesian/axes.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ axes.prepTicks = function(ax) {
527527
if(ax.tickmode === 'array') nt *= 100;
528528

529529

530-
ax._roughDTick = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / nt;
530+
ax._roughDTick = (Math.abs(rng[1] - rng[0]) - Math.abs(ax._lBreaks || 0)) / nt;
531531
axes.autoTicks(ax, ax._roughDTick);
532532

533533
// check for a forced minimum dtick
@@ -1009,7 +1009,7 @@ axes.tickText = function(ax, x, hover, noSuffixPrefix) {
10091009

10101010
if(arrayMode && Array.isArray(ax.ticktext)) {
10111011
var rng = Lib.simpleMap(ax.range, ax.r2l);
1012-
var minDiff = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / 10000;
1012+
var minDiff = (Math.abs(rng[1] - rng[0]) - Math.abs(ax._lBreaks || 0)) / 10000;
10131013

10141014
for(i = 0; i < ax.ticktext.length; i++) {
10151015
if(Math.abs(x - tickVal2l(ax.tickvals[i])) < minDiff) break;

src/plots/cartesian/dragbox.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -987,13 +987,12 @@ function zoomAxRanges(axList, r0Fraction, r1Fraction, updates, linkedAxes) {
987987
if(axi.fixedrange) continue;
988988

989989
if(axi.breaks) {
990-
if(axi._id.charAt(0) === 'y') {
991-
updates[axi._name + '.range[0]'] = axi.l2r(axi.p2l((1 - r0Fraction) * axi._length));
992-
updates[axi._name + '.range[1]'] = axi.l2r(axi.p2l((1 - r1Fraction) * axi._length));
993-
} else {
994-
updates[axi._name + '.range[0]'] = axi.l2r(axi.p2l(r0Fraction * axi._length));
995-
updates[axi._name + '.range[1]'] = axi.l2r(axi.p2l(r1Fraction * axi._length));
996-
}
990+
var isY = axi._id.charAt(0) === 'y';
991+
var r0F = isY ? (1 - r0Fraction) : r0Fraction;
992+
var r1F = isY ? (1 - r1Fraction) : r1Fraction;
993+
994+
updates[axi._name + '.range[0]'] = axi.l2r(axi.p2l(r0F * axi._length));
995+
updates[axi._name + '.range[1]'] = axi.l2r(axi.p2l(r1F * axi._length));
997996
} else {
998997
var axRangeLinear0 = axi._rl[0];
999998
var axRangeLinearSpan = axi._rl[1] - axRangeLinear0;

src/plots/cartesian/set_convert.js

+57-66
Original file line numberDiff line numberDiff line change
@@ -189,67 +189,58 @@ module.exports = function setConvert(ax, fullLayout) {
189189
};
190190

191191
if(ax.breaks) {
192-
if(axLetter === 'y') {
193-
l2p = function(v) {
194-
if(!isNumeric(v)) return BADNUM;
195-
if(!ax._breaks.length) return _l2p(v, ax._m, ax._b);
196-
197-
var b = ax._B[0];
198-
for(var i = 0; i < ax._breaks.length; i++) {
199-
var brk = ax._breaks[i];
200-
if(v <= brk.min) b = ax._B[i + 1];
201-
else if(v > brk.min && v < brk.max) {
202-
// when v falls into break, pick offset 'closest' to it
203-
if(v - brk.min <= brk.max - v) b = ax._B[i + 1];
204-
else b = ax._B[i];
205-
break;
206-
} else if(v > brk.max) break;
207-
}
208-
return _l2p(v, -ax._m2, b);
209-
};
210-
p2l = function(px) {
211-
if(!isNumeric(px)) return BADNUM;
212-
if(!ax._breaks.length) return _p2l(px, ax._m, ax._b);
213-
214-
var b = ax._B[0];
215-
for(var i = 0; i < ax._breaks.length; i++) {
216-
var brk = ax._breaks[i];
217-
if(px >= brk.pmin) b = ax._B[i + 1];
218-
else if(px < brk.pmax) break;
219-
}
220-
return _p2l(px, -ax._m2, b);
221-
};
222-
} else {
223-
l2p = function(v) {
224-
if(!isNumeric(v)) return BADNUM;
225-
if(!ax._breaks.length) return _l2p(v, ax._m, ax._b);
226-
227-
var b = ax._B[0];
228-
for(var i = 0; i < ax._breaks.length; i++) {
229-
var brk = ax._breaks[i];
230-
if(v >= brk.max) b = ax._B[i + 1];
231-
else if(v > brk.min && v < brk.max) {
232-
// when v falls into break, pick offset 'closest' to it
233-
if(v - brk.min <= brk.max - v) b = ax._B[i];
234-
else b = ax._B[i + 1];
235-
break;
236-
} else if(v < brk.min) break;
192+
l2p = function(v) {
193+
if(!isNumeric(v)) return BADNUM;
194+
var len = ax._breaks.length;
195+
if(!len) return _l2p(v, ax._m, ax._b);
196+
197+
var isY = axLetter === 'y';
198+
var pos = isY ? -v : v;
199+
200+
var q = 0;
201+
for(var i = 0; i < len; i++) {
202+
var nextI = i + 1;
203+
var brk = ax._breaks[i];
204+
205+
var min = isY ? -brk.max : brk.min;
206+
var max = isY ? -brk.min : brk.max;
207+
208+
if(pos < min) break;
209+
if(pos > max) q = nextI;
210+
else {
211+
// when falls into break, pick 'closest' offset
212+
q = pos > (min + max) / 2 ? nextI : i;
213+
break;
237214
}
238-
return _l2p(v, ax._m2, b);
239-
};
240-
p2l = function(px) {
241-
if(!isNumeric(px)) return BADNUM;
242-
if(!ax._breaks.length) return _p2l(px, ax._m, ax._b);
243-
244-
var b = ax._B[0];
245-
for(var i = 0; i < ax._breaks.length; i++) {
246-
var brk = ax._breaks[i];
247-
if(px >= brk.pmax) b = ax._B[i + 1];
248-
else if(px < brk.pmin) break;
215+
}
216+
return _l2p(v, (isY ? -1 : 1) * ax._m2, ax._B[q]);
217+
};
218+
219+
p2l = function(px) {
220+
if(!isNumeric(px)) return BADNUM;
221+
var len = ax._breaks.length;
222+
if(!len) return _p2l(px, ax._m, ax._b);
223+
224+
var isY = axLetter === 'y';
225+
var pos = isY ? -px : px;
226+
227+
var q = 0;
228+
for(var i = 0; i < len; i++) {
229+
var nextI = i + 1;
230+
var brk = ax._breaks[i];
231+
232+
var min = isY ? -brk.pmax : brk.pmin;
233+
var max = isY ? -brk.pmin : brk.pmax;
234+
235+
if(pos < min) break;
236+
if(pos > max) q = nextI;
237+
else {
238+
q = i;
239+
break;
249240
}
250-
return _p2l(px, ax._m2, b);
251-
};
252-
}
241+
}
242+
return _p2l(px, (isY ? -1 : 1) * ax._m2, ax._B[q]);
243+
};
253244
}
254245

255246
// conversions among c/l/p are fairly simple - do them together for all axis types
@@ -561,7 +552,7 @@ module.exports = function setConvert(ax, fullLayout) {
561552

562553
// set of "N" disjoint breaks inside the range
563554
ax._breaks = [];
564-
// length of these breaks in value space
555+
// length of these breaks in value space - negative on reversed axes
565556
ax._lBreaks = 0;
566557
// l2p slope (same for all intervals)
567558
ax._m2 = 0;
@@ -575,12 +566,13 @@ module.exports = function setConvert(ax, fullLayout) {
575566
Math.min(rl0, rl1),
576567
Math.max(rl0, rl1)
577568
);
578-
var signAx = rl0 > rl1 ? -1 : 1;
569+
var axReverse = rl0 > rl1;
570+
var signAx = axReverse ? -1 : 1;
579571

580572
if(ax._breaks.length) {
581573
for(i = 0; i < ax._breaks.length; i++) {
582574
brk = ax._breaks[i];
583-
ax._lBreaks += (brk.max - brk.min);
575+
ax._lBreaks += brk.max - brk.min;
584576
}
585577

586578
ax._m2 = ax._length / (rl1 - rl0 - ax._lBreaks * signAx);
@@ -597,17 +589,16 @@ module.exports = function setConvert(ax, fullLayout) {
597589
brk = ax._breaks[i];
598590
ax._B.push(ax._B[ax._B.length - 1] - ax._m2 * (brk.max - brk.min) * signAx);
599591
}
600-
601-
if(signAx === -1) {
592+
if(axReverse) {
602593
ax._B.reverse();
603594
}
604595

605596
// fill pixel (i.e. 'p') min/max here,
606597
// to not have to loop through the _breaks twice during `p2l`
607598
for(i = 0; i < ax._breaks.length; i++) {
608599
brk = ax._breaks[i];
609-
brk.pmin = l2p(brk.min);
610-
brk.pmax = l2p(brk.max);
600+
brk.pmin = l2p(axReverse ? brk.max : brk.min);
601+
brk.pmax = l2p(axReverse ? brk.min : brk.max);
611602
}
612603
}
613604
}
Loading

0 commit comments

Comments
 (0)