@@ -542,38 +542,72 @@ function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
542542// ----------------------------------------------------
543543
544544// 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 ;
565564 } else {
566- dist /= nt ;
565+ mockAx . dtick = ax . dtick ;
567566 }
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' ;
568596 }
569- } else {
570- dist /= nt || 7 ;
571597 }
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 ;
574604 }
575605} ;
576606
607+ function isMultiple ( bigger , smaller ) {
608+ return Math . abs ( ( bigger / smaller + 0.5 ) % 1 - 0.5 ) < 0.001 ;
609+ }
610+
577611// ensure we have tick0, dtick, and tick rounding calculated
578612axes . prepTicks = function ( ax , opts ) {
579613 var rng = Lib . simpleMap ( ax . range , ax . r2l , undefined , undefined , opts ) ;
@@ -855,20 +889,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
855889 var mockAx = major ? ax : Lib . extendFlat ( { } , ax , ax . minor ) ;
856890
857891 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 ) ;
872893 } else {
873894 axes . prepTicks ( mockAx , opts ) ;
874895 }
@@ -1336,7 +1357,12 @@ axes.autoTicks = function(ax, roughDTick, isMinor) {
13361357 } else if ( ax . type === 'log' ) {
13371358 ax . tick0 = 0 ;
13381359 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+ }
13401366 if ( roughDTick > 0.7 ) {
13411367 // only show powers of 10
13421368 ax . dtick = mayCeil ( roughDTick ) ;
0 commit comments