@@ -542,38 +542,72 @@ function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
542
542
// ----------------------------------------------------
543
543
544
544
// ensure we have minor tick0 and dtick calculated
545
- axes . prepMinorTicks = function ( ax ) {
546
- var majorDtick = ax . _majorDtick ;
547
- if ( ax . tickmode === 'auto' || ! ax . dtick ) {
548
- var nt = ax . nticks ; // minor.nticks
549
- var dist = majorDtick ;
550
-
551
- if ( ax . type === 'date' && typeof majorDtick === 'string' && majorDtick . charAt ( 0 ) === 'M' ) {
552
- var months = Number ( majorDtick . substring ( 1 ) ) ;
553
- dist = months * ONEAVGMONTH / ( nt || 7 ) ;
554
- } else if ( ax . type === 'log' ) {
555
- if ( ! nt ) nt = 2 ;
556
-
557
- if ( nt > 1 ) {
558
- if ( typeof majorDtick === 'string' && majorDtick . charAt ( 0 ) === 'L' ) {
559
- ax . dtick = 'L' + ( majorDtick . substring ( 1 ) / nt ) ;
560
- return ;
561
- } else if ( dist === 'D1' ) {
562
- dist = nt - 1 ;
563
- } else if ( dist === 'D2' ) {
564
- dist = nt === 2 ? 'D1' : nt ;
545
+ axes . prepMinorTicks = function ( mockAx , ax , opts ) {
546
+ if ( ! ax . minor . dtick ) {
547
+ 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 ) ;
551
+ mockAx . _isMinor = true ;
552
+ 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 ) ) {
563
+ mockAx . dtick = ONEWEEK ;
565
564
} else {
566
- dist /= nt ;
565
+ mockAx . dtick = ax . dtick ;
567
566
}
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 ;
587
+ }
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 ;
593
+ }
594
+ } else {
595
+ mockAx . dtick = 'D1' ;
568
596
}
569
- } else {
570
- dist /= nt || 7 ;
571
597
}
572
-
573
- axes . autoTicks ( ax , dist , 'minor' ) ;
598
+ // put back the original range, to use to find the full set of minor ticks
599
+ mockAx . range = ax . range ;
600
+ }
601
+ if ( ax . minor . _tick0Init === undefined ) {
602
+ // ensure identical tick0
603
+ mockAx . tick0 = ax . tick0 ;
574
604
}
575
605
} ;
576
606
607
+ function isMultiple ( bigger , smaller ) {
608
+ return Math . abs ( ( bigger / smaller + 0.5 ) % 1 - 0.5 ) < 0.001 ;
609
+ }
610
+
577
611
// ensure we have tick0, dtick, and tick rounding calculated
578
612
axes . prepTicks = function ( ax , opts ) {
579
613
var rng = Lib . simpleMap ( ax . range , ax . r2l , undefined , undefined , opts ) ;
@@ -855,20 +889,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
855
889
var mockAx = major ? ax : Lib . extendFlat ( { } , ax , ax . minor ) ;
856
890
857
891
if ( isMinor ) {
858
- if ( ! ax . minor . dtick ) {
859
- mockAx . _majorDtick = ax . dtick ;
860
- mockAx . dtick = mockAx . _dtickInit ;
861
- mockAx . tick0 = mockAx . _tick0Init ;
862
- mockAx . ntick = mockAx . _ntickInit ;
863
- }
864
- }
865
-
866
- if ( isMinor ) {
867
- axes . prepMinorTicks ( mockAx ) ;
868
- if ( mockAx . tick0 !== ax . tick0 && ax . minor . _tick0Init === undefined ) {
869
- // ensure identical tick0
870
- mockAx . tick0 = ax . tick0 ;
871
- }
892
+ axes . prepMinorTicks ( mockAx , ax , opts ) ;
872
893
} else {
873
894
axes . prepTicks ( mockAx , opts ) ;
874
895
}
@@ -1336,7 +1357,12 @@ axes.autoTicks = function(ax, roughDTick, isMinor) {
1336
1357
} else if ( ax . type === 'log' ) {
1337
1358
ax . tick0 = 0 ;
1338
1359
var rng = Lib . simpleMap ( ax . range , ax . r2l ) ;
1339
-
1360
+ if ( ax . _isMinor ) {
1361
+ // Log axes by default get MORE than nTicks based on the metrics below
1362
+ // But for minor ticks we don't want this increase, we already have
1363
+ // the major ticks.
1364
+ roughDTick *= 1.5 ;
1365
+ }
1340
1366
if ( roughDTick > 0.7 ) {
1341
1367
// only show powers of 10
1342
1368
ax . dtick = mayCeil ( roughDTick ) ;
0 commit comments