Skip to content

Commit 9c1514a

Browse files
committed
tweaks to minor nticks algorithm after feedback from @archmoj
1 parent b81617d commit 9c1514a

File tree

2 files changed

+75
-43
lines changed

2 files changed

+75
-43
lines changed

src/plots/cartesian/axes.js

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -545,54 +545,82 @@ function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
545545
axes.prepMinorTicks = function(mockAx, ax, opts) {
546546
if(!ax.minor.dtick) {
547547
delete mockAx.dtick;
548-
var tick2 = axes.tickIncrement(ax._tmin, ax.dtick, true);
549-
// mock range a tiny bit smaller than one major tick interval
550-
mockAx.range = Lib.simpleMap([ax._tmin, tick2 * 0.99 + ax._tmin * 0.01], ax.l2r);
548+
var hasMajor = ax.dtick && isNumeric(ax._tmin);
549+
var mockMinorRange;
550+
if(hasMajor) {
551+
var tick2 = axes.tickIncrement(ax._tmin, ax.dtick, true);
552+
// mock range a tiny bit smaller than one major tick interval
553+
mockMinorRange = [ax._tmin, tick2 * 0.99 + ax._tmin * 0.01];
554+
} else {
555+
var rl = Lib.simpleMap(ax.range, ax.r2l);
556+
// If we don't have a major dtick, the concept of minor ticks is a little
557+
// ambiguous - just take a stab and say minor.nticks should span 1/5 the axis
558+
mockMinorRange = [rl[0], 0.8 * rl[0] + 0.2 * rl[1]];
559+
}
560+
mockAx.range = Lib.simpleMap(mockMinorRange, ax.l2r);
551561
mockAx._isMinor = true;
562+
552563
axes.prepTicks(mockAx, opts);
553-
if(isNumeric(ax.dtick) && isNumeric(mockAx.dtick)) {
554-
if(!isMultiple(ax.dtick, mockAx.dtick)) {
555-
// give up on minor ticks, with one exception:
556-
// dtick === 2 weeks, minor = 3 days -> set minor 1 week
557-
// other than that, this can only happen if minor.nticks is
558-
// smaller than two jumps in the auto-tick scale and the first
559-
// jump is not an even multiple (5 -> 2 or for dates 3 ->2, 15 -> 10 etc)
560-
// or if you provided an explicit dtick, in which case it's fine to
561-
// give up, you can provide an explicit minor.dtick.
562-
if((ax.dtick === 2 * ONEWEEK) && (mockAx.dtick === 3 * ONEDAY)) {
564+
565+
if(hasMajor) {
566+
var numericMajor = isNumeric(ax.dtick);
567+
var numericMinor = isNumeric(mockAx.dtick);
568+
var majorNum = numericMajor ? ax.dtick : +ax.dtick.substring(1);
569+
var minorNum = numericMinor ? mockAx.dtick : +mockAx.dtick.substring(1);
570+
if(numericMajor && numericMinor) {
571+
if(!isMultiple(majorNum, minorNum)) {
572+
// give up on minor ticks - outside the below exceptions,
573+
// this can only happen if minor.nticks is smaller than two jumps
574+
// in the auto-tick scale and the first jump is not an even multiple
575+
// (5 -> 2 or for dates 3 ->2, 15 -> 10 etc) or if you provided
576+
// an explicit dtick, in which case it's fine to give up,
577+
// you can provide an explicit minor.dtick.
578+
if((majorNum === 2 * ONEWEEK) && (minorNum === 3 * ONEDAY)) {
579+
mockAx.dtick = ONEWEEK;
580+
} else if(majorNum === ONEWEEK && !(ax._input.minor || {}).nticks) {
581+
// minor.nticks defaults to 5, but in this one case we want 7,
582+
// so the minor ticks show on all days of the week
583+
mockAx.dtick = ONEDAY;
584+
} else if(isClose(majorNum / minorNum, 2.5)) {
585+
// 5*10^n -> 2*10^n and you've set nticks < 5
586+
// quarters are pretty common, we don't do this by default as it
587+
// would add an extra digit to display, but minor has no labels
588+
mockAx.dtick = majorNum / 2;
589+
} else {
590+
mockAx.dtick = majorNum;
591+
}
592+
} else if(majorNum === 2 * ONEWEEK && minorNum === 2 * ONEDAY) {
593+
// this is a weird one: we don't want to automatically choose
594+
// 2-day minor ticks for 2-week major, even though it IS an even multiple,
595+
// because people would expect to see the weeks clearly
563596
mockAx.dtick = ONEWEEK;
564-
} else {
565-
mockAx.dtick = ax.dtick;
566597
}
567-
} else if(ax.dtick === 2 * ONEWEEK && mockAx.dtick === 2 * ONEDAY) {
568-
// this is a weird one: we don't want to automatically choose
569-
// 2-day minor ticks for 2-week major, even though it IS an even multiple,
570-
// because people would expect to see the weeks clearly
571-
mockAx.dtick = ONEWEEK;
572-
}
573-
} else if(String(ax.dtick).charAt(0) === 'M') {
574-
if(isNumeric(mockAx.dtick)) {
575-
mockAx.dtick = 'M1';
576-
} else {
577-
var majorMonths = +ax.dtick.substring(1);
578-
var minorMonths = +mockAx.dtick.substring(1);
579-
if(!isMultiple(majorMonths, minorMonths)) {
580-
// unless you provided an explicit ax.dtick (in which case
581-
// it's OK for us to give up, you can provide an explicit
582-
// minor.dtick too), this can only happen with:
583-
// minor.nticks < 3 and dtick === M3, or
584-
// minor.nticks < 5 and dtick === 5 * 10^n years
585-
// so in all cases we just give up.
586-
mockAx.dtick = ax.dtick;
598+
} else if(String(ax.dtick).charAt(0) === 'M') {
599+
if(numericMinor) {
600+
mockAx.dtick = 'M1';
601+
} else {
602+
if(!isMultiple(majorNum, minorNum)) {
603+
// unless you provided an explicit ax.dtick (in which case
604+
// it's OK for us to give up, you can provide an explicit
605+
// minor.dtick too), this can only happen with:
606+
// minor.nticks < 3 and dtick === M3, or
607+
// minor.nticks < 5 and dtick === 5 * 10^n years
608+
// so in all cases we just give up.
609+
mockAx.dtick = ax.dtick;
610+
} else if((majorNum >= 12) && (minorNum === 2)) {
611+
// another special carve-out: for year major ticks, don't show
612+
// 2-month minor ticks, bump to quarters
613+
mockAx.dtick = 'M3';
614+
}
587615
}
588-
}
589-
} else if(String(mockAx.dtick).charAt(0) === 'L') {
590-
if(String(ax.dtick).charAt(0) === 'L') {
591-
if(!isMultiple(+ax.dtick.substring(1), +mockAx.dtick.substring(1))) {
592-
mockAx.dtick = ax.dtick;
616+
} else if(String(mockAx.dtick).charAt(0) === 'L') {
617+
if(String(ax.dtick).charAt(0) === 'L') {
618+
if(!isMultiple(majorNum, minorNum)) {
619+
mockAx.dtick = isClose(majorNum / minorNum, 2.5) ? (ax.dtick / 2) : ax.dtick;
620+
}
621+
} else {
622+
mockAx.dtick = 'D1';
593623
}
594-
} else {
595-
mockAx.dtick = 'D1';
596624
}
597625
}
598626
// put back the original range, to use to find the full set of minor ticks
@@ -608,6 +636,10 @@ function isMultiple(bigger, smaller) {
608636
return Math.abs((bigger / smaller + 0.5) % 1 - 0.5) < 0.001;
609637
}
610638

639+
function isClose(a, b) {
640+
return Math.abs((a / b) - 1) < 0.001;
641+
}
642+
611643
// ensure we have tick0, dtick, and tick rounding calculated
612644
axes.prepTicks = function(ax, opts) {
613645
var rng = Lib.simpleMap(ax.range, ax.r2l, undefined, undefined, opts);

src/plots/cartesian/layout_attributes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function makeNticks(minor) {
3333
return {
3434
valType: 'integer',
3535
min: 0,
36-
dflt: minor ? 7 : 0,
36+
dflt: minor ? 5 : 0,
3737
editType: 'ticks',
3838
description: [
3939
'Specifies the maximum number of ticks for the particular axis.',

0 commit comments

Comments
 (0)