1818*/
1919
2020import { each , map } from 'zrender/src/core/util' ;
21- import { linearMap , round } from '../util/number' ;
21+ import { linearMap } from '../util/number' ;
2222import {
2323 createAxisTicks ,
2424 createAxisLabels ,
2525 calculateCategoryInterval ,
2626 AxisLabelsComputingContext ,
2727 AxisTickLabelComputingKind ,
2828 createAxisLabelsComputingContext ,
29+ AxisLabelInfoDetermined ,
2930} from './axisTickLabelBuilder' ;
3031import Scale , { ScaleGetTicksOpt } from '../scale/Scale' ;
3132import { DimensionName , NullUndefined , ScaleDataValue , ScaleTick } from '../util/types' ;
3233import OrdinalScale from '../scale/Ordinal' ;
3334import Model from '../model/Model' ;
34- import { AxisBaseOption , CategoryAxisBaseOption , OptionAxisType } from './axisCommonTypes' ;
35+ import {
36+ AxisBaseOption , AxisTickOptionUnion , CategoryAxisBaseOption ,
37+ CategoryTickLabelSplitBuildingOption , OptionAxisType
38+ } from './axisCommonTypes' ;
3539import { AxisBaseModel } from './AxisBaseModel' ;
3640import { isOrdinalScale } from '../scale/helper' ;
3741import { calcBandWidth } from './axisBand' ;
@@ -40,8 +44,7 @@ const NORMALIZED_EXTENT = [0, 1] as [number, number];
4044
4145export interface AxisTickCoord {
4246 coord : number ;
43- // That is `scaleTick.value`.
44- tickValue ?: ScaleTick [ 'value' ] ;
47+ tickValue : ScaleTick [ 'value' ] ;
4548 // `true` if onBand fixed.
4649 onBand ?: boolean ;
4750}
@@ -159,42 +162,46 @@ class Axis {
159162 * `axis.getTicksCoords` considers `onBand`, which is used by
160163 * `boundaryGap:true` of category axis and splitLine and splitArea.
161164 * @param opt.tickModel default: axis.model.getModel('axisTick')
162- * @param opt.clamp If `true`, the first and the last
163- * tick must be at the axis end points. Otherwise, clip ticks
164- * that outside the axis extent.
165165 */
166166 getTicksCoords ( opt ?: {
167- tickModel ?: Model ,
168- clamp ?: boolean ,
167+ tickModel ?: Model < CategoryTickLabelSplitBuildingOption > ,
169168 breakTicks ?: ScaleGetTicksOpt [ 'breakTicks' ] ,
170169 pruneByBreak ?: ScaleGetTicksOpt [ 'pruneByBreak' ]
171170 } ) : AxisTickCoord [ ] {
172171 opt = opt || { } ;
173-
174172 const tickModel = opt . tickModel || this . getTickModel ( ) ;
173+
175174 const result = createAxisTicks ( this , tickModel as AxisBaseModel , {
176175 breakTicks : opt . breakTicks ,
177176 pruneByBreak : opt . pruneByBreak ,
178177 } ) ;
179- const ticksCoords = map ( result . ticks , function ( tick ) {
178+ const preTicksCoords = map ( result . ticks , function ( tick ) {
180179 const tickValue = tick . value ;
181180 return {
182181 coord : this . dataToCoord (
183182 isOrdinalScale ( this . scale )
184183 ? ( this . scale as OrdinalScale ) . getRawOrdinalNumber ( tickValue )
185184 : tickValue
186185 ) ,
187- tickValue ,
186+ tick ,
188187 } ;
189188 } , this ) ;
190189
191- const alignWithLabel = tickModel . get ( 'alignWithLabel' ) ;
190+ const alignWithLabel = ( tickModel as Model < CategoryAxisBaseOption [ 'axisTick' ] > ) . get ( 'alignWithLabel' ) ;
192191
193- fixOnBandTicksCoords (
194- this , ticksCoords , alignWithLabel , opt . clamp
192+ const onBandModified = fixOnBandTicksCoords (
193+ this ,
194+ preTicksCoords ,
195+ alignWithLabel ,
195196 ) ;
196197
197- return ticksCoords ;
198+ return map ( preTicksCoords , function ( item ) {
199+ return {
200+ coord : item . coord ,
201+ tickValue : item . tick . value ,
202+ onBand : onBandModified ,
203+ } ;
204+ } ) ;
198205 }
199206
200207 getMinorTicksCoords ( ) : AxisTickCoord [ ] [ ] {
@@ -214,7 +221,7 @@ class Axis {
214221 return map ( minorTicksGroup , function ( minorTick ) {
215222 return {
216223 coord : this . dataToCoord ( minorTick ) ,
217- tickValue : minorTick
224+ tickValue : minorTick ,
218225 } ;
219226 } , this ) ;
220227 } , this ) ;
@@ -223,7 +230,7 @@ class Axis {
223230
224231 getViewLabels (
225232 ctx ?: AxisLabelsComputingContext
226- ) : ReturnType < typeof createAxisLabels > [ 'labels' ] {
233+ ) : AxisLabelInfoDetermined [ ] {
227234 ctx = ctx || createAxisLabelsComputingContext ( AxisTickLabelComputingKind . determine ) ;
228235 return createAxisLabels ( this , ctx ) . labels ;
229236 }
@@ -239,7 +246,7 @@ class Axis {
239246 * In GL, this method may be overridden to:
240247 * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));`
241248 */
242- getTickModel ( ) : Model {
249+ getTickModel ( ) : Model < AxisTickOptionUnion > {
243250 return this . model . getModel ( 'axisTick' ) ;
244251 }
245252
@@ -275,79 +282,80 @@ function makeExtentWithBands(axis: Axis): number[] {
275282 const margin = size / ( axis . scale as OrdinalScale ) . count ( ) / 2 ;
276283 extent [ 0 ] += margin ;
277284 extent [ 1 ] -= margin ;
285+
278286 }
279287 return extent ;
280288}
281289
282- // If axis has labels [1, 2, 3, 4]. Bands on the axis are
283- // |---1---|---2---|---3---|---4---|.
284- // So the displayed ticks and splitLine/splitArea should between
285- // each data item, otherwise cause misleading (e.g., split tow bars
286- // of a single data item when there are two bar series).
287- // Also consider if tickCategoryInterval > 0 and onBand, ticks and
288- // splitLine/spliteArea should layout appropriately corresponding
289- // to displayed labels. (So we should not use `getBandWidth` in this
290- // case).
290+ /**
291+ * `axis.onBand: true` (i.e., `boundaryGap: true` in ec option) and `CategoryTickLabelSplitIntervalOption`
292+ * affects `axisTick`/`axisLabel`/`splitLine`/`splitArea`.
293+ *
294+ * Currently, the visual result is best only when `axisTick/splitLine/splitArea.interval === 0`.
295+ * The typical case is:
296+ * |---|---|---| <= This is the input `preTicksCoords`
297+ * 0 1 2 3 (having been added half band width by `makeExtentWithBands`).
298+ * |---|---|---|---| <= This is the result.
299+ * 0 1 2 3
300+ *
301+ * When `interval > 0`, the visual result may be odd for `axisLabel` and `customValues`, but acceptable
302+ * for `axisTick` `splitLine` and `splitArea`:
303+ * |---~---|---~---~---|---| <= This is the input `preTicksCoords`; `interval: 2; min: 1; max: 7`.
304+ * ₁ ₂ 3 ₄ ₅ 6 ₇ Subscript numbers (`₀`, `₁`, `₃`) indicate axis labels are hidden
305+ * (by default settings) due to off-interval.
306+ * A tilde (`~`) indicates a tick ignored due to off-interval.
307+ * |---~---|---~---~---|---~---| <= This is the result.
308+ * ₁ ₂ 3 ₄ ₅ 6 ₇
309+ *
310+ * NOTE:
311+ * - A inappropriate result may cause misleading (e.g., split 2 bars of a single data item when there
312+ * are two bar series).
313+ * - See also #11176 #11186 .
314+ * PENDING:
315+ * - The show/hide of `axisLabel` may be optimized when `interval > 1 and be an even number`,
316+ * but that may introduce complex and still not perfect in odd number, and may not necessary if
317+ * `axisTick: {show: false}` and `axisLabel` can auto hidden when overlapping.
318+ */
291319function fixOnBandTicksCoords (
292320 axis : Axis ,
293- ticksCoords : AxisTickCoord [ ] ,
321+ preTicksCoords : {
322+ coord : AxisTickCoord [ 'coord' ] ,
323+ tick : ScaleTick
324+ } [ ] ,
294325 alignWithLabel : boolean ,
295- clamp : boolean
296- ) {
297- const ticksLen = ticksCoords . length ;
326+ // return: whether coords are modified according to `onBand`.
327+ ) : boolean {
328+ const ticksLen = preTicksCoords . length ;
298329
299330 if ( ! axis . onBand || alignWithLabel || ! ticksLen ) {
300- return ;
331+ return false ;
301332 }
302333
303- const axisExtent = axis . getExtent ( ) ;
304- let last ;
305- let diffSize ;
306- if ( ticksLen === 1 ) {
307- ticksCoords [ 0 ] . coord = axisExtent [ 0 ] ;
308- ticksCoords [ 0 ] . onBand = true ;
309- last = ticksCoords [ 1 ] = { coord : axisExtent [ 1 ] , tickValue : ticksCoords [ 0 ] . tickValue , onBand : true } ;
334+ // Assume:
335+ // - If `onBand: true`, `bandWidth` has been calculated by `ticksLen + 1` rather than `ticksLen`.
336+ // - If `interval > 0`, some ticks may be ignored, but `ticksCoords` has always included boundary
337+ // ticks of axis extent, and be `offInterval: true` if off-interval.
338+ // - No need to consider breaks, since axis break is not supported in category axis.
339+ const bandWidth = calcBandWidth ( axis ) . w ;
340+ if ( ! bandWidth ) {
341+ return false ;
310342 }
311- else {
312- const crossLen = ticksCoords [ ticksLen - 1 ] . tickValue - ticksCoords [ 0 ] . tickValue ;
313- const shift = ( ticksCoords [ ticksLen - 1 ] . coord - ticksCoords [ 0 ] . coord ) / crossLen ;
314-
315- each ( ticksCoords , function ( ticksItem ) {
316- ticksItem . coord -= shift / 2 ;
317- ticksItem . onBand = true ;
318- } ) ;
319343
320- const dataExtent = axis . scale . getExtent ( ) ;
321- diffSize = 1 + dataExtent [ 1 ] - ticksCoords [ ticksLen - 1 ] . tickValue ;
344+ each ( preTicksCoords , function ( ticksItem ) {
345+ ticksItem . coord -= bandWidth / 2 ;
346+ } ) ;
322347
323- last = { coord : ticksCoords [ ticksLen - 1 ] . coord + shift * diffSize , tickValue : dataExtent [ 1 ] + 1 , onBand : true } ;
324-
325- ticksCoords . push ( last ) ;
348+ const dataExtent = axis . scale . getExtent ( ) ;
349+ const oldLast = preTicksCoords [ ticksLen - 1 ] ;
350+ if ( oldLast . tick . offInterval ) {
351+ preTicksCoords . pop ( ) ;
326352 }
353+ preTicksCoords . push ( {
354+ coord : oldLast . coord + bandWidth ,
355+ tick : { value : dataExtent [ 1 ] + 1 } ,
356+ } ) ;
327357
328- const inverse = axisExtent [ 0 ] > axisExtent [ 1 ] ;
329-
330- // Handling clamp.
331- if ( littleThan ( ticksCoords [ 0 ] . coord , axisExtent [ 0 ] ) ) {
332- clamp ? ( ticksCoords [ 0 ] . coord = axisExtent [ 0 ] ) : ticksCoords . shift ( ) ;
333- }
334- if ( clamp && littleThan ( axisExtent [ 0 ] , ticksCoords [ 0 ] . coord ) ) {
335- ticksCoords . unshift ( { coord : axisExtent [ 0 ] , onBand : true } ) ;
336- }
337- if ( littleThan ( axisExtent [ 1 ] , last . coord ) ) {
338- clamp ? ( last . coord = axisExtent [ 1 ] ) : ticksCoords . pop ( ) ;
339- }
340- if ( clamp && littleThan ( last . coord , axisExtent [ 1 ] ) ) {
341- ticksCoords . push ( { coord : axisExtent [ 1 ] , onBand : true } ) ;
342- }
343-
344- function littleThan ( a : number , b : number ) : boolean {
345- // Avoid rounding error cause calculated tick coord different with extent.
346- // It may cause an extra unnecessary tick added.
347- a = round ( a , 10 ) ;
348- b = round ( b , 10 ) ;
349- return inverse ? a > b : a < b ;
350- }
358+ return true ;
351359}
352360
353361export default Axis ;
0 commit comments