@@ -5,41 +5,68 @@ import Scale from '../core/core.scale';
5
5
import LinearScaleBase from './scale.linearbase' ;
6
6
import Ticks from '../core/core.ticks' ;
7
7
8
+ const log10Floor = v => Math . floor ( log10 ( v ) ) ;
9
+ const changeExponent = ( v , m ) => Math . pow ( 10 , log10Floor ( v ) + m ) ;
10
+
8
11
function isMajor ( tickVal ) {
9
- const remain = tickVal / ( Math . pow ( 10 , Math . floor ( log10 ( tickVal ) ) ) ) ;
12
+ const remain = tickVal / ( Math . pow ( 10 , log10Floor ( tickVal ) ) ) ;
10
13
return remain === 1 ;
11
14
}
12
15
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
+
13
36
/**
14
37
* Generate a set of logarithmic ticks
15
38
* @param generationOptions the options used to generate the ticks
16
39
* @param dataRange the range of the data
17
40
* @returns {object[] } array of tick objects
18
41
*/
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 ) ;
22
44
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 ) ;
26
47
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 ;
35
64
precision = exp >= 0 ? 1 : precision ;
36
65
}
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} ) ;
43
70
44
71
return ticks ;
45
72
}
@@ -92,6 +119,12 @@ export default class LogarithmicScale extends Scale {
92
119
this . _zero = true ;
93
120
}
94
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 && ! isFinite ( this . _userMin ) ) {
125
+ this . min = min === changeExponent ( this . min , 0 ) ? changeExponent ( this . min , - 1 ) : changeExponent ( this . min , 0 ) ;
126
+ }
127
+
95
128
this . handleTickRangeOptions ( ) ;
96
129
}
97
130
@@ -102,28 +135,24 @@ export default class LogarithmicScale extends Scale {
102
135
103
136
const setMin = v => ( min = minDefined ? min : v ) ;
104
137
const setMax = v => ( max = maxDefined ? max : v ) ;
105
- const exp = ( v , m ) => Math . pow ( 10 , Math . floor ( log10 ( v ) ) + m ) ;
106
138
107
139
if ( min === max ) {
108
140
if ( min <= 0 ) { // includes null
109
141
setMin ( 1 ) ;
110
142
setMax ( 10 ) ;
111
143
} else {
112
- setMin ( exp ( min , - 1 ) ) ;
113
- setMax ( exp ( max , + 1 ) ) ;
144
+ setMin ( changeExponent ( min , - 1 ) ) ;
145
+ setMax ( changeExponent ( max , + 1 ) ) ;
114
146
}
115
147
}
116
148
if ( min <= 0 ) {
117
- setMin ( exp ( max , - 1 ) ) ;
149
+ setMin ( changeExponent ( max , - 1 ) ) ;
118
150
}
119
151
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 ) ) ;
126
154
}
155
+
127
156
this . min = min ;
128
157
this . max = max ;
129
158
}
0 commit comments