This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch PR/plainheart_fix/alignTicks-precision
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 52ceb924aaa4806fb50502e593871b075c81449d
Author: 100pah <[email protected]>
AuthorDate: Thu Feb 19 00:58:54 2026 +0800

    fix: (1) Fix dataZoom AxisProxy can not be cleared when dataZoom option 
changed. (2) Fix onZero on double value axis and dataZoom is applied on a base 
axis - onZero should be disabled by default.
---
 src/component/dataZoom/AxisProxy.ts         | 15 ++++++++-
 src/component/dataZoom/DataZoomModel.ts     |  1 -
 src/component/dataZoom/SliderZoomModel.ts   |  6 +++-
 src/component/dataZoom/dataZoomProcessor.ts | 23 +++++++------
 src/component/dataZoom/helper.ts            | 42 ++++++++++++++++++------
 src/coord/Axis.ts                           |  6 ++--
 src/coord/AxisBaseModel.ts                  |  2 +-
 src/coord/axisHelper.ts                     | 27 +++++++++++++++-
 src/coord/cartesian/Cartesian2D.ts          |  4 ++-
 src/coord/cartesian/Grid.ts                 | 26 ++++++++-------
 src/core/echarts.ts                         |  4 ++-
 src/layout/barGrid.ts                       |  6 ++--
 src/util/model.ts                           | 41 ++++++++++++++++++-----
 src/util/number.ts                          |  2 ++
 test/bar-overflow-plot2.html                | 50 ++++++++++++++++++++++-------
 15 files changed, 190 insertions(+), 65 deletions(-)

diff --git a/src/component/dataZoom/AxisProxy.ts 
b/src/component/dataZoom/AxisProxy.ts
index 57f034a7c..3bcf7a23a 100644
--- a/src/component/dataZoom/AxisProxy.ts
+++ b/src/component/dataZoom/AxisProxy.ts
@@ -35,6 +35,7 @@ import { isOrdinalScale, isTimeScale } from 
'../../scale/helper';
 import {
     AXIS_EXTENT_INFO_BUILD_FROM_DATA_ZOOM, scaleRawExtentInfoReallyCreate,
 } from '../../coord/scaleRawExtentInfo';
+import { suppressOnAxisZero } from '../../coord/axisHelper';
 
 
 interface MinMaxSpan {
@@ -56,6 +57,8 @@ interface AxisProxyWindow {
 }
 
 /**
+ * NOTICE: Its lifetime is different from `Axis` instance. It is recreated in 
each run of "ec prepare".
+ *
  * Operate single axis.
  * One axis can only operated by one axis operator.
  * Different dataZoomModels may be defined to operate the same axis.
@@ -156,7 +159,8 @@ class AxisProxy {
         const dataExtent = this._dataExtent;
         const axis = this.getAxisModel().axis;
         const scale = axis.scale;
-        const rangePropMode = this._dataZoomModel.getRangePropMode();
+        const dataZoomModel = this._dataZoomModel;
+        const rangePropMode = dataZoomModel.getRangePropMode();
         const percentExtent = [0, 100];
         const percentWindow = [] as unknown as [number, number];
         const valueWindow = [] as unknown as [number, number];
@@ -262,6 +266,13 @@ class AxisProxy {
         const pxSpan = mathAbs(pxExtent[1] - pxExtent[0]);
         const precision = isScaleOrdinalOrTime
             ? 0
+            // NOTICE: We deliberately do not allow specifying this precision 
by users, until real requirements
+            // occur. Otherwise, unnecessary complexity and bad case may be 
introduced. A small precision may
+            // cause the rounded ends overflow the expected min/max 
significantly. And this precision effectively
+            // determines the size of a roaming step, and a big step would 
likely constantly cut through series
+            // shapes in an unexpected place and cause visual artifacts (e.g., 
for bar series). Although
+            // theroetically that defect can be resolved by introducing extra 
spaces between axis min/max tick
+            // and axis boundary (see `SCALE_EXTENT_KIND_MAPPING`), it's 
complicated and unnecessary.
             : getAcceptableTickPrecision(valueWindow, pxSpan, 0.5);
         each([[0, mathCeil], [1, mathFloor]] as const, function ([idx, 
ceilOrFloor]) {
             if (!needRound[idx] || !isFinite(precision)) {
@@ -324,6 +335,8 @@ class AxisProxy {
         const axis = this.getAxisModel().axis;
         scaleRawExtentInfoReallyCreate(this.ecModel, axis, 
AXIS_EXTENT_INFO_BUILD_FROM_DATA_ZOOM);
 
+        suppressOnAxisZero(axis, {dz: true});
+
         const rawExtentInfo = axis.scale.rawExtentInfo;
         this._dataExtent = rawExtentInfo.makeForZoom();
 
diff --git a/src/component/dataZoom/DataZoomModel.ts 
b/src/component/dataZoom/DataZoomModel.ts
index c83e166cb..52d93dc3a 100644
--- a/src/component/dataZoom/DataZoomModel.ts
+++ b/src/component/dataZoom/DataZoomModel.ts
@@ -24,7 +24,6 @@ import {
     LayoutOrient,
     ComponentOption,
     LabelOption,
-    NullUndefined
 } from '../../util/types';
 import Model from '../../model/Model';
 import GlobalModel from '../../model/Global';
diff --git a/src/component/dataZoom/SliderZoomModel.ts 
b/src/component/dataZoom/SliderZoomModel.ts
index 80d206842..53f2d9a54 100644
--- a/src/component/dataZoom/SliderZoomModel.ts
+++ b/src/component/dataZoom/SliderZoomModel.ts
@@ -107,7 +107,11 @@ export interface SliderDataZoomOption
      * Height of handle rect. Can be a percent string relative to the slider 
height.
      */
     moveHandleSize?: number
-
+    /**
+     * The precision only used on displayed labels.
+     * NOTICE: Specifying the "value precision" or "roaming step" is not 
allowed.
+     *  `getAcceptableTickPrecision` is used for that. See `AxisProxy` for 
reasons.
+     */
     labelPrecision?: number | 'auto'
 
     labelFormatter?: string | ((value: number, valueStr: string) => string)
diff --git a/src/component/dataZoom/dataZoomProcessor.ts 
b/src/component/dataZoom/dataZoomProcessor.ts
index d64269295..96033e38d 100644
--- a/src/component/dataZoom/dataZoomProcessor.ts
+++ b/src/component/dataZoom/dataZoomProcessor.ts
@@ -20,9 +20,12 @@
 import {createHashMap, each} from 'zrender/src/core/util';
 import SeriesModel from '../../model/Series';
 import DataZoomModel from './DataZoomModel';
-import { getAxisMainType, DataZoomAxisDimension, 
DataZoomExtendedAxisBaseModel, getAlignTo } from './helper';
+import {
+    getAxisMainType, DataZoomAxisDimension, getAlignTo, getAxisProxyFromModel, 
setAxisProxyToModel
+} from './helper';
 import AxisProxy from './AxisProxy';
 import { StageHandler } from '../../util/types';
+import { AxisBaseModel } from '../../coord/AxisBaseModel';
 
 
 const dataZoomProcessor: StageHandler = {
@@ -36,31 +39,27 @@ const dataZoomProcessor: StageHandler = {
             cb: (
                 axisDim: DataZoomAxisDimension,
                 axisIndex: number,
-                axisModel: DataZoomExtendedAxisBaseModel,
+                axisModel: AxisBaseModel,
                 dataZoomModel: DataZoomModel
             ) => void
         ) {
             ecModel.eachComponent('dataZoom', function (dataZoomModel: 
DataZoomModel) {
                 dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) {
                     const axisModel = 
ecModel.getComponent(getAxisMainType(axisDim), axisIndex);
-                    cb(axisDim, axisIndex, axisModel as 
DataZoomExtendedAxisBaseModel, dataZoomModel);
+                    cb(axisDim, axisIndex, axisModel as AxisBaseModel, 
dataZoomModel);
                 });
             });
         }
         // FIXME: it brings side-effect to `getTargetSeries`.
-        // Prepare axis proxies.
-        eachAxisModel(function (axisDim, axisIndex, axisModel, dataZoomModel) {
-            // dispose all last axis proxy, in case that some axis are deleted.
-            axisModel.__dzAxisProxy = null;
-        });
         const proxyList: AxisProxy[] = [];
         eachAxisModel(function (axisDim, axisIndex, axisModel, dataZoomModel) {
             // Different dataZooms may control the same axis. In that case,
             // an axisProxy serves both of them.
-            if (!axisModel.__dzAxisProxy) {
+            if (!getAxisProxyFromModel(axisModel)) {
                 // Use the first dataZoomModel as the main model of axisProxy.
-                axisModel.__dzAxisProxy = new AxisProxy(axisDim, axisIndex, 
dataZoomModel, ecModel);
-                proxyList.push(axisModel.__dzAxisProxy);
+                const axisProxy = new AxisProxy(axisDim, axisIndex, 
dataZoomModel, ecModel);
+                proxyList.push(axisProxy);
+                setAxisProxyToModel(axisModel, axisProxy);
             }
         });
 
@@ -135,4 +134,4 @@ const dataZoomProcessor: StageHandler = {
     }
 };
 
-export default dataZoomProcessor;
\ No newline at end of file
+export default dataZoomProcessor;
diff --git a/src/component/dataZoom/helper.ts b/src/component/dataZoom/helper.ts
index e2009cb76..d3170ea2b 100644
--- a/src/component/dataZoom/helper.ts
+++ b/src/component/dataZoom/helper.ts
@@ -25,6 +25,8 @@ import SeriesModel from '../../model/Series';
 import { CoordinateSystemHostModel } from '../../coord/CoordinateSystem';
 import { AxisBaseModel } from '../../coord/AxisBaseModel';
 import type AxisProxy from './AxisProxy';
+import { getCachePerECPrepare, GlobalModelCachePerECPrepare, makeInner } from 
'../../util/model';
+import type ComponentModel from '../../model/Component';
 
 
 export interface DataZoomPayloadBatchItem {
@@ -37,17 +39,13 @@ export interface DataZoomPayloadBatchItem {
 
 export interface DataZoomReferCoordSysInfo {
     model: CoordinateSystemHostModel;
-    // Notice: if two dataZooms refer the same coordinamte system model,
-    // (1) The axis they refered may different
+    // Notice: if two dataZooms refer the same coordinate system model,
+    // (1) The axis they referred may different
     // (2) The sequence the axisModels matters, may different in
     // different dataZooms.
     axisModels: AxisBaseModel[];
 }
 
-export type DataZoomExtendedAxisBaseModel = AxisBaseModel & {
-    __dzAxisProxy: AxisProxy
-};
-
 export const DATA_ZOOM_AXIS_DIMENSIONS = [
     'x', 'y', 'radius', 'angle', 'single'
 ] as const;
@@ -61,6 +59,12 @@ type DataZoomAxisIdPropName =
     'xAxisId' | 'yAxisId' | 'radiusAxisId' | 'angleAxisId' | 'singleAxisId';
 export type DataZoomCoordSysMainType = 'polar' | 'grid' | 'singleAxis';
 
+const ecModelCacheInner = makeInner<{
+    axisProxyMap: AxisProxyMap;
+}, GlobalModelCachePerECPrepare>();
+
+type AxisProxyMap = HashMap<AxisProxy, ComponentModel['uid']>;
+
 // Supported coords.
 // FIXME: polar has been broken (but rarely used).
 const SERIES_COORDS = ['cartesian2d', 'polar', 'singleAxis'] as const;
@@ -211,8 +215,28 @@ export function 
collectReferCoordSysModelInfo(dataZoomModel: DataZoomModel): {
     return coordSysInfoWrap;
 }
 
-export function getAxisProxyFromModel(axisModel: AxisBaseModel): AxisProxy | 
NullUndefined {
-    return axisModel && (axisModel as 
DataZoomExtendedAxisBaseModel).__dzAxisProxy;
+function ensureAxisProxyMap(ecModel: GlobalModel): AxisProxyMap {
+    // Consider some axes may be deleted, and dataZoom options may be changed 
at and only at each run of
+    // "ec prepare", we save axis proxies to a cache that is auto-cleared for 
each run of "ec prepare".
+    const store = ecModelCacheInner(getCachePerECPrepare(ecModel));
+    return store.axisProxyMap || (store.axisProxyMap = createHashMap());
+}
+
+export function getAxisProxyFromModel(axisModel: AxisBaseModel | 
NullUndefined): AxisProxy | NullUndefined {
+    if (!axisModel) {
+        return;
+    }
+    if (__DEV__) {
+        assert(axisModel.ecModel);
+    }
+    return ensureAxisProxyMap(axisModel.ecModel).get(axisModel.uid);
+}
+
+export function setAxisProxyToModel(axisModel: AxisBaseModel, axisProxy: 
AxisProxy): void {
+    if (__DEV__) {
+        assert(axisModel.ecModel);
+    }
+    ensureAxisProxyMap(axisModel.ecModel).set(axisModel.uid, axisProxy);
 }
 
 /**
@@ -221,7 +245,7 @@ export function getAxisProxyFromModel(axisModel: 
AxisBaseModel): AxisProxy | Nul
  * then do not input it into `AxisProxy#reset`.
  */
 export function getAlignTo(dataZoomModel: DataZoomModel, axisProxy: 
AxisProxy): AxisProxy | NullUndefined {
-    const alignToAxis = axisProxy.getAxisModel().axis.alignTo;
+    const alignToAxis = axisProxy.getAxisModel().axis.__alignTo;
     return (
         alignToAxis && dataZoomModel.getAxisProxy(
             alignToAxis.dim as DataZoomAxisDimension,
diff --git a/src/coord/Axis.ts b/src/coord/Axis.ts
index af3721ee1..d36018834 100644
--- a/src/coord/Axis.ts
+++ b/src/coord/Axis.ts
@@ -82,10 +82,8 @@ class Axis {
     // `inverse` can be inferred by `extent` unless `extent[0] === extent[1]`.
     inverse: AxisBaseOption['inverse'] = false;
 
-    // Injected outside
-    alignTo: Axis;
-    // Injected outside.
-    suggestNotOnZeroOfMe: boolean;
+    // To be injected outside. May change - do not use it outside of echarts.
+    __alignTo: Axis;
 
 
     constructor(dim: DimensionName, scale: Scale, extent: [number, number]) {
diff --git a/src/coord/AxisBaseModel.ts b/src/coord/AxisBaseModel.ts
index 9b482f0cb..1909edb25 100644
--- a/src/coord/AxisBaseModel.ts
+++ b/src/coord/AxisBaseModel.ts
@@ -31,5 +31,5 @@ export interface AxisBaseModel<T extends AxisBaseOptionCommon 
= AxisBaseOptionCo
     AxisModelCommonMixin<T>,
     AxisModelExtendedInCreator {
 
-    axis: Axis
+    axis: Axis;
 }
\ No newline at end of file
diff --git a/src/coord/axisHelper.ts b/src/coord/axisHelper.ts
index c7e23a375..36fbb39ab 100644
--- a/src/coord/axisHelper.ts
+++ b/src/coord/axisHelper.ts
@@ -50,11 +50,21 @@ import {
     extentDiffers, isLogScale, isOrdinalScale
 } from '../scale/helper';
 import { AxisModelExtendedInCreator } from './axisModelCreator';
-import { initExtentForUnion } from '../util/model';
+import { initExtentForUnion, makeInner } from '../util/model';
 import { ComponentModel } from '../echarts.simple';
 import { SCALE_EXTENT_KIND_EFFECTIVE, SCALE_MAPPER_DEPTH_OUT_OF_BREAK } from 
'../scale/scaleMapper';
 
 
+const axisInner = makeInner<{
+    noOnMyZero: SuppressOnAxisZeroReason;
+}, Axis>();
+
+type SuppressOnAxisZeroReason = {
+    dz?: boolean;
+    base?: boolean
+};
+
+
 export function determineAxisType(
     model: Model<Pick<AxisBaseOption, 'type'>>
 ): OptionAxisType {
@@ -137,6 +147,21 @@ export function ifAxisCrossZero(axis: Axis) {
     return !((min > 0 && max > 0) || (min < 0 && max < 0));
 }
 
+export function suppressOnAxisZero(axis: Axis, reason: 
Partial<SuppressOnAxisZeroReason>): void {
+    zrUtil.defaults(axisInner(axis).noOnMyZero || (axisInner(axis).noOnMyZero 
= {}), reason);
+}
+
+/**
+ * `true`: Prevent orthoganal axes from positioning at the zero point of this 
axis.
+ */
+export function isOnAxisZeroSuppressed(axis: Axis): boolean {
+    const dontOnAxisZero = axisInner(axis).noOnMyZero;
+    // Empirically, onZero causes weired effect when dataZoom is used on an 
"base axis". Consider
+    // bar series as an example. And also consider when 
`SCALE_EXTENT_KIND_MAPPING` is used, where
+    // the axis line is likely to cross the series shapes unexpectedly.
+    return dontOnAxisZero && dontOnAxisZero.dz && dontOnAxisZero.base;
+}
+
 /**
  * @param axis
  * @return Label formatter function.
diff --git a/src/coord/cartesian/Cartesian2D.ts 
b/src/coord/cartesian/Cartesian2D.ts
index d50379275..fc56cc2b5 100644
--- a/src/coord/cartesian/Cartesian2D.ts
+++ b/src/coord/cartesian/Cartesian2D.ts
@@ -88,9 +88,11 @@ class Cartesian2D extends Cartesian<Axis2D> implements 
CoordinateSystem {
     }
 
     /**
-     * Base axis will be used on stacking.
+     * Base axis will be used on stacking and series such as 'bar', 
'pictorialBar', etc.
      */
     getBaseAxis(): Axis2D {
+        // PENGING: Should we allow bar series to specify a base axis when
+        // both axes are type "value", rather than force to xAxis?
         return this.getAxesByScale('ordinal')[0]
             || this.getAxesByScale('time')[0]
             || this.getAxis('x');
diff --git a/src/coord/cartesian/Grid.ts b/src/coord/cartesian/Grid.ts
index f3e698d19..1c18aae1a 100644
--- a/src/coord/cartesian/Grid.ts
+++ b/src/coord/cartesian/Grid.ts
@@ -32,6 +32,8 @@ import {
     shouldAxisShow,
     retrieveAxisBreaksOption,
     determineAxisType,
+    suppressOnAxisZero,
+    isOnAxisZeroSuppressed,
 } from '../../coord/axisHelper';
 import Cartesian2D, {cartesian2DDimensions} from './Cartesian2D';
 import Axis2D from './Axis2D';
@@ -74,7 +76,6 @@ import {
     AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE,
     scaleRawExtentInfoEnableBoxCoordSysUsage, scaleRawExtentInfoReallyCreate, 
scaleRawExtentInfoRequireCreate
 } from '../scaleRawExtentInfo';
-import { SCALE_EXTENT_KIND_MAPPING } from '../../scale/scaleMapper';
 import { hasBreaks } from '../../scale/break';
 
 
@@ -144,7 +145,7 @@ class Grid implements CoordinateSystemMaster {
 
             for (let i = axesIndices.length - 1; i >= 0; i--) { // Reverse 
order
                 const axis = axes[+axesIndices[i]];
-                if (axis.alignTo) {
+                if (axis.__alignTo) {
                     axisNeedsAlign.push(axis);
                 }
                 else {
@@ -152,13 +153,13 @@ class Grid implements CoordinateSystemMaster {
                 }
             };
             each(axisNeedsAlign, axis => {
-                if (incapableOfAlignNeedFallback(axis, axis.alignTo as 
Axis2D)) {
+                if (incapableOfAlignNeedFallback(axis, axis.__alignTo as 
Axis2D)) {
                     scaleCalcNice(axis);
                 }
                 else {
                     scaleCalcAlign(
                         axis,
-                        axis.alignTo.scale as IntervalScale | LogScale
+                        axis.__alignTo.scale as IntervalScale | LogScale
                     );
                 }
             });
@@ -445,6 +446,8 @@ class Grid implements CoordinateSystemMaster {
 
                 cartesian.addAxis(xAxis);
                 cartesian.addAxis(yAxis);
+
+                suppressOnAxisZero(cartesian.getBaseAxis(), {base: true});
             });
         });
 
@@ -666,17 +669,18 @@ function fixAxisOnZero(
     }
 }
 
+/**
+ * CAVEAT: Must not be called before `CoordinateSystem#update` due to 
`__dontOnMyZero`.
+ */
 function canOnZeroToAxis(
     onZeroOption: AxisBaseOptionCommon['axisLine']['onZero'],
     axis: Axis2D
 ): boolean {
+    // PENDING: Historical behavior: `onZero` on 'category' and 'time' axis 
are always disabled
+    // even if ec option gives `onZero: true`.
     let can = axis && axis.type !== 'category' && axis.type !== 'time' && 
ifAxisCrossZero(axis);
-    if (can && onZeroOption === 'auto') {
-        if (axis.scale.getExtentUnsafe(SCALE_EXTENT_KIND_MAPPING, null)) {
-            // Empirically, onZero is inappropriate when 
`SCALE_EXTENT_KIND_MAPPING` is
-            // used - the axis line is likely to cross the series shapes 
unexpectedly.
-            can = false;
-        }
+    if (can && onZeroOption === 'auto' && isOnAxisZeroSuppressed(axis)) {
+        can = false;
     }
     // falsy value of `onZeroOption` has been handled in the previous logic.
     return can;
@@ -722,7 +726,7 @@ function prepareAlignToInCoordSysCreate(axes: 
Record<number, Axis2D>): void {
     }
     if (alignTo) {
         each(axisNeedsAlign, function (axis) {
-            axis.alignTo = alignTo;
+            axis.__alignTo = alignTo;
         });
     }
 }
diff --git a/src/core/echarts.ts b/src/core/echarts.ts
index c199e60cb..18efa5c25 100644
--- a/src/core/echarts.ts
+++ b/src/core/echarts.ts
@@ -1577,6 +1577,8 @@ class ECharts extends Eventful<ECEventDefinition> {
     private static internalField = (function () {
 
         prepare = function (ecIns: ECharts): void {
+            modelUtil.resetCachePerECPrepare(ecIns._model);
+
             const scheduler = ecIns._scheduler;
 
             scheduler.restorePipelines(ecIns._model);
@@ -1802,7 +1804,7 @@ class ECharts extends Eventful<ECEventDefinition> {
                     return;
                 }
 
-                modelUtil.resetCachePerECUpdate(ecModel);
+                modelUtil.resetCachePerECFullUpdate(ecModel);
                 ecModel.setUpdatePayload(payload);
 
                 scheduler.restoreData(ecModel, payload);
diff --git a/src/layout/barGrid.ts b/src/layout/barGrid.ts
index e16641fb1..dcba7aa30 100644
--- a/src/layout/barGrid.ts
+++ b/src/layout/barGrid.ts
@@ -28,7 +28,7 @@ import { StageHandler, NullUndefined } from '../util/types';
 import { createFloat32Array } from '../util/vendor';
 import {
     extentHasValue,
-    getCachePerECUpdate, GlobalModelCachePerECUpdate, initExtentForUnion,
+    getCachePerECFullUpdate, GlobalModelCachePerECFullUpdate, 
initExtentForUnion,
     isValidNumberForExtent, makeCallOnlyOnce, makeInner,
     unionExtentFromNumber,
 } from '../util/model';
@@ -48,7 +48,7 @@ import type Scale from '../scale/Scale';
 
 const ecModelCacheInner = makeInner<{
     layoutPre: BarGridLayoutPre;
-}, GlobalModelCachePerECUpdate>();
+}, GlobalModelCachePerECFullUpdate>();
 
 // Record of layout preparation by series sub type.
 type BarGridLayoutPre = Partial<Record<BaseBarSeriesSubType, 
BarGridLayoutPreOnSeriesType>>;
@@ -176,7 +176,7 @@ export function getLayoutOnAxis(opt: BarGridLayoutOption): 
BarGridLayoutResultFo
 function ensureLayoutPre(
     ecModel: GlobalModel, seriesType: BaseBarSeriesSubType
 ): BarGridLayoutPreOnSeriesType {
-    const ecCache = ecModelCacheInner(getCachePerECUpdate(ecModel));
+    const ecCache = ecModelCacheInner(getCachePerECFullUpdate(ecModel));
     const layoutPre = ecCache.layoutPre || (ecCache.layoutPre = {});
     return layoutPre[seriesType] || (layoutPre[seriesType] = {
         axes: [], axisMap: {}, seriesReady: false
diff --git a/src/util/model.ts b/src/util/model.ts
index b63cf293f..67ef0282d 100644
--- a/src/util/model.ts
+++ b/src/util/model.ts
@@ -1269,22 +1269,47 @@ let onceUniqueIndex = getRandomIdBase();
 
 
 const ecModelCacheInner = makeInner<{
-    perECUpdate: GlobalModelCachePerECUpdate;
+    fullUpdate: GlobalModelCachePerECFullUpdate;
+    prepare: GlobalModelCachePerECPrepare;
 }, GlobalModel>();
 
+export type GlobalModelCachePerECPrepare = {__: 'prepare'}; // Nominal to 
distinguish.
+export type GlobalModelCachePerECFullUpdate = {__: 'fullUpdate'}; // Nominal 
to distinguish.
+
 /**
- * Reset on each time `updateMethods.update` (i.e., full update) is called.
- * It is mainly used for cache.
+ * CAVEAT: Can only be called by `echarts.ts`
  */
-export type GlobalModelCachePerECUpdate = {};
+export function resetCachePerECPrepare(ecModel: GlobalModel): void {
+    ecModelCacheInner(ecModel).prepare = {} as GlobalModelCachePerECPrepare;
+}
 
 /**
  * CAVEAT: Can only be called by `echarts.ts`
  */
-export function resetCachePerECUpdate(ecModel: GlobalModel): void {
-    ecModelCacheInner(ecModel).perECUpdate = {};
+export function resetCachePerECFullUpdate(ecModel: GlobalModel): void {
+    ecModelCacheInner(ecModel).fullUpdate = {} as 
GlobalModelCachePerECFullUpdate;
 }
 
-export function getCachePerECUpdate(ecModel: GlobalModel): 
GlobalModelCachePerECUpdate {
-    return ecModelCacheInner(ecModel).perECUpdate;
+/**
+ * The cache is auto cleared at the begining of a run of "ec prepare".
+ *
+ * NOTICE:
+ *  - It can be only called at "ec prepare" stage, such as,
+ *      - Do not call it in processor `getTargetSeries` methods.
+ *      - Do not call it in component/series model 
`init`/`mergeOption`/`optionUpdated`/`getData` methods.
+ *  - "ec prepare" is not necessarily called before each "ec full update".
+ */
+export function getCachePerECPrepare(ecModel: GlobalModel): 
GlobalModelCachePerECPrepare {
+    return ecModelCacheInner(ecModel).prepare;
+}
+
+/**
+ * The cache is auto cleared at the begining of a run of "ec full update".
+ *
+ * NOTICE:
+ *  - Do not call it at "ec prepare" stage. See `getCachePerECPrepare` for 
details.
+ *  - All shortcuts (such as `updateView`/`updateLayout`/etc.) do not clear it.
+ */
+export function getCachePerECFullUpdate(ecModel: GlobalModel): 
GlobalModelCachePerECFullUpdate {
+    return ecModelCacheInner(ecModel).fullUpdate;
 }
diff --git a/src/util/number.ts b/src/util/number.ts
index e5fa52256..84a802dd1 100644
--- a/src/util/number.ts
+++ b/src/util/number.ts
@@ -37,6 +37,8 @@ const RADIAN_EPSILON = 1e-4;
 const TO_FIXED_SUPPORTED_PRECISION_MAX = 20;
 
 // For rounding error like `2.9999999999999996`, with respect to IEEE754 64bit 
float.
+// NOTICE: It only works when the expected result is a rational number with low
+// precision. See method `round` for details.
 export const DEFAULT_PRECISION_FOR_ROUNDING_ERROR = 14;
 
 function _trim(str: string): string {
diff --git a/test/bar-overflow-plot2.html b/test/bar-overflow-plot2.html
index 869a364ec..cec30e22f 100644
--- a/test/bar-overflow-plot2.html
+++ b/test/bar-overflow-plot2.html
@@ -42,10 +42,12 @@ under the License.
                     // Historically, true by default.
                     yAxisOnZero: 'unspecified',
                     useDataZoom: true,
+                    xAxisShowMinMaxLabel: undefined,
+                    dataZoomLabelPrecision: undefined,
                 };
 
                 function createData() {
-                    var MIN_X = 0;
+                    var MIN_X = -0.0025;
                     var MAX_X = 0.0035;
                     var actualMinX = Infinity;
                     var data0 = [];
@@ -57,23 +59,22 @@ under the License.
                     var dataSet = {bar_a: [], bar_b: [], 
bar_partially_negative: []};
                     var minXSet = {bar_a: Infinity, bar_b: Infinity, 
bar_partially_negative: Infinity};
 
-                    for (var i = MIN_X; i <= MAX_X; i += 0.0001) {
+                    for (var i = MIN_X; i <= MAX_X; i = +(i + 
0.0001).toFixed(10)) {
                         lastDelta += (Math.random() - 0.5) * 5;
                         last += lastDelta;
-                        addItem(
-                            'bar_a', i, last
-                        );
-                        addItem(
-                            'bar_b', i, Math.max(90, last - Math.random() * 
700)
-                        );
-                        addItem(
-                            'bar_partially_negative', i - Math.round((MAX_X - 
MIN_X) / 2), Math.max(70, last + Math.random() * 600)
-                        );
+                        if (i >= 0) {
+                            addItem('bar_a', i, last);
+                            addItem('bar_b', i, Math.max(90, last - 
Math.random() * 700));
+                        }
+                        if (i < 0.0030) {
+                            addItem('bar_partially_negative', i, Math.max(70, 
last + Math.random() * 600));
+                        }
                     }
                     function addItem(prop, x, y) {
                         dataSet[prop].push([x, y]);
                         minXSet[prop] = Math.min(minXSet[prop], x);
                     }
+                    console.log(dataSet, minXSet)
 
                     return {dataSet, minXSet};
                 }
@@ -104,8 +105,12 @@ under the License.
                         dataZoom: _ctx.useDataZoom
                             ? [{
                                 type: 'slider',
+                                labelPrecision: _ctx.dataZoomLabelPrecision,
+                                valuePrecision: 4,
                             }, {
                                 type: 'inside',
+                                labelPrecision: _ctx.dataZoomLabelPrecision,
+                                valuePrecision: 4,
                             }]
                             : undefined,
                         xAxis: {
@@ -113,6 +118,10 @@ under the License.
                             splitLine: {
                                 show: false
                             },
+                            axisLabel: {
+                                showMinLabel: _ctx.xAxisShowMinMaxLabel,
+                                showMaxLabel: _ctx.xAxisShowMinMaxLabel,
+                            }
                         },
                         yAxis: {
                             type: 'value',
@@ -153,6 +162,7 @@ under the License.
 
                 var myChart = testHelper.create(echarts, 'main_onZero', {
                     title: [
+                        'Bars must not overflow the xAxis.',
                         'Check yAxis onZero to xAxis (xAxis is value axis).',
                         `series data min value is 
**${testHelper.printObject(_data.minXSet)}**`,
                     ],
@@ -174,6 +184,24 @@ under the License.
                             _ctx.useDataZoom = this.value;
                             updateChart();
                         }
+                    }, {
+                        type: 'select',
+                        text: 'xAxisShowMinMaxLabel:',
+                        values: [_ctx.xAxisShowMinMaxLabel, true, false],
+                        onchange() {
+                            _ctx.xAxisShowMinMaxLabel = this.value;
+                            updateChart();
+                        }
+                    }, {
+                        type: 'br',
+                    }, {
+                        type: 'select',
+                        text: 'dataZoomLabelPrecision:',
+                        values: [_ctx.dataZoomLabelPrecision, 0, 2, 4, 8],
+                        onchange() {
+                            _ctx.dataZoomLabelPrecision = this.value;
+                            updateChart();
+                        }
                     }]
                 });
             })


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to