@@ -31,6 +31,8 @@ var HALFDAY = ONEDAY / 2;
3131var ONEHOUR = constants . ONEHOUR ;
3232var ONEMIN = constants . ONEMIN ;
3333var ONESEC = constants . ONESEC ;
34+ var ONEMILLI = constants . ONEMILLI ;
35+ var ONEMICROSEC = constants . ONEMICROSEC ;
3436var MINUS_SIGN = constants . MINUS_SIGN ;
3537var BADNUM = constants . BADNUM ;
3638
@@ -908,7 +910,9 @@ axes.calcTicks = function calcTicks(ax, opts) {
908910 var calendar = ax . calendar ;
909911 var ticklabelstep = ax . ticklabelstep ;
910912 var isPeriod = ax . ticklabelmode === 'period' ;
911-
913+ var isReversed = ax . range [ 0 ] > ax . range [ 1 ] ;
914+ var ticklabelIndex = ( ! ax . ticklabelindex || Lib . isArrayOrTypedArray ( ax . ticklabelindex ) ) ?
915+ ax . ticklabelindex : [ ax . ticklabelindex ] ;
912916 var rng = Lib . simpleMap ( ax . range , ax . r2l , undefined , undefined , opts ) ;
913917 var axrev = ( rng [ 1 ] < rng [ 0 ] ) ;
914918 var minRange = Math . min ( rng [ 0 ] , rng [ 1 ] ) ;
@@ -921,6 +925,9 @@ axes.calcTicks = function calcTicks(ax, opts) {
921925
922926 var tickVals = [ ] ;
923927 var minorTickVals = [ ] ;
928+ // all ticks for which labels are drawn which is not necessarily the major ticks when
929+ // `ticklabelindex` is set.
930+ var allTicklabelVals = [ ] ;
924931
925932 var hasMinor = ax . minor && ( ax . minor . ticks || ax . minor . showgrid ) ;
926933
@@ -1075,6 +1082,52 @@ axes.calcTicks = function calcTicks(ax, opts) {
10751082 }
10761083 }
10771084
1085+ // check if ticklabelIndex makes sense, otherwise ignore it
1086+ if ( ! minorTickVals || minorTickVals . length < 2 ) {
1087+ ticklabelIndex = false ;
1088+ } else {
1089+ var diff = ( minorTickVals [ 1 ] . value - minorTickVals [ 0 ] . value ) * ( isReversed ? - 1 : 1 ) ;
1090+ if ( ! periodCompatibleWithTickformat ( diff , ax . tickformat ) ) {
1091+ ticklabelIndex = false ;
1092+ }
1093+ }
1094+ // Determine for which ticks to draw labels
1095+ if ( ! ticklabelIndex ) {
1096+ allTicklabelVals = tickVals ;
1097+ } else {
1098+ // Collect and sort all major and minor ticks, to find the minor ticks `ticklabelIndex`
1099+ // steps away from each major tick. For those minor ticks we want to draw the label.
1100+
1101+ var allTickVals = tickVals . concat ( minorTickVals ) ;
1102+ if ( isPeriod && tickVals . length ) {
1103+ // first major tick was just added for period handling
1104+ allTickVals = allTickVals . slice ( 1 ) ;
1105+ }
1106+
1107+ allTickVals =
1108+ allTickVals
1109+ . sort ( function ( a , b ) { return a . value - b . value ; } )
1110+ . filter ( function ( tick , index , self ) {
1111+ return index === 0 || tick . value !== self [ index - 1 ] . value ;
1112+ } ) ;
1113+
1114+ var majorTickIndices =
1115+ allTickVals
1116+ . map ( function ( item , index ) {
1117+ return item . minor === undefined && ! item . skipLabel ? index : null ;
1118+ } )
1119+ . filter ( function ( index ) { return index !== null ; } ) ;
1120+
1121+ majorTickIndices . forEach ( function ( majorIdx ) {
1122+ ticklabelIndex . map ( function ( nextLabelIdx ) {
1123+ var minorIdx = majorIdx + nextLabelIdx ;
1124+ if ( minorIdx >= 0 && minorIdx < allTickVals . length ) {
1125+ Lib . pushUnique ( allTicklabelVals , allTickVals [ minorIdx ] ) ;
1126+ }
1127+ } ) ;
1128+ } ) ;
1129+ }
1130+
10781131 if ( hasMinor ) {
10791132 var canOverlap =
10801133 ( ax . minor . ticks === 'inside' && ax . ticks === 'outside' ) ||
@@ -1108,7 +1161,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
11081161 }
11091162 }
11101163
1111- if ( isPeriod ) positionPeriodTicks ( tickVals , ax , ax . _definedDelta ) ;
1164+ if ( isPeriod ) positionPeriodTicks ( allTicklabelVals , ax , ax . _definedDelta ) ;
11121165
11131166 var i ;
11141167 if ( ax . rangebreaks ) {
@@ -1166,38 +1219,44 @@ axes.calcTicks = function calcTicks(ax, opts) {
11661219
11671220 tickVals = tickVals . concat ( minorTickVals ) ;
11681221
1169- var t , p ;
1222+ function setTickLabel ( ax , tickVal ) {
1223+ var text = axes . tickText (
1224+ ax ,
1225+ tickVal . value ,
1226+ false , // hover
1227+ tickVal . simpleLabel // noSuffixPrefix
1228+ ) ;
1229+ var p = tickVal . periodX ;
1230+ if ( p !== undefined ) {
1231+ text . periodX = p ;
1232+ if ( p > maxRange || p < minRange ) { // hide label if outside the range
1233+ if ( p > maxRange ) text . periodX = maxRange ;
1234+ if ( p < minRange ) text . periodX = minRange ;
1235+
1236+ hideLabel ( text ) ;
1237+ }
1238+ }
1239+ return text ;
1240+ }
1241+
1242+ var t ;
11701243 for ( i = 0 ; i < tickVals . length ; i ++ ) {
11711244 var _minor = tickVals [ i ] . minor ;
11721245 var _value = tickVals [ i ] . value ;
11731246
11741247 if ( _minor ) {
1175- minorTicks . push ( {
1176- x : _value ,
1177- minor : true
1178- } ) ;
1248+ if ( ticklabelIndex && allTicklabelVals . indexOf ( tickVals [ i ] ) !== - 1 ) {
1249+ t = setTickLabel ( ax , tickVals [ i ] ) ;
1250+ } else {
1251+ t = { x : _value } ;
1252+ }
1253+ t . minor = true ;
1254+ minorTicks . push ( t ) ;
11791255 } else {
11801256 lastVisibleHead = ax . _prevDateHead ;
1181-
1182- t = axes . tickText (
1183- ax ,
1184- _value ,
1185- false , // hover
1186- tickVals [ i ] . simpleLabel // noSuffixPrefix
1187- ) ;
1188-
1189- p = tickVals [ i ] . periodX ;
1190- if ( p !== undefined ) {
1191- t . periodX = p ;
1192- if ( p > maxRange || p < minRange ) { // hide label if outside the range
1193- if ( p > maxRange ) t . periodX = maxRange ;
1194- if ( p < minRange ) t . periodX = minRange ;
1195-
1196- hideLabel ( t ) ;
1197- }
1198- }
1199-
1200- if ( tickVals [ i ] . skipLabel ) {
1257+ t = setTickLabel ( ax , tickVals [ i ] ) ;
1258+ if ( tickVals [ i ] . skipLabel ||
1259+ ticklabelIndex && allTicklabelVals . indexOf ( tickVals [ i ] ) === - 1 ) {
12011260 hideLabel ( t ) ;
12021261 }
12031262
@@ -4542,3 +4601,28 @@ function setShiftVal(ax, axShifts) {
45424601 axShifts [ ax . overlaying ] [ ax . side ] :
45434602 ( ax . shift || 0 ) ;
45444603}
4604+
4605+ /**
4606+ * Checks if the given period is at least the period described by the tickformat or larger. If that
4607+ * is the case, they are compatible, because then the tickformat can be used to describe the period.
4608+ * E.g. it doesn't make sense to put a year label on a period spanning only a month.
4609+ * @param {number } period in ms
4610+ * @param {string } tickformat
4611+ * @returns {boolean }
4612+ */
4613+ function periodCompatibleWithTickformat ( period , tickformat ) {
4614+ return (
4615+ / % f / . test ( tickformat ) ? period >= ONEMICROSEC :
4616+ / % L / . test ( tickformat ) ? period >= ONEMILLI :
4617+ / % [ S X ] / . test ( tickformat ) ? period >= ONESEC :
4618+ / % M / . test ( tickformat ) ? period >= ONEMIN :
4619+ / % [ H I ] / . test ( tickformat ) ? period >= ONEHOUR :
4620+ / % p / . test ( tickformat ) ? period >= HALFDAY :
4621+ / % [ A a d e j u w x ] / . test ( tickformat ) ? period >= ONEDAY :
4622+ / % [ U V W ] / . test ( tickformat ) ? period >= ONEWEEK :
4623+ / % [ B b m ] / . test ( tickformat ) ? period >= ONEMINMONTH :
4624+ / % [ q ] / . test ( tickformat ) ? period >= ONEMINQUARTER :
4625+ / % [ Y y ] / . test ( tickformat ) ? period >= ONEMINYEAR :
4626+ true
4627+ ) ;
4628+ }
0 commit comments