@@ -575,6 +575,10 @@ axes.prepTicks = function(ax, opts) {
575
575
}
576
576
}
577
577
578
+ if ( ax . ticklabelmode === 'period' ) {
579
+ adjustPeriodDelta ( ax ) ;
580
+ }
581
+
578
582
// check for missing tick0
579
583
if ( ! ax . tick0 ) {
580
584
ax . tick0 = ( ax . type === 'date' ) ? '2000-01-01' : 0 ;
@@ -592,47 +596,18 @@ function nMonths(dtick) {
592
596
return + ( dtick . substring ( 1 ) ) ;
593
597
}
594
598
595
- // calculate the ticks: text, values, positioning
596
- // if ticks are set to automatic, determine the right values (tick0,dtick)
597
- // in any case, set tickround to # of digits to round tick labels to,
598
- // or codes to this effect for log and date scales
599
- axes . calcTicks = function calcTicks ( ax , opts ) {
600
- axes . prepTicks ( ax , opts ) ;
601
- var rng = Lib . simpleMap ( ax . range , ax . r2l , undefined , undefined , opts ) ;
602
-
603
- // now that we've figured out the auto values for formatting
604
- // in case we're missing some ticktext, we can break out for array ticks
605
- if ( ax . tickmode === 'array' ) return arrayTicks ( ax ) ;
606
-
607
- // add a tiny bit so we get ticks which may have rounded out
608
- var exRng = expandRange ( rng ) ;
609
- var startTick = exRng [ 0 ] ;
610
- var endTick = exRng [ 1 ] ;
611
- // check for reversed axis
612
- var axrev = ( rng [ 1 ] < rng [ 0 ] ) ;
613
- var minRange = Math . min ( rng [ 0 ] , rng [ 1 ] ) ;
614
- var maxRange = Math . max ( rng [ 0 ] , rng [ 1 ] ) ;
615
-
616
- // find the first tick
617
- ax . _tmin = axes . tickFirst ( ax , opts ) ;
618
-
619
- // No visible ticks? Quit.
620
- // I've only seen this on category axes with all categories off the edge.
621
- if ( ( ax . _tmin < startTick ) !== axrev ) return [ ] ;
599
+ function adjustPeriodDelta ( ax ) { // adjusts ax.dtick and sets ax._definedDelta
600
+ var definedDelta ;
622
601
623
- // return the full set of tick vals
624
- if ( ax . type === 'category' || ax . type === 'multicategory' ) {
625
- endTick = ( axrev ) ? Math . max ( - 0.5 , endTick ) :
626
- Math . min ( ax . _categories . length - 0.5 , endTick ) ;
602
+ function mDate ( ) {
603
+ return ! (
604
+ isNumeric ( ax . dtick ) ||
605
+ ax . dtick . charAt ( 0 ) !== 'M'
606
+ ) ;
627
607
}
628
-
629
- var isDLog = ( ax . type === 'log' ) && ! ( isNumeric ( ax . dtick ) || ax . dtick . charAt ( 0 ) === 'L' ) ;
630
- var isMDate = ( ax . type === 'date' ) && ! ( isNumeric ( ax . dtick ) || ax . dtick . charAt ( 0 ) === 'M' ) ;
631
-
608
+ var isMDate = mDate ( ) ;
632
609
var tickformat = axes . getTickFormat ( ax ) ;
633
- var isPeriod = ax . ticklabelmode === 'period' ;
634
- var definedDelta ;
635
- if ( isPeriod && tickformat ) {
610
+ if ( tickformat ) {
636
611
var noDtick = ax . _dtickInit !== ax . dtick ;
637
612
if (
638
613
! ( / % [ f L Q s S M X ] / . test ( tickformat ) )
@@ -708,9 +683,136 @@ axes.calcTicks = function calcTicks(ax, opts) {
708
683
}
709
684
}
710
685
711
- var maxTicks = Math . max ( 1000 , ax . _length || 0 ) ;
712
- var tickVals = [ ] ;
713
- var xPrevious = null ;
686
+ isMDate = mDate ( ) ;
687
+ if ( isMDate && ax . tick0 === ax . _dowTick0 ) {
688
+ // discard Sunday/Monday tweaks
689
+ ax . tick0 = ax . _rawTick0 ;
690
+ }
691
+
692
+ ax . _definedDelta = definedDelta ;
693
+ }
694
+
695
+ function positionPeriodTicks ( tickVals , ax , definedDelta ) {
696
+ for ( var i = 0 ; i < tickVals . length ; i ++ ) {
697
+ var v = tickVals [ i ] . value ;
698
+
699
+ var a = i ;
700
+ var b = i + 1 ;
701
+ if ( i < tickVals . length - 1 ) {
702
+ a = i ;
703
+ b = i + 1 ;
704
+ } else if ( i > 0 ) {
705
+ a = i - 1 ;
706
+ b = i ;
707
+ } else {
708
+ a = i ;
709
+ b = i ;
710
+ }
711
+
712
+ var A = tickVals [ a ] . value ;
713
+ var B = tickVals [ b ] . value ;
714
+ var actualDelta = Math . abs ( B - A ) ;
715
+ var delta = definedDelta || actualDelta ;
716
+ var periodLength = 0 ;
717
+
718
+ if ( delta >= ONEMINYEAR ) {
719
+ if ( actualDelta >= ONEMINYEAR && actualDelta <= ONEMAXYEAR ) {
720
+ periodLength = actualDelta ;
721
+ } else {
722
+ periodLength = ONEAVGYEAR ;
723
+ }
724
+ } else if ( definedDelta === ONEAVGQUARTER && delta >= ONEMINQUARTER ) {
725
+ if ( actualDelta >= ONEMINQUARTER && actualDelta <= ONEMAXQUARTER ) {
726
+ periodLength = actualDelta ;
727
+ } else {
728
+ periodLength = ONEAVGQUARTER ;
729
+ }
730
+ } else if ( delta >= ONEMINMONTH ) {
731
+ if ( actualDelta >= ONEMINMONTH && actualDelta <= ONEMAXMONTH ) {
732
+ periodLength = actualDelta ;
733
+ } else {
734
+ periodLength = ONEAVGMONTH ;
735
+ }
736
+ } else if ( definedDelta === ONEWEEK && delta >= ONEWEEK ) {
737
+ periodLength = ONEWEEK ;
738
+ } else if ( delta >= ONEDAY ) {
739
+ periodLength = ONEDAY ;
740
+ } else if ( definedDelta === HALFDAY && delta >= HALFDAY ) {
741
+ periodLength = HALFDAY ;
742
+ } else if ( definedDelta === ONEHOUR && delta >= ONEHOUR ) {
743
+ periodLength = ONEHOUR ;
744
+ }
745
+
746
+ var inBetween ;
747
+ if ( periodLength >= actualDelta ) {
748
+ // ensure new label positions remain between ticks
749
+ periodLength = actualDelta ;
750
+ inBetween = true ;
751
+ }
752
+
753
+ var endPeriod = v + periodLength ;
754
+ if ( ax . rangebreaks && periodLength > 0 ) {
755
+ var nAll = 84 ; // highly divisible 7 * 12
756
+ var n = 0 ;
757
+ for ( var c = 0 ; c < nAll ; c ++ ) {
758
+ var r = ( c + 0.5 ) / nAll ;
759
+ if ( ax . maskBreaks ( v * ( 1 - r ) + r * endPeriod ) !== BADNUM ) n ++ ;
760
+ }
761
+ periodLength *= n / nAll ;
762
+
763
+ if ( ! periodLength ) {
764
+ tickVals [ i ] . drop = true ;
765
+ }
766
+
767
+ if ( inBetween && actualDelta > ONEWEEK ) periodLength = actualDelta ; // center monthly & longer periods
768
+ }
769
+
770
+ if (
771
+ periodLength > 0 || // not instant
772
+ i === 0 // taking care first tick added
773
+ ) {
774
+ tickVals [ i ] . periodX = v + periodLength / 2 ;
775
+ }
776
+ }
777
+ }
778
+
779
+ // calculate the ticks: text, values, positioning
780
+ // if ticks are set to automatic, determine the right values (tick0,dtick)
781
+ // in any case, set tickround to # of digits to round tick labels to,
782
+ // or codes to this effect for log and date scales
783
+ axes . calcTicks = function calcTicks ( ax , opts ) {
784
+ axes . prepTicks ( ax , opts ) ;
785
+ var rng = Lib . simpleMap ( ax . range , ax . r2l , undefined , undefined , opts ) ;
786
+
787
+ // now that we've figured out the auto values for formatting
788
+ // in case we're missing some ticktext, we can break out for array ticks
789
+ if ( ax . tickmode === 'array' ) return arrayTicks ( ax ) ;
790
+
791
+ // add a tiny bit so we get ticks which may have rounded out
792
+ var exRng = expandRange ( rng ) ;
793
+ var startTick = exRng [ 0 ] ;
794
+ var endTick = exRng [ 1 ] ;
795
+ // check for reversed axis
796
+ var axrev = ( rng [ 1 ] < rng [ 0 ] ) ;
797
+ var minRange = Math . min ( rng [ 0 ] , rng [ 1 ] ) ;
798
+ var maxRange = Math . max ( rng [ 0 ] , rng [ 1 ] ) ;
799
+
800
+ var isDLog = ( ax . type === 'log' ) && ! ( isNumeric ( ax . dtick ) || ax . dtick . charAt ( 0 ) === 'L' ) ;
801
+ var isPeriod = ax . ticklabelmode === 'period' ;
802
+
803
+ // find the first tick
804
+ ax . _tmin = axes . tickFirst ( ax , opts ) ;
805
+
806
+ // No visible ticks? Quit.
807
+ // I've only seen this on category axes with all categories off the edge.
808
+ if ( ( ax . _tmin < startTick ) !== axrev ) return [ ] ;
809
+
810
+ // return the full set of tick vals
811
+ if ( ax . type === 'category' || ax . type === 'multicategory' ) {
812
+ endTick = ( axrev ) ? Math . max ( - 0.5 , endTick ) :
813
+ Math . min ( ax . _categories . length - 0.5 , endTick ) ;
814
+ }
815
+
714
816
var x = ax . _tmin ;
715
817
716
818
if ( ax . rangebreaks && ax . _tick0Init !== ax . tick0 ) {
@@ -726,6 +828,9 @@ axes.calcTicks = function calcTicks(ax, opts) {
726
828
x = axes . tickIncrement ( x , ax . dtick , ! axrev , ax . calendar ) ;
727
829
}
728
830
831
+ var maxTicks = Math . max ( 1000 , ax . _length || 0 ) ;
832
+ var tickVals = [ ] ;
833
+ var xPrevious = null ;
729
834
for ( ;
730
835
( axrev ) ? ( x >= endTick ) : ( x <= endTick ) ;
731
836
x = axes . tickIncrement ( x , ax . dtick , axrev , ax . calendar )
@@ -753,91 +858,9 @@ axes.calcTicks = function calcTicks(ax, opts) {
753
858
} ) ;
754
859
}
755
860
756
- var i ;
757
- if ( isPeriod ) {
758
- for ( i = 0 ; i < tickVals . length ; i ++ ) {
759
- var v = tickVals [ i ] . value ;
760
-
761
- var a = i ;
762
- var b = i + 1 ;
763
- if ( i < tickVals . length - 1 ) {
764
- a = i ;
765
- b = i + 1 ;
766
- } else if ( i > 0 ) {
767
- a = i - 1 ;
768
- b = i ;
769
- } else {
770
- a = i ;
771
- b = i ;
772
- }
773
-
774
- var A = tickVals [ a ] . value ;
775
- var B = tickVals [ b ] . value ;
776
- var actualDelta = Math . abs ( B - A ) ;
777
- var delta = definedDelta || actualDelta ;
778
- var periodLength = 0 ;
779
-
780
- if ( delta >= ONEMINYEAR ) {
781
- if ( actualDelta >= ONEMINYEAR && actualDelta <= ONEMAXYEAR ) {
782
- periodLength = actualDelta ;
783
- } else {
784
- periodLength = ONEAVGYEAR ;
785
- }
786
- } else if ( definedDelta === ONEAVGQUARTER && delta >= ONEMINQUARTER ) {
787
- if ( actualDelta >= ONEMINQUARTER && actualDelta <= ONEMAXQUARTER ) {
788
- periodLength = actualDelta ;
789
- } else {
790
- periodLength = ONEAVGQUARTER ;
791
- }
792
- } else if ( delta >= ONEMINMONTH ) {
793
- if ( actualDelta >= ONEMINMONTH && actualDelta <= ONEMAXMONTH ) {
794
- periodLength = actualDelta ;
795
- } else {
796
- periodLength = ONEAVGMONTH ;
797
- }
798
- } else if ( definedDelta === ONEWEEK && delta >= ONEWEEK ) {
799
- periodLength = ONEWEEK ;
800
- } else if ( delta >= ONEDAY ) {
801
- periodLength = ONEDAY ;
802
- } else if ( definedDelta === HALFDAY && delta >= HALFDAY ) {
803
- periodLength = HALFDAY ;
804
- } else if ( definedDelta === ONEHOUR && delta >= ONEHOUR ) {
805
- periodLength = ONEHOUR ;
806
- }
807
-
808
- var inBetween ;
809
- if ( periodLength >= actualDelta ) {
810
- // ensure new label positions remain between ticks
811
- periodLength = actualDelta ;
812
- inBetween = true ;
813
- }
814
-
815
- var endPeriod = v + periodLength ;
816
- if ( ax . rangebreaks && periodLength > 0 ) {
817
- var nAll = 84 ; // highly divisible 7 * 12
818
- var n = 0 ;
819
- for ( var c = 0 ; c < nAll ; c ++ ) {
820
- var r = ( c + 0.5 ) / nAll ;
821
- if ( ax . maskBreaks ( v * ( 1 - r ) + r * endPeriod ) !== BADNUM ) n ++ ;
822
- }
823
- periodLength *= n / nAll ;
824
-
825
- if ( ! periodLength ) {
826
- tickVals [ i ] . drop = true ;
827
- }
828
-
829
- if ( inBetween && actualDelta > ONEWEEK ) periodLength = actualDelta ; // center monthly & longer periods
830
- }
831
-
832
- if (
833
- periodLength > 0 || // not instant
834
- i === 0 // taking care first tick added
835
- ) {
836
- tickVals [ i ] . periodX = v + periodLength / 2 ;
837
- }
838
- }
839
- }
861
+ if ( isPeriod ) positionPeriodTicks ( tickVals , ax , ax . _definedDelta ) ;
840
862
863
+ var i ;
841
864
if ( ax . rangebreaks ) {
842
865
var flip = ax . _id . charAt ( 0 ) === 'y' ;
843
866
@@ -1022,11 +1045,16 @@ axes.autoTicks = function(ax, roughDTick) {
1022
1045
// this will also move the base tick off 2000-01-01 if dtick is
1023
1046
// 2 or 3 days... but that's a weird enough case that we'll ignore it.
1024
1047
var tickformat = axes . getTickFormat ( ax ) ;
1048
+ var isPeriod = ax . ticklabelmode === 'period' ;
1049
+ if ( isPeriod ) ax . _rawTick0 = ax . tick0 ;
1050
+
1025
1051
if ( / % [ u V W ] / . test ( tickformat ) ) {
1026
1052
ax . tick0 = Lib . dateTick0 ( ax . calendar , 2 ) ; // Monday
1027
1053
} else {
1028
1054
ax . tick0 = Lib . dateTick0 ( ax . calendar , 1 ) ; // Sunday
1029
1055
}
1056
+
1057
+ if ( isPeriod ) ax . _dowTick0 = ax . tick0 ;
1030
1058
} else if ( roughX2 > ONEHOUR ) {
1031
1059
ax . dtick = roundDTick ( roughDTick , ONEHOUR , roundBase24 ) ;
1032
1060
} else if ( roughX2 > ONEMIN ) {
0 commit comments