@@ -5,41 +5,68 @@ import Scale from '../core/core.scale';
55import LinearScaleBase from './scale.linearbase' ;
66import Ticks from '../core/core.ticks' ;
77
8+ const log10Floor = v => Math . floor ( log10 ( v ) ) ;
9+ const changeExponent = ( v , m ) => Math . pow ( 10 , log10Floor ( v ) + m ) ;
10+
811function isMajor ( tickVal ) {
9- const remain = tickVal / ( Math . pow ( 10 , Math . floor ( log10 ( tickVal ) ) ) ) ;
12+ const remain = tickVal / ( Math . pow ( 10 , log10Floor ( tickVal ) ) ) ;
1013 return remain === 1 ;
1114}
1215
16+ function steps ( min , max , rangeExp ) {
17+ const rangeStep = Math . pow ( 10 , rangeExp ) ;
18+ const start = Math . floor ( min / rangeStep ) ;
19+ const end = Math . ceil ( max / rangeStep ) ;
20+ return end - start ;
21+ }
22+
23+ function startExp ( min , max ) {
24+ const range = max - min ;
25+ let rangeExp = log10Floor ( range ) ;
26+ while ( steps ( min , max , rangeExp ) > 10 ) {
27+ rangeExp ++ ;
28+ }
29+ while ( steps ( min , max , rangeExp ) < 10 ) {
30+ rangeExp -- ;
31+ }
32+ return Math . min ( rangeExp , log10Floor ( min ) ) ;
33+ }
34+
35+
1336/**
1437 * Generate a set of logarithmic ticks
1538 * @param generationOptions the options used to generate the ticks
1639 * @param dataRange the range of the data
1740 * @returns {object[] } array of tick objects
1841 */
19- function generateTicks ( generationOptions , dataRange ) {
20- const endExp = Math . floor ( log10 ( dataRange . max ) ) ;
21- const endSignificand = Math . ceil ( dataRange . max / Math . pow ( 10 , endExp ) ) ;
42+ function generateTicks ( generationOptions , { min, max} ) {
43+ min = finiteOrDefault ( generationOptions . min , min ) ;
2244 const ticks = [ ] ;
23- let tickVal = finiteOrDefault ( generationOptions . min , Math . pow ( 10 , Math . floor ( log10 ( dataRange . min ) ) ) ) ;
24- let exp = Math . floor ( log10 ( tickVal ) ) ;
25- let significand = Math . floor ( tickVal / Math . pow ( 10 , exp ) ) ;
45+ const minExp = log10Floor ( min ) ;
46+ let exp = startExp ( min , max ) ;
2647 let precision = exp < 0 ? Math . pow ( 10 , Math . abs ( exp ) ) : 1 ;
27-
28- do {
29- ticks . push ( { value : tickVal , major : isMajor ( tickVal ) } ) ;
30-
31- ++ significand ;
32- if ( significand === 10 ) {
33- significand = 1 ;
34- ++ exp ;
48+ const stepSize = Math . pow ( 10 , exp ) ;
49+ const base = minExp > exp ? Math . pow ( 10 , minExp ) : 0 ;
50+ const start = Math . round ( ( min - base ) * precision ) / precision ;
51+ const offset = Math . floor ( ( min - base ) / stepSize / 10 ) * stepSize * 10 ;
52+ let significand = Math . floor ( ( start - offset ) / Math . pow ( 10 , exp ) ) ;
53+ let value = finiteOrDefault ( generationOptions . min , Math . round ( ( base + offset + significand * Math . pow ( 10 , exp ) ) * precision ) / precision ) ;
54+ while ( value < max ) {
55+ ticks . push ( { value, major : isMajor ( value ) , significand} ) ;
56+ if ( significand >= 10 ) {
57+ significand = significand < 15 ? 15 : 20 ;
58+ } else {
59+ significand ++ ;
60+ }
61+ if ( significand >= 20 ) {
62+ exp ++ ;
63+ significand = 2 ;
3564 precision = exp >= 0 ? 1 : precision ;
3665 }
37-
38- tickVal = Math . round ( significand * Math . pow ( 10 , exp ) * precision ) / precision ;
39- } while ( exp < endExp || ( exp === endExp && significand < endSignificand ) ) ;
40-
41- const lastTick = finiteOrDefault ( generationOptions . max , tickVal ) ;
42- ticks . push ( { value : lastTick , major : isMajor ( tickVal ) } ) ;
66+ value = Math . round ( ( base + offset + significand * Math . pow ( 10 , exp ) ) * precision ) / precision ;
67+ }
68+ const lastTick = finiteOrDefault ( generationOptions . max , value ) ;
69+ ticks . push ( { value : lastTick , major : isMajor ( lastTick ) , significand} ) ;
4370
4471 return ticks ;
4572}
@@ -92,6 +119,12 @@ export default class LogarithmicScale extends Scale {
92119 this . _zero = true ;
93120 }
94121
122+ // if data has `0` in it or `beginAtZero` is true, min (non zero) value is at bottom
123+ // of scale, and it does not equal suggestedMin, lower the min bound by one exp.
124+ if ( this . _zero && this . min !== this . _suggestedMin && ! isFinite ( this . _userMin ) ) {
125+ this . min = min === changeExponent ( this . min , 0 ) ? changeExponent ( this . min , - 1 ) : changeExponent ( this . min , 0 ) ;
126+ }
127+
95128 this . handleTickRangeOptions ( ) ;
96129 }
97130
@@ -102,28 +135,24 @@ export default class LogarithmicScale extends Scale {
102135
103136 const setMin = v => ( min = minDefined ? min : v ) ;
104137 const setMax = v => ( max = maxDefined ? max : v ) ;
105- const exp = ( v , m ) => Math . pow ( 10 , Math . floor ( log10 ( v ) ) + m ) ;
106138
107139 if ( min === max ) {
108140 if ( min <= 0 ) { // includes null
109141 setMin ( 1 ) ;
110142 setMax ( 10 ) ;
111143 } else {
112- setMin ( exp ( min , - 1 ) ) ;
113- setMax ( exp ( max , + 1 ) ) ;
144+ setMin ( changeExponent ( min , - 1 ) ) ;
145+ setMax ( changeExponent ( max , + 1 ) ) ;
114146 }
115147 }
116148 if ( min <= 0 ) {
117- setMin ( exp ( max , - 1 ) ) ;
149+ setMin ( changeExponent ( max , - 1 ) ) ;
118150 }
119151 if ( max <= 0 ) {
120- setMax ( exp ( min , + 1 ) ) ;
121- }
122- // if data has `0` in it or `beginAtZero` is true, min (non zero) value is at bottom
123- // of scale, and it does not equal suggestedMin, lower the min bound by one exp.
124- if ( this . _zero && this . min !== this . _suggestedMin && min === exp ( this . min , 0 ) ) {
125- setMin ( exp ( min , - 1 ) ) ;
152+
153+ setMax ( changeExponent ( min , + 1 ) ) ;
126154 }
155+
127156 this . min = min ;
128157 this . max = max ;
129158 }
0 commit comments