@@ -23,13 +23,18 @@ var axAttrs = require('./layout_attributes');
23
23
var cleanTicks = require ( './clean_ticks' ) ;
24
24
25
25
var constants = require ( '../../constants/numerical' ) ;
26
+ var ONEMAXYEAR = constants . ONEMAXYEAR ;
26
27
var ONEAVGYEAR = constants . ONEAVGYEAR ;
28
+ var ONEMINYEAR = constants . ONEMINYEAR ;
29
+ var ONEMAXQUARTER = constants . ONEMAXQUARTER ;
27
30
var ONEAVGQUARTER = constants . ONEAVGQUARTER ;
31
+ var ONEMINQUARTER = constants . ONEMINQUARTER ;
28
32
var ONEMAXMONTH = constants . ONEMAXMONTH ;
29
33
var ONEAVGMONTH = constants . ONEAVGMONTH ;
30
34
var ONEMINMONTH = constants . ONEMINMONTH ;
31
35
var ONEWEEK = constants . ONEWEEK ;
32
36
var ONEDAY = constants . ONEDAY ;
37
+ var HALFDAY = ONEDAY / 2 ;
33
38
var ONEHOUR = constants . ONEHOUR ;
34
39
var ONEMIN = constants . ONEMIN ;
35
40
var ONESEC = constants . ONESEC ;
@@ -499,7 +504,7 @@ function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
499
504
// will always give a somewhat odd-looking label, until we do something
500
505
// smarter like showing the bin boundaries (or the bounds of the actual
501
506
// data in each bin)
502
- binStart -= ONEDAY / 2 ;
507
+ binStart -= HALFDAY ;
503
508
}
504
509
var nextBinStart = axes . tickIncrement ( binStart , dtick ) ;
505
510
@@ -698,22 +703,28 @@ axes.calcTicks = function calcTicks(ax, opts) {
698
703
var maxRange = Math . max ( rng [ 0 ] , rng [ 1 ] ) ;
699
704
700
705
var definedDelta ;
701
- if ( isPeriod && ax . tickformat ) {
706
+ var tickformat = axes . getTickFormat ( ax ) ;
707
+ if ( isPeriod && tickformat ) {
702
708
if (
703
- ! ( / % [ f L Q s S M H I p X ] / . test ( ax . tickformat ) )
709
+ ! ( / % [ f L Q s S M X ] / . test ( tickformat ) )
704
710
// %f: microseconds as a decimal number [000000, 999999]
705
711
// %L: milliseconds as a decimal number [000, 999]
706
712
// %Q: milliseconds since UNIX epoch
707
713
// %s: seconds since UNIX epoch
708
714
// %S: second as a decimal number [00,61]
709
715
// %M: minute as a decimal number [00,59]
710
- // %H: hour (24-hour clock) as a decimal number [00,23]
711
- // %I: hour (12-hour clock) as a decimal number [01,12]
712
- // %p: either AM or PM
713
716
// %X: the locale’s time, such as %-I:%M:%S %p
714
717
) {
715
718
if (
716
- / % [ A a d e j u w x ] / . test ( ax . tickformat )
719
+ / % [ H I ] / . test ( tickformat )
720
+ // %H: hour (24-hour clock) as a decimal number [00,23]
721
+ // %I: hour (12-hour clock) as a decimal number [01,12]
722
+ ) definedDelta = ONEHOUR ;
723
+ else if (
724
+ / % p / . test ( tickformat ) // %p: either AM or PM
725
+ ) definedDelta = HALFDAY ;
726
+ else if (
727
+ / % [ A a d e j u w x ] / . test ( tickformat )
717
728
// %A: full weekday name
718
729
// %a: abbreviated weekday name
719
730
// %d: zero-padded day of the month as a decimal number [01,31]
@@ -724,75 +735,124 @@ axes.calcTicks = function calcTicks(ax, opts) {
724
735
// %x: the locale’s date, such as %-m/%-d/%Y
725
736
) definedDelta = ONEDAY ;
726
737
else if (
727
- / % [ U V W ] / . test ( ax . tickformat )
738
+ / % [ U V W ] / . test ( tickformat )
728
739
// %U: Sunday-based week of the year as a decimal number [00,53]
729
740
// %V: ISO 8601 week of the year as a decimal number [01, 53]
730
741
// %W: Monday-based week of the year as a decimal number [00,53]
731
742
) definedDelta = ONEWEEK ;
732
743
else if (
733
- / % [ B b m ] / . test ( ax . tickformat )
744
+ / % [ B b m ] / . test ( tickformat )
734
745
// %B: full month name
735
746
// %b: abbreviated month name
736
747
// %m: month as a decimal number [01,12]
737
748
) definedDelta = ONEAVGMONTH ;
738
749
else if (
739
- / % [ q ] / . test ( ax . tickformat )
750
+ / % [ q ] / . test ( tickformat )
740
751
// %q: quarter of the year as a decimal number [1,4]
741
752
) definedDelta = ONEAVGQUARTER ;
742
753
else if (
743
- / % [ Y y ] / . test ( ax . tickformat )
754
+ / % [ Y y ] / . test ( tickformat )
744
755
// %Y: year with century as a decimal number, such as 1999
745
756
// %y: year without century as a decimal number [00,99]
746
757
) definedDelta = ONEAVGYEAR ;
747
758
}
748
759
}
749
760
750
- var removedPreTick0Label = false ;
751
- var ticksOut = new Array ( tickVals . length ) ;
761
+ var ticksOut = [ ] ;
752
762
var i ;
763
+ var prevText ;
753
764
for ( i = 0 ; i < tickVals . length ; i ++ ) {
754
765
var _minor = tickVals [ i ] . minor ;
755
766
var _value = tickVals [ i ] . value ;
756
767
757
- ticksOut [ i ] = axes . tickText (
768
+ var t = axes . tickText (
758
769
ax ,
759
770
_value ,
760
771
false , // hover
761
772
_minor // noSuffixPrefix
762
773
) ;
763
774
764
- if ( isPeriod ) {
765
- var v = tickVals [ i ] . value ;
775
+ if ( isPeriod && prevText === t . text ) continue ;
776
+ prevText = t . text ;
777
+
778
+ ticksOut . push ( t ) ;
779
+ }
780
+
781
+ if ( isPeriod ) {
782
+ var removedPreTick0Label = false ;
783
+
784
+ for ( i = 0 ; i < ticksOut . length ; i ++ ) {
785
+ var v = ticksOut [ i ] . x ;
766
786
767
787
var a = i ;
768
788
var b = i + 1 ;
769
- if ( i < tickVals . length - 1 ) {
789
+ if ( i < ticksOut . length - 1 ) {
770
790
a = i ;
771
791
b = i + 1 ;
772
- } else {
792
+ } else if ( i > 0 ) {
773
793
a = i - 1 ;
774
794
b = i ;
795
+ } else {
796
+ a = i ;
797
+ b = i ;
775
798
}
776
799
777
- var A = tickVals [ a ] . value ;
778
- var B = tickVals [ b ] . value ;
800
+ var A = ticksOut [ a ] . x ;
801
+ var B = ticksOut [ b ] . x ;
802
+ var actualDelta = Math . abs ( B - A ) ;
803
+ var delta = definedDelta || actualDelta ;
804
+ var periodLength = 0 ;
779
805
780
- var delta = definedDelta || Math . abs ( B - A ) ;
781
- if ( delta >= ONEDAY * 365 ) { // Years could have days less than ONEAVGYEAR period
782
- v += ONEAVGYEAR / 2 ;
783
- } else if ( delta >= ONEAVGQUARTER ) {
784
- v += ONEAVGQUARTER / 2 ;
785
- } else if ( delta >= ONEMINMONTH ) { // Months could have days less than ONEAVGMONTH period
786
- var actualDelta = Math . abs ( B - A ) ;
806
+ if ( delta >= ONEMINYEAR ) {
807
+ if ( actualDelta >= ONEMINYEAR && actualDelta <= ONEMAXYEAR ) {
808
+ periodLength = actualDelta ;
809
+ } else {
810
+ periodLength = ONEAVGYEAR ;
811
+ }
812
+ } else if ( definedDelta === ONEAVGQUARTER && delta >= ONEMINQUARTER ) {
813
+ if ( actualDelta >= ONEMINQUARTER && actualDelta <= ONEMAXQUARTER ) {
814
+ periodLength = actualDelta ;
815
+ } else {
816
+ periodLength = ONEAVGQUARTER ;
817
+ }
818
+ } else if ( delta >= ONEMINMONTH ) {
787
819
if ( actualDelta >= ONEMINMONTH && actualDelta <= ONEMAXMONTH ) {
788
- v + = actualDelta / 2 ;
820
+ periodLength = actualDelta ;
789
821
} else {
790
- v + = ONEAVGMONTH / 2 ;
822
+ periodLength = ONEAVGMONTH ;
791
823
}
792
- } else if ( delta >= ONEWEEK ) {
793
- v + = ONEWEEK / 2 ;
824
+ } else if ( definedDelta === ONEWEEK && delta >= ONEWEEK ) {
825
+ periodLength = ONEWEEK ;
794
826
} else if ( delta >= ONEDAY ) {
795
- v += ONEDAY / 2 ;
827
+ periodLength = ONEDAY ;
828
+ } else if ( definedDelta === HALFDAY && delta >= HALFDAY ) {
829
+ periodLength = HALFDAY ;
830
+ } else if ( definedDelta === ONEHOUR && delta >= ONEHOUR ) {
831
+ periodLength = ONEHOUR ;
832
+ }
833
+
834
+ if ( periodLength && ax . rangebreaks ) {
835
+ var nFirstHalf = 0 ;
836
+ var nSecondHalf = 0 ;
837
+ var nAll = 2 * 3 * 7 ; // number of samples
838
+ for ( var c = 0 ; c < nAll ; c ++ ) {
839
+ var r = c / nAll ;
840
+ if ( ax . maskBreaks ( A * ( 1 - r ) + B * r ) !== BADNUM ) {
841
+ if ( r < 0.5 ) {
842
+ nFirstHalf ++ ;
843
+ } else {
844
+ nSecondHalf ++ ;
845
+ }
846
+ }
847
+ }
848
+
849
+ if ( nSecondHalf ) {
850
+ periodLength *= ( nFirstHalf + nSecondHalf ) / nAll ;
851
+ }
852
+ }
853
+
854
+ if ( periodLength <= actualDelta ) { // i.e. to ensure new label positions remain between ticks
855
+ v += periodLength / 2 ;
796
856
}
797
857
798
858
ticksOut [ i ] . periodX = v ;
@@ -802,15 +862,15 @@ axes.calcTicks = function calcTicks(ax, opts) {
802
862
removedPreTick0Label = true ;
803
863
}
804
864
}
805
- }
806
865
807
- if ( removedPreTick0Label ) {
808
- for ( i = 0 ; i < ticksOut . length ; i ++ ) {
809
- if ( ticksOut [ i ] . periodX <= maxRange && ticksOut [ i ] . periodX >= minRange ) {
810
- // redo first visible tick
811
- ax . _prevDateHead = '' ;
812
- ticksOut [ i ] . text = axes . tickText ( ax , tickVals [ i ] . value ) . text ;
813
- break ;
866
+ if ( removedPreTick0Label ) {
867
+ for ( i = 0 ; i < ticksOut . length ; i ++ ) {
868
+ if ( ticksOut [ i ] . periodX <= maxRange && ticksOut [ i ] . periodX >= minRange ) {
869
+ // redo first visible tick
870
+ ax . _prevDateHead = '' ;
871
+ ticksOut [ i ] . text = axes . tickText ( ax , ticksOut [ i ] . x ) . text ;
872
+ break ;
873
+ }
814
874
}
815
875
}
816
876
}
@@ -924,7 +984,8 @@ axes.autoTicks = function(ax, roughDTick) {
924
984
// 2 or 3 days... but that's a weird enough case that we'll ignore it.
925
985
ax . tick0 = Lib . dateTick0 ( ax . calendar , true ) ;
926
986
927
- if ( / % [ u V W ] / . test ( ax . tickformat ) ) {
987
+ var tickformat = axes . getTickFormat ( ax ) ;
988
+ if ( / % [ u V W ] / . test ( tickformat ) ) {
928
989
// replace Sunday with Monday for ISO and Monday-based formats
929
990
var len = ax . tick0 . length ;
930
991
var lastD = + ax . tick0 [ len - 1 ] ;
0 commit comments