1717* under the License.
1818*/
1919
20- import { assert , each } from 'zrender/src/core/util' ;
20+ import { assert , each , retrieve2 } from 'zrender/src/core/util' ;
2121import { NullUndefined } from '../util/types' ;
2222import type Axis from './Axis' ;
23- import type Scale from '../scale/Scale' ;
2423import { isOrdinalScale } from '../scale/helper' ;
2524import { isNullableNumberFinite , mathAbs , mathMax } from '../util/number' ;
2625import {
@@ -34,15 +33,15 @@ import type SeriesModel from '../model/Series';
3433const FALLBACK_BAND_WIDTH_RATIO = 0.8 ;
3534
3635export type AxisBandWidthResult = {
37- // The result ` bandWidth`. In pixel.
36+ // bandWidth in pixel.
3837 // Never be null/undefined.
39- // May be NaN if no meaningfull `bandWidth` . But it's unlikely to be NaN, since edge cases
38+ // May be NaN if no meaningfull value . But it's unlikely to be NaN, since edge cases
4039 // are handled internally whenever possible.
4140 w : number ;
42- // This is a ratio from pixel span to data span .
43- // Note that the conversion can not be performed if it is not valid, typically when only
44- // one or no series data item exists on the axis .
45- invRatio ? : number | NullUndefined ;
41+ // bandWidth in data space .
42+ // Never be null/undefined.
43+ // May be NaN if no meaningfull value, typically when no valid series data item .
44+ w2 : number ;
4645} ;
4746
4847/**
@@ -86,16 +85,36 @@ export function calcBandWidth(
8685 opt ?: CalculateBandWidthOpt | NullUndefined
8786) : AxisBandWidthResult {
8887 opt = opt || { } ;
89- const out : AxisBandWidthResult = { w : NaN } ;
88+ const out : AxisBandWidthResult = { w : NaN , w2 : NaN } ;
9089 const scale = axis . scale ;
9190 const fromStat = opt . fromStat ;
9291 const min = opt . min ;
9392
93+ // [BAND_WIDTH_USED_SCALE_LINEAR_SPAN]
94+ // - Band width should always respect to the currently specified extent, and `SCALE_EXTENT_KIND_MAPPING`
95+ // should be used if specified.
96+ // Otherwise, the result may incorrect, especially when data count is small.
97+ // For example, when "containShape" is calculating, no `SCALE_EXTENT_KIND_MAPPING` is set, so here only
98+ // `SCALE_EXTENT_KIND_EFFECTIVE` is returned, say, `[3, 5]`, based on which a `SCALE_EXTENT_KIND_MAPPING`
99+ // is calculated, say `[2.5, 5.5]` (expanded by `0.5`). Then when rendering, that `SCALE_EXTENT_KIND_MAPPING`
100+ // is returned here.
101+ // See AXIS_CONTAIN_SHAPE_COMMON_STRATEGY for more details.
102+ // - The span should be in the linear space (typically, the innermost space).
103+ // - We use the scale extent after being zoommed and `intervalScaleEnsureValidExtent`-ish applied and
104+ // "nice"/"align" applied, because:
105+ // - For OrdinalScale, fine;
106+ // - For numeric scale, `scaleLinearSpan` is normally not used for a consistent result when `dataZoom`
107+ // is applied, but used when none or single data item case.
108+ const scaleLinearSpan = getScaleLinearSpanForMapping ( scale ) ;
109+ const axisExtent = axis . getExtent ( ) ;
110+ // Always use a new pxSpan because it may be changed in `grid` contain label calculation.
111+ const pxSpan = mathAbs ( axisExtent [ 1 ] - axisExtent [ 0 ] ) ;
112+
94113 if ( isOrdinalScale ( scale ) ) {
95- calcBandWidthForCategoryAxis ( out , axis , scale ) ;
114+ calcBandWidthForCategoryAxis ( out , axis , scaleLinearSpan , pxSpan ) ;
96115 }
97116 else if ( fromStat ) {
98- calcBandWidthForNumericAxis ( out , axis , scale , fromStat ) ;
117+ calcBandWidthForNumericAxis ( out , axis , scaleLinearSpan , pxSpan , fromStat ) ;
99118 }
100119 else if ( min == null ) {
101120 if ( __DEV__ ) {
@@ -111,52 +130,36 @@ export function calcBandWidth(
111130 return out ;
112131}
113132
114- /**
115- * Only reasonable on 'category'.
116- *
117- * It can be used as a fallback, as it does not produce a significant negative impact
118- * on non-category axes.
119- *
120- * @see CalculateBandWidthOpt
121- */
122133function calcBandWidthForCategoryAxis (
123134 out : AxisBandWidthResult ,
124135 axis : Axis ,
125- scale : Scale
136+ scaleLinearSpan : number ,
137+ pxSpan : number ,
126138) : void {
127- const axisExtent = axis . getExtent ( ) ;
128- const pxSpan = mathAbs ( axisExtent [ 1 ] - axisExtent [ 0 ] ) ;
129- // See the reason on BAND_WIDTH_USED_LINEAR_SCALE_SPAN.
130- const linearScaleSpan = getScaleLinearSpanForMapping ( scale ) ;
131139 const onBand = axis . onBand ;
132140
133- let len = linearScaleSpan + ( onBand ? 1 : 0 ) ;
141+ let len = scaleLinearSpan + ( onBand ? 1 : 0 ) ;
134142 // Fix #2728, avoid NaN when only one data.
135143 len === 0 && ( len = 1 ) ;
136144
137145 out . w = pxSpan / len ;
138146 // NOTE:
139- // - When `linearScaleSpan === 0`, no need to expand extent.
147+ // - When `scaleLinearSpan === 0`, no need to expand extent.
140148 // - `onBand: true` (`boundaryGap: true`) does not need to support `containShape`,
141149 // thereby no `invRatio`.
142- if ( ! onBand && linearScaleSpan && pxSpan ) {
143- out . invRatio = linearScaleSpan / pxSpan ;
150+ if ( ! onBand && scaleLinearSpan && pxSpan ) {
151+ out . w2 = out . w * scaleLinearSpan / pxSpan ;
144152 }
145153}
146154
147- /**
148- * @see CalculateBandWidthOpt
149- */
150155function calcBandWidthForNumericAxis (
151156 out : AxisBandWidthResult ,
152157 axis : Axis ,
153- scale : Scale ,
158+ scaleLinearSpan : number ,
159+ pxSpan : number ,
154160 fromStat : CalculateBandWidthOpt [ 'fromStat' ] ,
155161) : void {
156162
157- let bandWidth = NaN ;
158- let invRatio : number | NullUndefined ;
159-
160163 if ( __DEV__ ) {
161164 assert ( fromStat ) ;
162165 }
@@ -184,36 +187,16 @@ function calcBandWidthForNumericAxis(
184187 }
185188 ) ;
186189
187- const axisExtent = axis . getExtent ( ) ;
188- // Always use a new pxSpan because it may be changed in `grid` contain label calculation.
189- const pxSpan = mathAbs ( axisExtent [ 1 ] - axisExtent [ 0 ] ) ;
190-
191- // [BAND_WIDTH_USED_LINEAR_SCALE_SPAN]
192- // Here we deliberately use `getScaleLinearSpanForMapping` rather than `scale.getExtent()`,
193- // because band width should always respect to the currently specified extent (e.g., specified by
194- // `calcContainShape`). Otherwise, the result may incorrect, especially when data count is small.
195- // For example, when "containShape" is calculating, no `SCALE_EXTENT_KIND_MAPPING` is set, so here only
196- // `SCALE_EXTENT_KIND_EFFECTIVE` is returned, say, `[3, 5]`, based on which a `SCALE_EXTENT_KIND_MAPPING`
197- // is calculated, say `[2.5, 5.5]` (expanded by `0.5`). Then when rendering, that `SCALE_EXTENT_KIND_MAPPING`
198- // is returned here.
199- // See AXIS_CONTAIN_SHAPE_COMMON_STRATEGY for more details.
200- const linearScaleSpan = getScaleLinearSpanForMapping ( scale ) ;
201-
202- // `linearScaleSpan` may be `0` or `Infinity` or `NaN`, since normalizers like
190+ // `scaleLinearSpan` may be `0` or `Infinity` or `NaN`, since normalizers like
203191 // `intervalScaleEnsureValidExtent` may not have been called yet.
204- if ( isNullableNumberFinite ( linearScaleSpan ) && linearScaleSpan > 0
192+ if ( isNullableNumberFinite ( scaleLinearSpan ) && scaleLinearSpan > 0
205193 && isNullableNumberFinite ( bandWidthInData )
206194 ) {
207- // NOTE: even when the `bandWidth` is far smaller than `1`, we should still preserve the
208- // precision, because it is required to convert back to data space by `invRatio` for
209- // displaying of zoomed ticks and band.
210- bandWidth = pxSpan / linearScaleSpan * bandWidthInData ;
211- invRatio = linearScaleSpan / pxSpan ;
195+ out . w = pxSpan / scaleLinearSpan * bandWidthInData ;
196+ out . w2 = bandWidthInData ;
212197 }
213198 else if ( allSingularOrNone ) {
214- bandWidth = pxSpan * FALLBACK_BAND_WIDTH_RATIO ;
199+ out . w = pxSpan * FALLBACK_BAND_WIDTH_RATIO ;
200+ out . w2 = out . w * scaleLinearSpan / pxSpan ;
215201 }
216-
217- out . w = bandWidth ;
218- out . invRatio = invRatio ;
219202}
0 commit comments