@@ -545,54 +545,82 @@ function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
545
545
axes . prepMinorTicks = function ( mockAx , ax , opts ) {
546
546
if ( ! ax . minor . dtick ) {
547
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 ) ;
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 ) ;
551
561
mockAx . _isMinor = true ;
562
+
552
563
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
563
596
mockAx . dtick = ONEWEEK ;
564
- } else {
565
- mockAx . dtick = ax . dtick ;
566
597
}
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
+ }
587
615
}
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' ;
593
623
}
594
- } else {
595
- mockAx . dtick = 'D1' ;
596
624
}
597
625
}
598
626
// put back the original range, to use to find the full set of minor ticks
@@ -608,6 +636,10 @@ function isMultiple(bigger, smaller) {
608
636
return Math . abs ( ( bigger / smaller + 0.5 ) % 1 - 0.5 ) < 0.001 ;
609
637
}
610
638
639
+ function isClose ( a , b ) {
640
+ return Math . abs ( ( a / b ) - 1 ) < 0.001 ;
641
+ }
642
+
611
643
// ensure we have tick0, dtick, and tick rounding calculated
612
644
axes . prepTicks = function ( ax , opts ) {
613
645
var rng = Lib . simpleMap ( ax . range , ax . r2l , undefined , undefined , opts ) ;
0 commit comments