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]
