1717* under the License.
1818*/
1919
20- import { getPrecision , round , nice , quantityExponent } from '../util/number' ;
20+ import { getPrecision , round , nice , quantityExponent , mathPow , mathMax , mathRound } from '../util/number' ;
2121import IntervalScale from './Interval' ;
2222import LogScale from './Log' ;
2323import type Scale from './Scale' ;
2424import { bind } from 'zrender/src/core/util' ;
2525import type { ScaleBreakContext } from './break' ;
26+ import TimeScale from './Time' ;
27+ import { NullUndefined } from '../util/types' ;
2628
2729type intervalScaleNiceTicksResult = {
2830 interval : number ,
2931 intervalPrecision : number ,
3032 niceTickExtent : [ number , number ]
3133} ;
3234
33- export function isValueNice ( val : number ) {
34- const exp10 = Math . pow ( 10 , quantityExponent ( Math . abs ( val ) ) ) ;
35- const f = Math . abs ( val / exp10 ) ;
36- return f === 0
37- || f === 1
38- || f === 2
39- || f === 3
40- || f === 5 ;
41- }
35+ /**
36+ * See also method `nice` in `src/util/number.ts`.
37+ */
38+ // export function isValueNice(val: number) {
39+ // const exp10 = Math.pow(10, quantityExponent(Math.abs(val)));
40+ // const f = Math.abs(round(val / exp10, 0));
41+ // return f === 0
42+ // || f === 1
43+ // || f === 2
44+ // || f === 3
45+ // || f === 5;
46+ // }
4247
4348export function isIntervalOrLogScale ( scale : Scale ) : scale is LogScale | IntervalScale {
44- return scale . type === 'interval' || scale . type === 'log' ;
49+ return isIntervalScale ( scale ) || isLogScale ( scale ) ;
50+ }
51+
52+ export function isIntervalScale ( scale : Scale ) : scale is IntervalScale {
53+ return scale . type === 'interval' ;
4554}
55+
56+ export function isTimeScale ( scale : Scale ) : scale is TimeScale {
57+ return scale . type === 'time' ;
58+ }
59+
60+ export function isLogScale ( scale : Scale ) : scale is LogScale {
61+ return scale . type === 'log' ;
62+ }
63+
64+ export function isOrdinalScale ( scale : Scale ) : boolean {
65+ return scale . type === 'ordinal' ;
66+ }
67+
4668/**
4769 * @param extent Both extent[0] and extent[1] should be valid number.
4870 * Should be extent[0] < extent[1].
@@ -65,23 +87,29 @@ export function intervalScaleNiceTicks(
6587 if ( maxInterval != null && interval > maxInterval ) {
6688 interval = result . interval = maxInterval ;
6789 }
68- // Tow more digital for tick.
6990 const precision = result . intervalPrecision = getIntervalPrecision ( interval ) ;
7091 // Niced extent inside original extent
7192 const niceTickExtent = result . niceTickExtent = [
7293 round ( Math . ceil ( extent [ 0 ] / interval ) * interval , precision ) ,
7394 round ( Math . floor ( extent [ 1 ] / interval ) * interval , precision )
7495 ] ;
7596
76- fixExtent ( niceTickExtent , extent ) ;
97+ fixNiceExtent ( niceTickExtent , extent ) ;
7798
7899 return result ;
79100}
80101
81- export function increaseInterval ( interval : number ) {
82- const exp10 = Math . pow ( 10 , quantityExponent ( interval ) ) ;
83- // Increase interval
84- let f = interval / exp10 ;
102+ /**
103+ * The input `niceInterval` should be generated
104+ * from `nice` method in `src/util/number.ts`, or
105+ * from `increaseInterval` itself.
106+ */
107+ export function increaseInterval ( niceInterval : number ) {
108+ const exponent = quantityExponent ( niceInterval ) ;
109+ // No rounding error in Math.pow(10, xxx).
110+ const exp10 = mathPow ( 10 , exponent ) ;
111+ // Fix IEEE 754 float rounding error
112+ let f = mathRound ( niceInterval / exp10 ) ;
85113 if ( ! f ) {
86114 f = 1 ;
87115 }
@@ -94,25 +122,26 @@ export function increaseInterval(interval: number) {
94122 else { // f is 1 or 5
95123 f *= 2 ;
96124 }
97- return round ( f * exp10 ) ;
125+ // Fix IEEE 754 float rounding error
126+ return round ( f * exp10 , - exponent ) ;
98127}
99128
100- /**
101- * @return interval precision
102- */
103- export function getIntervalPrecision ( interval : number ) : number {
129+ export function getIntervalPrecision ( niceInterval : number ) : number {
104130 // Tow more digital for tick.
105- return getPrecision ( interval ) + 2 ;
131+ // NOTE: `2` was introduced in commit `af2a2a9f6303081d7c3b52f0a38add07b4c6e0c7`;
132+ // it works on "nice" interval, but seems not necessarily mathematically required.
133+ return getPrecision ( niceInterval ) + 2 ;
106134}
107135
136+
108137function clamp (
109138 niceTickExtent : [ number , number ] , idx : number , extent : [ number , number ]
110139) : void {
111140 niceTickExtent [ idx ] = Math . max ( Math . min ( niceTickExtent [ idx ] , extent [ 1 ] ) , extent [ 0 ] ) ;
112141}
113142
114143// In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent.
115- export function fixExtent (
144+ export function fixNiceExtent (
116145 niceTickExtent : [ number , number ] , extent : [ number , number ]
117146) : void {
118147 ! isFinite ( niceTickExtent [ 0 ] ) && ( niceTickExtent [ 0 ] = extent [ 0 ] ) ;
@@ -173,3 +202,70 @@ export function logTransform(base: number, extent: number[], noClampNegative?: b
173202 Math . log ( noClampNegative ? extent [ 1 ] : Math . max ( 0 , extent [ 1 ] ) ) / loggedBase
174203 ] ;
175204}
205+
206+ export function powTransform ( base : number , extent : number [ ] ) : [ number , number ] {
207+ return [
208+ mathPow ( base , extent [ 0 ] ) ,
209+ mathPow ( base , extent [ 1 ] )
210+ ] ;
211+ }
212+
213+ /**
214+ * A valid extent is:
215+ * - No non-finite number.
216+ * - `extent[0] < extent[1]`.
217+ *
218+ * [NOTICE]: The input `rawExtent` can only be:
219+ * - All non-finite numbers or `NaN`; or
220+ * - `[Infinity, -Infinity]` (A typical initial extent with no data.)
221+ * (Improve it when needed.)
222+ */
223+ export function intervalScaleEnsureValidExtent (
224+ rawExtent : number [ ] ,
225+ opt : {
226+ fixMax ?: boolean
227+ }
228+ ) : number [ ] {
229+ const extent = rawExtent . slice ( ) ;
230+ // If extent start and end are same, expand them
231+ if ( extent [ 0 ] === extent [ 1 ] ) {
232+ if ( extent [ 0 ] !== 0 ) {
233+ // Expand extent
234+ // Note that extents can be both negative. See #13154
235+ const expandSize = Math . abs ( extent [ 0 ] ) ;
236+ // In the fowllowing case
237+ // Axis has been fixed max 100
238+ // Plus data are all 100 and axis extent are [100, 100].
239+ // Extend to the both side will cause expanded max is larger than fixed max.
240+ // So only expand to the smaller side.
241+ if ( ! opt . fixMax ) {
242+ extent [ 1 ] += expandSize / 2 ;
243+ extent [ 0 ] -= expandSize / 2 ;
244+ }
245+ else {
246+ extent [ 0 ] -= expandSize / 2 ;
247+ }
248+ }
249+ else {
250+ extent [ 1 ] = 1 ;
251+ }
252+ }
253+ const span = extent [ 1 ] - extent [ 0 ] ;
254+ // If there are no data and extent are [Infinity, -Infinity]
255+ if ( ! isFinite ( span ) ) {
256+ extent [ 0 ] = 0 ;
257+ extent [ 1 ] = 1 ;
258+ }
259+ else if ( span < 0 ) {
260+ extent . reverse ( ) ;
261+ }
262+
263+ return extent ;
264+ }
265+
266+ export function ensureValidSplitNumber (
267+ rawSplitNumber : number | NullUndefined , defaultSplitNumber : number
268+ ) : number {
269+ rawSplitNumber = rawSplitNumber || defaultSplitNumber ;
270+ return mathRound ( mathMax ( rawSplitNumber , 1 ) ) ;
271+ }
0 commit comments