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 d2cc085b26b130bfcacc1123debb80672c964db1 Author: 100pah <[email protected]> AuthorDate: Tue Mar 10 02:00:01 2026 +0800 tweak: Clarity the previous implements of axis statistics. --- src/chart/boxplot/boxplotLayout.ts | 24 +- src/chart/candlestick/candlestickLayout.ts | 11 +- src/chart/helper/axisSnippets.ts | 43 ++-- src/chart/scatter/jitterLayout.ts | 7 +- src/component/dataZoom/AxisProxy.ts | 4 +- src/component/singleAxis/install.ts | 4 +- src/coord/axisStatistics.ts | 344 ++++++++++++++++++++--------- src/coord/cartesian/Grid.ts | 13 +- src/coord/cartesian/GridModel.ts | 2 +- src/coord/parallel/Parallel.ts | 8 +- src/coord/parallel/ParallelModel.ts | 7 +- src/coord/parallel/parallelCreator.ts | 13 +- src/coord/polar/PolarModel.ts | 3 +- src/coord/polar/polarCreator.ts | 19 +- src/coord/radar/Radar.ts | 21 +- src/coord/radar/RadarModel.ts | 10 +- src/coord/scaleRawExtentInfo.ts | 106 ++++----- src/coord/single/AxisModel.ts | 6 +- src/coord/single/Single.ts | 8 +- src/coord/single/singleCreator.ts | 12 +- src/core/echarts.ts | 10 +- src/layout/barCommon.ts | 23 +- src/layout/barGrid.ts | 29 ++- src/layout/barPolar.ts | 28 ++- src/model/Global.ts | 3 + src/scale/scaleMapper.ts | 4 +- src/util/jitter.ts | 6 +- 27 files changed, 453 insertions(+), 315 deletions(-) diff --git a/src/chart/boxplot/boxplotLayout.ts b/src/chart/boxplot/boxplotLayout.ts index feda1efd5..283c18979 100644 --- a/src/chart/boxplot/boxplotLayout.ts +++ b/src/chart/boxplot/boxplotLayout.ts @@ -22,7 +22,7 @@ import {parsePercent} from '../../util/number'; import type GlobalModel from '../../model/Global'; import BoxplotSeriesModel, { SERIES_TYPE_BOXPLOT } from './BoxplotSeries'; import { - eachCollectedAxis, eachCollectedSeries, getCollectedSeriesLength, + countSeriesOnAxisOnKey, eachAxisOnKey, eachSeriesOnAxisOnKey, requireAxisStatistics } from '../../coord/axisStatistics'; import { makeCallOnlyOnce } from '../../util/model'; @@ -31,8 +31,9 @@ import Axis from '../../coord/Axis'; import { registerAxisContainShapeHandler } from '../../coord/scaleRawExtentInfo'; import { calcBandWidth } from '../../coord/axisBand'; import { - makeAxisStatKey, - createSimpleAxisStatClient, createBandWidthBasedAxisContainShapeHandler + createBandWidthBasedAxisContainShapeHandler, + getMetricsNonOrdinalLinearPositiveMinGap, + makeAxisStatKey } from '../helper/axisSnippets'; @@ -45,13 +46,13 @@ export interface BoxplotItemLayout { export function boxplotLayout(ecModel: GlobalModel) { const axisStatKey = makeAxisStatKey(SERIES_TYPE_BOXPLOT); - eachCollectedAxis(ecModel, axisStatKey, function (axis) { - const seriesCount = getCollectedSeriesLength(axis, axisStatKey); + eachAxisOnKey(ecModel, axisStatKey, function (axis) { + const seriesCount = countSeriesOnAxisOnKey(axis, axisStatKey); if (!seriesCount) { return; } const baseResult = calculateBase(axis, seriesCount); - eachCollectedSeries(axis, axisStatKey, function (seriesModel: BoxplotSeriesModel, idx) { + eachSeriesOnAxisOnKey(axis, axisStatKey, function (seriesModel: BoxplotSeriesModel, idx) { layoutSingleSeries( seriesModel, baseResult.boxOffsetList[idx], @@ -77,7 +78,7 @@ function calculateBase(baseAxis: Axis, seriesCount: number): { {fromStat: {key: makeAxisStatKey(SERIES_TYPE_BOXPLOT)}, min: 1}, ).w; - eachCollectedSeries(baseAxis, makeAxisStatKey(SERIES_TYPE_BOXPLOT), function (seriesModel: BoxplotSeriesModel) { + eachSeriesOnAxisOnKey(baseAxis, makeAxisStatKey(SERIES_TYPE_BOXPLOT), function (seriesModel: BoxplotSeriesModel) { let boxWidthBound = seriesModel.get('boxWidth'); if (!isArray(boxWidthBound)) { boxWidthBound = [boxWidthBound, boxWidthBound]; @@ -93,7 +94,7 @@ function calculateBase(baseAxis: Axis, seriesCount: number): { const boxWidth = (availableWidth - boxGap * (seriesCount - 1)) / seriesCount; let base = boxWidth / 2 - availableWidth / 2; - eachCollectedSeries(baseAxis, makeAxisStatKey(SERIES_TYPE_BOXPLOT), function (seriesModel, idx) { + eachSeriesOnAxisOnKey(baseAxis, makeAxisStatKey(SERIES_TYPE_BOXPLOT), function (seriesModel, idx) { boxOffsetList.push(base); base += boxGap + boxWidth; @@ -189,8 +190,11 @@ export function registerBoxplotAxisHandlers(registers: EChartsExtensionInstallRe const axisStatKey = makeAxisStatKey(SERIES_TYPE_BOXPLOT); requireAxisStatistics( registers, - axisStatKey, - createSimpleAxisStatClient(SERIES_TYPE_BOXPLOT) + { + key: axisStatKey, + seriesType: SERIES_TYPE_BOXPLOT, + getMetrics: getMetricsNonOrdinalLinearPositiveMinGap, + } ); registerAxisContainShapeHandler( axisStatKey, diff --git a/src/chart/candlestick/candlestickLayout.ts b/src/chart/candlestick/candlestickLayout.ts index 5abd593ab..67fd4e710 100644 --- a/src/chart/candlestick/candlestickLayout.ts +++ b/src/chart/candlestick/candlestickLayout.ts @@ -34,7 +34,9 @@ import { import { EChartsExtensionInstallRegisters } from '../../extension'; import { registerAxisContainShapeHandler } from '../../coord/scaleRawExtentInfo'; import { - makeAxisStatKey, createSimpleAxisStatClient, createBandWidthBasedAxisContainShapeHandler + createBandWidthBasedAxisContainShapeHandler, + getMetricsNonOrdinalLinearPositiveMinGap, + makeAxisStatKey } from '../helper/axisSnippets'; import { calcBandWidth } from '../../coord/axisBand'; @@ -280,8 +282,11 @@ export function registerCandlestickAxisHandlers(registers: EChartsExtensionInsta const axisStatKey = makeAxisStatKey(SERIES_TYPE_CANDLESTICK); requireAxisStatistics( registers, - axisStatKey, - createSimpleAxisStatClient(SERIES_TYPE_CANDLESTICK) + { + key: axisStatKey, + seriesType: SERIES_TYPE_CANDLESTICK, + getMetrics: getMetricsNonOrdinalLinearPositiveMinGap + } ); registerAxisContainShapeHandler( axisStatKey, diff --git a/src/chart/helper/axisSnippets.ts b/src/chart/helper/axisSnippets.ts index d7b3ac142..66bcdf53b 100644 --- a/src/chart/helper/axisSnippets.ts +++ b/src/chart/helper/axisSnippets.ts @@ -17,33 +17,16 @@ * under the License. */ +import type Axis from '../../coord/Axis'; import { calcBandWidth } from '../../coord/axisBand'; -import { AxisStatisticsClient, AxisStatKey } from '../../coord/axisStatistics'; +import { AXIS_STAT_KEY_DELIMITER, AxisStatKey, AxisStatMetrics } from '../../coord/axisStatistics'; +import { CoordinateSystem } from '../../coord/CoordinateSystem'; import { AxisContainShapeHandler } from '../../coord/scaleRawExtentInfo'; import { isOrdinalScale } from '../../scale/helper'; import { isNullableNumberFinite } from '../../util/number'; import { ComponentSubType } from '../../util/types'; -export const getMetricsMinGapOnNonCategoryAxis: AxisStatisticsClient['getMetrics'] = function (axis) { - return { - liPosMinGap: !isOrdinalScale(axis.scale) - }; -}; - -export function createSimpleAxisStatClient( - seriesType: ComponentSubType, -): AxisStatisticsClient { - return { - collectAxisSeries(ecModel, saveAxisSeries) { - ecModel.eachSeriesByType(seriesType, function (seriesModel) { - saveAxisSeries(seriesModel.getBaseAxis(), seriesModel); - }); - }, - getMetrics: getMetricsMinGapOnNonCategoryAxis - }; -} - /** * Require `requireAxisStatistics`. */ @@ -57,6 +40,24 @@ export function createBandWidthBasedAxisContainShapeHandler(axisStatKey: AxisSta }; } + +/** + * A pre-built `makeAxisStatKey`. + * See `makeAxisStatKey2`. Use two functions rather than a optional parameter to impose checking. + */ export function makeAxisStatKey(seriesType: ComponentSubType): AxisStatKey { - return seriesType as AxisStatKey; + return (seriesType + AXIS_STAT_KEY_DELIMITER) as AxisStatKey; +} +export function makeAxisStatKey2(seriesType: ComponentSubType, coordSysType: CoordinateSystem['type']): AxisStatKey { + return (seriesType + AXIS_STAT_KEY_DELIMITER + coordSysType) as AxisStatKey; } + +/** + * A pre-built `getMetrics`. + */ +export function getMetricsNonOrdinalLinearPositiveMinGap(axis: Axis): AxisStatMetrics { + return { + // non-category scale do not use `liPosMinGap` to calculate `bandWidth`. + liPosMinGap: !isOrdinalScale(axis.scale) + }; +}; diff --git a/src/chart/scatter/jitterLayout.ts b/src/chart/scatter/jitterLayout.ts index ad2c2e9ba..1da8ce195 100644 --- a/src/chart/scatter/jitterLayout.ts +++ b/src/chart/scatter/jitterLayout.ts @@ -23,6 +23,8 @@ import type SingleAxis from '../../coord/single/SingleAxis'; import type Axis2D from '../../coord/cartesian/Axis2D'; import type { StageHandler } from '../../util/types'; import createRenderPlanner from '../helper/createRenderPlanner'; +import { COORD_SYS_TYPE_CARTESIAN_2D } from '../../coord/cartesian/GridModel'; +import { COORD_SYS_TYPE_SINGLE_AXIS } from '../../coord/single/AxisModel'; export default function jitterLayout(): StageHandler { return { @@ -32,7 +34,10 @@ export default function jitterLayout(): StageHandler { reset(seriesModel: ScatterSeriesModel) { const coordSys = seriesModel.coordinateSystem; - if (!coordSys || (coordSys.type !== 'cartesian2d' && coordSys.type !== 'single')) { + if (!coordSys || ( + coordSys.type !== COORD_SYS_TYPE_CARTESIAN_2D + && coordSys.type !== COORD_SYS_TYPE_SINGLE_AXIS + )) { return; } const baseAxis = coordSys.getBaseAxis && coordSys.getBaseAxis() as Axis2D | SingleAxis; diff --git a/src/component/dataZoom/AxisProxy.ts b/src/component/dataZoom/AxisProxy.ts index 5b7d22a89..f84880d1c 100644 --- a/src/component/dataZoom/AxisProxy.ts +++ b/src/component/dataZoom/AxisProxy.ts @@ -33,7 +33,7 @@ import { getAxisMainType, isCoordSupported, DataZoomAxisDimension } from './help import { SINGLE_REFERRING } from '../../util/model'; import { isOrdinalScale, isTimeScale } from '../../scale/helper'; import { - AXIS_EXTENT_INFO_BUILD_FROM_DATA_ZOOM, scaleRawExtentInfoReallyCreate, + AXIS_EXTENT_INFO_BUILD_FROM_DATA_ZOOM, scaleRawExtentInfoCreate, ScaleRawExtentResultForZoom, } from '../../coord/scaleRawExtentInfo'; import { discourageOnAxisZero } from '../../coord/axisHelper'; @@ -351,7 +351,7 @@ class AxisProxy { // Nevertheless, user can set min/max/scale on axes to make extent of axes // consistent. const axis = this.getAxisModel().axis; - scaleRawExtentInfoReallyCreate(this.ecModel, axis, AXIS_EXTENT_INFO_BUILD_FROM_DATA_ZOOM); + scaleRawExtentInfoCreate(this.ecModel, axis, AXIS_EXTENT_INFO_BUILD_FROM_DATA_ZOOM); discourageOnAxisZero(axis, {dz: true}); diff --git a/src/component/singleAxis/install.ts b/src/component/singleAxis/install.ts index b2e6b8dc8..c949a1e53 100644 --- a/src/component/singleAxis/install.ts +++ b/src/component/singleAxis/install.ts @@ -21,7 +21,7 @@ import { EChartsExtensionInstallRegisters, use } from '../../extension'; import ComponentView from '../../view/Component'; import SingleAxisView from '../axis/SingleAxisView'; import axisModelCreator from '../../coord/axisModelCreator'; -import SingleAxisModel from '../../coord/single/AxisModel'; +import SingleAxisModel, { COORD_SYS_TYPE_SINGLE_AXIS } from '../../coord/single/AxisModel'; import singleCreator from '../../coord/single/singleCreator'; import {install as installAxisPointer} from '../axisPointer/install'; import AxisView from '../axis/AxisView'; @@ -43,7 +43,7 @@ export function install(registers: EChartsExtensionInstallRegisters) { registers.registerComponentView(SingleAxisView); registers.registerComponentModel(SingleAxisModel); - axisModelCreator(registers, 'single', SingleAxisModel, SingleAxisModel.defaultOption); + axisModelCreator(registers, COORD_SYS_TYPE_SINGLE_AXIS, SingleAxisModel, SingleAxisModel.defaultOption); registers.registerCoordinateSystem('single', singleCreator); } \ No newline at end of file diff --git a/src/coord/axisStatistics.ts b/src/coord/axisStatistics.ts index ca2454619..77f864b8c 100644 --- a/src/coord/axisStatistics.ts +++ b/src/coord/axisStatistics.ts @@ -17,13 +17,13 @@ * under the License. */ -import { assert, createHashMap, each, HashMap } from 'zrender/src/core/util'; +import { assert, createHashMap, HashMap, retrieve2 } from 'zrender/src/core/util'; import type GlobalModel from '../model/Global'; import type SeriesModel from '../model/Series'; import { initExtentForUnion, makeCallOnlyOnce, makeInner, } from '../util/model'; -import { DimensionIndex, NullUndefined } from '../util/types'; +import { ComponentSubType, DimensionIndex, NullUndefined } from '../util/types'; import type Axis from './Axis'; import { asc, isNullableNumberFinite } from '../util/number'; import { parseSanitizationFilter, passesSanitizationFilter } from '../data/helper/dataValueHelper'; @@ -36,24 +36,41 @@ import { getCachePerECFullUpdate, getCachePerECPrepare, GlobalModelCachePerECFullUpdate, GlobalModelCachePerECPrepare } from '../util/cycleCache'; +import { CoordinateSystem } from './CoordinateSystem'; const callOnlyOnce = makeCallOnlyOnce(); +// Ensure that it never appears in internal generated uid and pre-defined coordSysType. +export const AXIS_STAT_KEY_DELIMITER = '|&'; const ecModelCacheFullUpdateInner = makeInner<{ - all: AxisStatAll; + + // It stores all <axis, series> pairs, aggregated by axis, based on which axis scale extent is calculated. + // NOTICE: series that has been filtered out are included. + // It is unrelated to `AxisStatKeyedClient`. + axSer: HashMap<SeriesModel[], AxisBaseModel['uid']>; + + // AxisStatKey based statistics records. + // Only `AxisStatKeyedClient` concerned <axis, series> pairs are collected, based on which + // statistics are calculated. + keyed: AxisStatKeyed; + // `keys` is only used to quick travel. keys: AxisStatKeys; - axisMapForCheck?: HashMap<1, ComponentModel['uid']>; // Only used in dev mode - seriesMapForCheck?: HashMap<1, ComponentModel['uid']>; // Only used in dev mode + + // Only used in dev mode for duplication checking. + axSerPairCheck?: HashMap<1, string>; + }, GlobalModelCachePerECFullUpdate>(); type AxisStatKeys = HashMap<AxisStatKey[], ComponentModel['uid']>; -type AxisStatAll = HashMap<AxisStatPerKey | NullUndefined, AxisStatKey>; +type AxisStatKeyed = HashMap<AxisStatPerKey | NullUndefined, AxisStatKey>; type AxisStatPerKey = HashMap<AxisStatPerKeyPerAxis | NullUndefined, AxisBaseModel['uid']>; type AxisStatPerKeyPerAxis = { axis: Axis; // This is series use this axis as base axis and need to be laid out. // The order is determined by the client and must be respected. + // Never be null/undefined. + // series filtered out is included. sers: SeriesModel[]; // For query. The array index is series index. serByIdx: SeriesModel[]; @@ -64,16 +81,14 @@ type AxisStatPerKeyPerAxis = { liPosMinGap?: number | NullUndefined; // metrics corresponds to this record. - metrics?: AxisStatisticsMetrics; - // ecPrepareCache corresponds to this record. - ecPrepare?: AxisStatECPrepareCachePerKeyPerAxis; + metrics?: AxisStatMetrics; }; const ecModelCachePrepareInner = makeInner<{ - all: AxisStatECPrepareCacheAll | NullUndefined; + keyed: AxisStatECPrepareCacheKeyed | NullUndefined; }, GlobalModelCachePerECPrepare>(); -type AxisStatECPrepareCacheAll = HashMap<AxisStatECPrepareCachePerKey | NullUndefined, AxisStatKey>; +type AxisStatECPrepareCacheKeyed = HashMap<AxisStatECPrepareCachePerKey | NullUndefined, AxisStatKey>; type AxisStatECPrepareCachePerKey = HashMap<AxisStatECPrepareCachePerKeyPerAxis | NullUndefined, AxisBaseModel['uid']>; type AxisStatECPrepareCachePerKeyPerAxis = Pick<AxisStatPerKeyPerAxis, 'liPosMinGap'> & { @@ -81,17 +96,20 @@ type AxisStatECPrepareCachePerKeyPerAxis = serUids?: HashMap<1, ComponentModel['uid']> }; -export type AxisStatisticsClient = { - /** - * NOTICE: It is called after series filtering. - */ - collectAxisSeries: ( - ecModel: GlobalModel, - saveAxisSeries: (axis: Axis | NullUndefined, series: SeriesModel) => void - ) => void; - getMetrics: ( - axis: Axis, - ) => AxisStatisticsMetrics; +export type AxisStatKeyedClient = { + + // A key for retrieving result. + key: AxisStatKey; + + // Only the specific `seriesType` is covered. + seriesType: ComponentSubType; + // `true` by default - the <axis, series> pair is collected only if series's base axis is that axis. + baseAxis?: boolean | NullUndefined; + // `NullUndefined` by default - all coordinate systems are covered. + coordSysType?: CoordinateSystem['type'] | NullUndefined; + + // `NullUndefined` return indicates this axis should be omitted. + getMetrics: (axis: Axis) => AxisStatMetrics | NullUndefined; }; /** @@ -99,9 +117,9 @@ export type AxisStatisticsClient = { * designated by `AxisStatKey`. In most case `seriesType` is used as `AxisStatKey`. */ export type AxisStatKey = string & {_: 'AxisStatKey'}; // Nominal to avoid misusing. +type ClientQueryKey = string & {_: 'ClientQueryKey'}; // Nominal to avoid misusing; internal usage. -type AxisStatisticsMetrics = { - // Currently only one metric is required. +export type AxisStatMetrics = { // NOTICE: // May be time-consuming in large data due to some metrics requiring travel and sort of @@ -115,6 +133,8 @@ export type AxisStatisticsResult = Pick< 'liPosMinGap' >; +type AxisStatEachSeriesCb = (seriesModel: SeriesModel, travelIdx: number) => void; + let validateInputAxis: ((axis: Axis) => void) | NullUndefined; if (__DEV__) { validateInputAxis = function (axis) { @@ -127,8 +147,8 @@ function getAxisStatPerKeyPerAxis( axisStatKey: AxisStatKey ): AxisStatPerKeyPerAxis | NullUndefined { const axisModel = axis.model; - const all = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(axisModel.ecModel)).all; - const perKey = all && all.get(axisStatKey); + const keyed = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(axisModel.ecModel)).keyed; + const perKey = keyed && keyed.get(axisStatKey); return perKey && perKey.get(axisModel.uid); } @@ -153,7 +173,7 @@ export function getAxisStatBySeries( validateInputAxis(axis); } const result: AxisStatisticsResult[] = []; - eachPerKeyPerAxis(axis.model.ecModel, function (perKeyPerAxis) { + eachKeyEachAxis(axis.model.ecModel, function (perKeyPerAxis) { for (let idx = 0; idx < seriesList.length; idx++) { if (seriesList[idx] && perKeyPerAxis.serByIdx[seriesList[idx].seriesIndex]) { result.push(wrapStatResult(perKeyPerAxis)); @@ -163,7 +183,7 @@ export function getAxisStatBySeries( return result; } -function eachPerKeyPerAxis( +function eachKeyEachAxis( ecModel: GlobalModel, cb: ( perKeyPerAxis: AxisStatPerKeyPerAxis, @@ -171,8 +191,8 @@ function eachPerKeyPerAxis( axisModelUid: AxisBaseModel['uid'] ) => void ): void { - const all = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel)).all; - all && all.each(function (perKey, axisStatKey) { + const keyed = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel)).keyed; + keyed && keyed.each(function (perKey, axisStatKey) { perKey.each(function (perKeyPerAxis, axisModelUid) { cb(perKeyPerAxis, axisStatKey, axisModelUid); }); @@ -185,19 +205,57 @@ function wrapStatResult(record: AxisStatPerKeyPerAxis | NullUndefined): AxisStat }; } -export function eachCollectedSeries( +/** + * NOTE: + * - series declaration order is respected. + * - series filtered out are excluded. + */ +export function eachSeriesOnAxis( + axis: Axis, + cb: AxisStatEachSeriesCb +): void { + if (__DEV__) { + validateInputAxis(axis); + } + const ecModel = axis.model.ecModel; + const seriesOnAxisMap = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel)).axSer; + seriesOnAxisMap && seriesOnAxisMap.each(function (seriesList) { + eachSeriesDeal(ecModel, seriesList, cb); + }); +} + +export function eachSeriesOnAxisOnKey( axis: Axis, axisStatKey: AxisStatKey, cb: (series: SeriesModel, idx: number) => void ): void { if (__DEV__) { + assert(axisStatKey != null); validateInputAxis(axis); } const perKeyPerAxis = getAxisStatPerKeyPerAxis(axis, axisStatKey); - perKeyPerAxis && each(perKeyPerAxis.sers, cb); + perKeyPerAxis && eachSeriesDeal(axis.model.ecModel, perKeyPerAxis.sers, cb); +} + +function eachSeriesDeal( + ecModel: GlobalModel, + seriesList: SeriesModel[], + cb: AxisStatEachSeriesCb +): void { + for (let i = 0; i < seriesList.length; i++) { + const seriesModel = seriesList[i]; + // Legend-filtered series need to be ignored since series are registered before `legendFilter`. + if (!ecModel.isSeriesFiltered(seriesModel)) { + cb(seriesModel, i); + } + } } -export function getCollectedSeriesLength( +/** + * NOTE: + * - series filtered out are excluded. + */ +export function countSeriesOnAxisOnKey( axis: Axis, axisStatKey: AxisStatKey, ): number { @@ -206,10 +264,17 @@ export function getCollectedSeriesLength( validateInputAxis(axis); } const perKeyPerAxis = getAxisStatPerKeyPerAxis(axis, axisStatKey); - return perKeyPerAxis ? perKeyPerAxis.sers.length : 0; + if (!perKeyPerAxis || !perKeyPerAxis.sers.length) { + return 0; + } + let count = 0; + eachSeriesDeal(axis.model.ecModel, perKeyPerAxis.sers, function () { + count++; + }); + return count; } -export function eachCollectedAxis( +export function eachAxisOnKey( ecModel: GlobalModel, axisStatKey: AxisStatKey, cb: (axis: Axis) => void @@ -217,14 +282,17 @@ export function eachCollectedAxis( if (__DEV__) { assert(axisStatKey != null); } - const all = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel)).all; - const perKey = all && all.get(axisStatKey); + const keyed = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel)).keyed; + const perKey = keyed && keyed.get(axisStatKey); perKey && perKey.each(function (perKeyPerAxis) { cb(perKeyPerAxis.axis); }); } -export function eachAxisStatKey( +/** + * NOTICE: Available after `CoordinateSystem['create']` (not included). + */ +export function eachKeyOnAxis( axis: Axis, cb: (axisStatKey: AxisStatKey) => void ): void { @@ -239,75 +307,33 @@ export function eachAxisStatKey( }); } +/** + * NOTICE: this processor may be omitted - it is registered only if required. + */ function performAxisStatisticsOnOverallReset(ecModel: GlobalModel): void { - const ecFullUpdateCache = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel)); - const axisStatAll: AxisStatAll = ecFullUpdateCache.all = createHashMap(); - const ecPrepareCache = ecModelCachePrepareInner(getCachePerECPrepare(ecModel)); - const ecPrepareCacheAll = ecPrepareCache.all || (ecPrepareCache.all = createHashMap()); - - axisStatisticsClients.each(function (client, axisStatKey) { - client.collectAxisSeries(ecModel, function saveAxisSeries(axis, series) { - if (!axis) { - return; - } + const ecPrepareCacheKeyed = ecPrepareCache.keyed || (ecPrepareCache.keyed = createHashMap()); - if (__DEV__) { - validateInputAxis(axis); - // - An axis can be associated with multiple `axisStatKey`s. For example, if `axisStatKey`s are - // "candlestick" and "bar", they can be associated with the same "xAxis". - // - Within an individual axis, it is a typically incorrect usage if a <series-axis> pair is - // associated with multiple `perKeyPerAxis`, which may cause repeated calculation and - // performance degradation, had hard to be found without the checking below. For example, If - // `axisStatKey` are "grid-bar" (see `barGrid.ts`) and "polar-bar" (see `barPolar.ts`), and - // a <xAxis-series> pair is wrongly associated with both "polar-bar" and "grid-bar", the - // relevant statistics will be computed twice. - const axisMapForCheck = ecFullUpdateCache.axisMapForCheck - || (ecFullUpdateCache.axisMapForCheck = createHashMap()); - const seriesMapForCheck = ecFullUpdateCache.seriesMapForCheck - || (ecFullUpdateCache.seriesMapForCheck = createHashMap()); - assert(!axisMapForCheck.get(axis.model.uid) || !seriesMapForCheck.get(series.uid)); - axisMapForCheck.set(axis.model.uid, 1); - seriesMapForCheck.set(series.uid, 1); - } - - const perKey = axisStatAll.get(axisStatKey) || axisStatAll.set(axisStatKey, createHashMap()); - - const axisModelUid = axis.model.uid; - let perKeyPerAxis = perKey.get(axisModelUid); - if (!perKeyPerAxis) { - perKeyPerAxis = perKey.set(axisModelUid, {axis, sers: [], serByIdx: []}); - perKeyPerAxis.metrics = client.getMetrics(axis) || {}; + eachKeyEachAxis(ecModel, function (perKeyPerAxis, axisStatKey, axisModelUid) { + const ecPrepareCachePerKey = ecPrepareCacheKeyed.get(axisStatKey) + || ecPrepareCacheKeyed.set(axisStatKey, createHashMap()); + const ecPreparePerKeyPerAxis = ecPrepareCachePerKey.get(axisModelUid) + || ecPrepareCachePerKey.set(axisModelUid, {}); - const ecPrepareCachePerKey = ecPrepareCacheAll.get(axisStatKey) - || ecPrepareCacheAll.set(axisStatKey, createHashMap()); - perKeyPerAxis.ecPrepare = ecPrepareCachePerKey.get(axisModelUid) - || ecPrepareCachePerKey.set(axisModelUid, {}); - } - // NOTICE: series order should respect to the input order, since it - // matters in some cases (see `axisSnippets.ts` for more details). - perKeyPerAxis.sers.push(series); - perKeyPerAxis.serByIdx[series.seriesIndex] = series; - }); - }); - - const axisStatKeys: AxisStatKeys = ecFullUpdateCache.keys = createHashMap(); - eachPerKeyPerAxis(ecModel, function (perKeyPerAxis, axisStatKey, axisModelUid) { - (axisStatKeys.get(axisModelUid) || axisStatKeys.set(axisModelUid, [])) - .push(axisStatKey); - performStatisticsForRecord(perKeyPerAxis); + performStatisticsForRecord(ecModel, perKeyPerAxis, ecPreparePerKeyPerAxis); }); } function performStatisticsForRecord( + ecModel: GlobalModel, perKeyPerAxis: AxisStatPerKeyPerAxis, + ecPreparePerKeyPerAxis: AxisStatECPrepareCachePerKeyPerAxis ): void { if (!perKeyPerAxis.metrics.liPosMinGap) { return; } const newSerUids: AxisStatECPrepareCachePerKeyPerAxis['serUids'] = createHashMap(); - const ecPreparePerKeyPerAxis = perKeyPerAxis.ecPrepare; const ecPrepareSerUids = ecPreparePerKeyPerAxis.serUids; const ecPrepareLiPosMinGap = ecPreparePerKeyPerAxis.liPosMinGap; let ecPrepareCacheMiss: boolean; @@ -325,26 +351,25 @@ function performStatisticsForRecord( // timeAll[0] = Date.now(); // _EC_PERF_ function eachSeries( - cb: (dimStoreIdx: DimensionIndex, seriesModel: SeriesModel, store: DataStore) => void + cb: (dimStoreIdx: DimensionIndex, seriesModel: SeriesModel, rawDataStore: DataStore) => void ) { - for (let i = 0; i < perKeyPerAxis.sers.length; i++) { - const seriesModel = perKeyPerAxis.sers[i]; - const data = seriesModel.getData(); + eachSeriesDeal(ecModel, perKeyPerAxis.sers, function (seriesModel) { + const rawData = seriesModel.getRawData(); // NOTE: Currently there is no series that a "base axis" can map to multiple dimensions. - const dimStoreIdx = data.getDimensionIndex(data.mapDimension(axis.dim)); + const dimStoreIdx = rawData.getDimensionIndex(rawData.mapDimension(axis.dim)); if (dimStoreIdx >= 0) { - cb(dimStoreIdx, seriesModel, data.getStore()); + cb(dimStoreIdx, seriesModel, rawData.getStore()); } - } + }); } let bufferCapacity = 0; - eachSeries(function (dimStoreIdx, seriesModel, store) { + eachSeries(function (dimStoreIdx, seriesModel, rawDataStore) { newSerUids.set(seriesModel.uid, 1); if (!ecPrepareSerUids || !ecPrepareSerUids.hasKey(seriesModel.uid)) { ecPrepareCacheMiss = true; } - bufferCapacity += store.count(); + bufferCapacity += rawDataStore.count(); }); if (!ecPrepareSerUids || ecPrepareSerUids.keys().length !== newSerUids.keys().length) { @@ -435,6 +460,101 @@ const tmpValueBuffer = tryEnsureTypedArray( 50 // arbitrary. May be expanded if needed. ); +/** + * NOTICE: + * - It must be called in `CoordinateSystem['create']`, before series filtering. + * - It must be called in `seriesIndex` ascending order (series declaration order). + * i.e., iterated by `ecModel.eachSeries`. + * - Every <axis, series> pair can only call this method once. + * + * @see scaleRawExtentInfoCreate in `scaleRawExtentInfo.ts` + */ +export function associateSeriesWithAxis( + axis: Axis | NullUndefined, + seriesModel: SeriesModel, + coordSysType: CoordinateSystem['type'] +): void { + if (!axis) { + return; + } + + const ecModel = seriesModel.ecModel; + const ecFullUpdateCache = ecModelCacheFullUpdateInner(getCachePerECFullUpdate(ecModel)); + const axisModelUid = axis.model.uid; + + if (__DEV__) { + validateInputAxis(axis); + // - An axis can be associated with multiple `axisStatKey`s. For example, if `axisStatKey`s are + // "candlestick" and "bar", they can be associated with the same "xAxis". + // - Within an individual axis, it is a typically incorrect usage if a <axis, series> pair is + // associated with multiple `perKeyPerAxis`, which may cause repeated calculation and + // performance degradation, had hard to be found without the checking below. For example, If + // `axisStatKey` are "grid-bar" (see `barGrid.ts`) and "polar-bar" (see `barPolar.ts`), and + // a <xAxis-series> pair is wrongly associated with both "polar-bar" and "grid-bar", the + // relevant statistics will be computed twice. + const axSerPairCheck = ecFullUpdateCache.axSerPairCheck + || (ecFullUpdateCache.axSerPairCheck = createHashMap()); + const pairKey = `${axisModelUid}${AXIS_STAT_KEY_DELIMITER}${seriesModel.uid}`; + assert(!axSerPairCheck.get(pairKey)); + axSerPairCheck.set(pairKey, 1); + } + + const seriesOnAxisMap = ecFullUpdateCache.axSer || (ecFullUpdateCache.axSer = createHashMap()); + const seriesListPerAxis = seriesOnAxisMap.get(axisModelUid) || (seriesOnAxisMap.set(axisModelUid, [])); + if (__DEV__) { + const lastSeries = seriesListPerAxis[seriesListPerAxis.length - 1]; + if (lastSeries) { + // Series order should respect to the input order, since it matters in some cases + // (e.g., see `barGrid.ts` and `barPolar.ts` - ec option declaration order matters). + assert(lastSeries.seriesIndex < seriesModel.seriesIndex); + } + } + seriesListPerAxis.push(seriesModel); + + const seriesType = seriesModel.subType; + const isBaseAxis = seriesModel.getBaseAxis() === axis; + + const client = clientsByQueryKey.get(makeClientQueryKey(seriesType, isBaseAxis, coordSysType)) + || clientsByQueryKey.get(makeClientQueryKey(seriesType, isBaseAxis, null)); + if (!client) { + return; + } + + const keyed: AxisStatKeyed = ecFullUpdateCache.keyed || (ecFullUpdateCache.keyed = createHashMap()); + const keys: AxisStatKeys = ecFullUpdateCache.keys || (ecFullUpdateCache.keys = createHashMap()); + + const axisStatKey = client.key; + const perKey = keyed.get(axisStatKey) || keyed.set(axisStatKey, createHashMap()); + let perKeyPerAxis = perKey.get(axisModelUid); + if (!perKeyPerAxis) { + perKeyPerAxis = perKey.set(axisModelUid, {axis, sers: [], serByIdx: []}); + // They should only be executed for each <key, axis> pair once: + perKeyPerAxis.metrics = client.getMetrics(axis); + (keys.get(axisModelUid) || keys.set(axisModelUid, [])) + .push(axisStatKey); + } + + // series order should respect to the input order. + perKeyPerAxis.sers.push(seriesModel); + perKeyPerAxis.serByIdx[seriesModel.seriesIndex] = seriesModel; +} + +/** + * NOTE: Currently, the scenario is simple enough to look up clients by hash map. + * Otherwise, a caller-provided `filter` may be an alternative if more complex requirements arise. + */ +function makeClientQueryKey( + seriesType: ComponentSubType, + isBaseAxis: boolean | NullUndefined, + coordSysType: CoordinateSystem['type'] | NullUndefined +): ClientQueryKey { + return ( + seriesType + + AXIS_STAT_KEY_DELIMITER + retrieve2(isBaseAxis, true) + + AXIS_STAT_KEY_DELIMITER + (coordSysType || '') + ) as ClientQueryKey; +} + /** * NOTICE: Can only be called in "install" stage. * @@ -442,20 +562,32 @@ const tmpValueBuffer = tryEnsureTypedArray( */ export function requireAxisStatistics( registers: EChartsExtensionInstallRegisters, - axisStatKey: AxisStatKey, - client: AxisStatisticsClient + client: AxisStatKeyedClient ): void { + const queryKey = makeClientQueryKey(client.seriesType, client.baseAxis, client.coordSysType); + if (__DEV__) { - assert(!axisStatisticsClients.get(axisStatKey)); + assert(client.seriesType + && client.key + && !clientsCheckStatKey.get(client.key) + && !clientsByQueryKey.get(queryKey) + ); // More checking is performed in `axSerPairCheck`. + clientsCheckStatKey.set(client.key, 1); } - axisStatisticsClients.set(axisStatKey, client); + clientsByQueryKey.set(queryKey, client); callOnlyOnce(registers, function () { registers.registerProcessor(registers.PRIORITY.PROCESSOR.AXIS_STATISTICS, { + // Theoretically, `appendData` requires to re-calculate them. + dirtyOnOverallProgress: true, overallReset: performAxisStatisticsOnOverallReset }); }); } -const axisStatisticsClients: HashMap<AxisStatisticsClient, AxisStatKey> = createHashMap(); +let clientsCheckStatKey: HashMap<1, AxisStatKey>; +if (__DEV__) { + clientsCheckStatKey = createHashMap(); +} +const clientsByQueryKey: HashMap<AxisStatKeyedClient, ClientQueryKey> = createHashMap(); diff --git a/src/coord/cartesian/Grid.ts b/src/coord/cartesian/Grid.ts index 1a2a2c131..915e9f041 100644 --- a/src/coord/cartesian/Grid.ts +++ b/src/coord/cartesian/Grid.ts @@ -43,7 +43,7 @@ import Axis2D from './Axis2D'; import {ParsedModelFinder, ParsedModelFinderKnown, SINGLE_REFERRING} from '../../util/model'; // Depends on GridModel, AxisModel, which performs preprocess. -import GridModel, { GridOption, OUTER_BOUNDS_CLAMP_DEFAULT, OUTER_BOUNDS_DEFAULT } from './GridModel'; +import GridModel, { COORD_SYS_TYPE_CARTESIAN_2D, GridOption, OUTER_BOUNDS_CLAMP_DEFAULT, OUTER_BOUNDS_DEFAULT } from './GridModel'; import CartesianAxisModel from './AxisModel'; import GlobalModel from '../../model/Global'; import ExtensionAPI from '../../core/ExtensionAPI'; @@ -77,9 +77,10 @@ import { createDimNameMap } from '../../data/helper/SeriesDataSchema'; import type Axis from '../Axis'; import { AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE, - scaleRawExtentInfoEnableBoxCoordSysUsage, scaleRawExtentInfoReallyCreate, scaleRawExtentInfoRequireCreate + scaleRawExtentInfoEnableBoxCoordSysUsage, scaleRawExtentInfoCreate } from '../scaleRawExtentInfo'; import { hasBreaks } from '../../scale/break'; +import { associateSeriesWithAxis } from '../axisStatistics'; type Cartesian2DDimensionName = 'x' | 'y'; @@ -134,7 +135,7 @@ class Grid implements CoordinateSystemMaster { const axesMap = this._axesMap; each(this._axesList, function (axis) { - scaleRawExtentInfoReallyCreate(ecModel, axis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); + scaleRawExtentInfoCreate(ecModel, axis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); const scale = axis.scale; if (isOrdinalScale(scale)) { scale.setSortInfo(axis.model.get('categorySortInfo')); @@ -559,7 +560,7 @@ class Grid implements CoordinateSystemMaster { injectCoordSysByOption({ targetModel: seriesModel, - coordSysType: 'cartesian2d', + coordSysType: COORD_SYS_TYPE_CARTESIAN_2D, coordSysProvider: coordSysProvider }); @@ -594,8 +595,8 @@ class Grid implements CoordinateSystemMaster { ); } if (xAxis && yAxis) { - scaleRawExtentInfoRequireCreate(xAxis, seriesModel); - scaleRawExtentInfoRequireCreate(yAxis, seriesModel); + associateSeriesWithAxis(xAxis, seriesModel, COORD_SYS_TYPE_CARTESIAN_2D); + associateSeriesWithAxis(yAxis, seriesModel, COORD_SYS_TYPE_CARTESIAN_2D); } }, this); diff --git a/src/coord/cartesian/GridModel.ts b/src/coord/cartesian/GridModel.ts index edef4d51a..654919df5 100644 --- a/src/coord/cartesian/GridModel.ts +++ b/src/coord/cartesian/GridModel.ts @@ -23,7 +23,7 @@ import { ComponentOption, BoxLayoutOptionMixin, ZRColor, ShadowOptionMixin, NullUndefined, ComponentOnCalendarOptionMixin, ComponentOnMatrixOptionMixin } from '../../util/types'; -import Grid from './Grid'; +import type Grid from './Grid'; import { CoordinateSystemHostModel } from '../CoordinateSystem'; import type GlobalModel from '../../model/Global'; import { getLayoutParams, mergeLayoutParam } from '../../util/layout'; diff --git a/src/coord/parallel/Parallel.ts b/src/coord/parallel/Parallel.ts index 53852a770..be3c3e71e 100644 --- a/src/coord/parallel/Parallel.ts +++ b/src/coord/parallel/Parallel.ts @@ -31,7 +31,7 @@ import ParallelAxis from './ParallelAxis'; import * as graphic from '../../util/graphic'; import {mathCeil, mathFloor, mathMax, mathMin, mathPI, round} from '../../util/number'; import sliderMove from '../../component/helper/sliderMove'; -import ParallelModel, { ParallelLayoutDirection } from './ParallelModel'; +import ParallelModel, { COORD_SYS_TYPE_PARALLEL, ParallelLayoutDirection } from './ParallelModel'; import GlobalModel from '../../model/Global'; import ExtensionAPI from '../../core/ExtensionAPI'; import { Dictionary, DimensionName, ScaleDataValue } from '../../util/types'; @@ -42,7 +42,7 @@ import { AxisBaseModel } from '../AxisBaseModel'; import { CategoryAxisBaseOption } from '../axisCommonTypes'; import { scaleCalcNice } from '../axisNiceTicks'; import { - AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE, scaleRawExtentInfoReallyCreate + AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE, scaleRawExtentInfoCreate } from '../scaleRawExtentInfo'; @@ -77,7 +77,7 @@ type SlidedAxisExpandBehavior = 'none' | 'slide' | 'jump'; class Parallel implements CoordinateSystemMaster, CoordinateSystem { - readonly type = 'parallel'; + readonly type = COORD_SYS_TYPE_PARALLEL; /** * key: dimension @@ -149,7 +149,7 @@ class Parallel implements CoordinateSystemMaster, CoordinateSystem { update(ecModel: GlobalModel, api: ExtensionAPI): void { each(this.dimensions, function (dim) { const axis = this._axesMap.get(dim); - scaleRawExtentInfoReallyCreate(ecModel, axis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); + scaleRawExtentInfoCreate(ecModel, axis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); scaleCalcNice(axis); }, this); } diff --git a/src/coord/parallel/ParallelModel.ts b/src/coord/parallel/ParallelModel.ts index 4ca648bb3..080e9d9fb 100644 --- a/src/coord/parallel/ParallelModel.ts +++ b/src/coord/parallel/ParallelModel.ts @@ -20,7 +20,7 @@ import * as zrUtil from 'zrender/src/core/util'; import ComponentModel from '../../model/Component'; -import Parallel from './Parallel'; +import type Parallel from './Parallel'; import { DimensionName, ComponentOption, BoxLayoutOptionMixin, ComponentOnCalendarOptionMixin, ComponentOnMatrixOptionMixin @@ -31,6 +31,9 @@ import ParallelSeriesModel from '../../chart/parallel/ParallelSeries'; import SeriesModel from '../../model/Series'; +export const COORD_SYS_TYPE_PARALLEL = 'parallel'; +export const COMPONENT_TYPE_PARALLEL = COORD_SYS_TYPE_PARALLEL; + export type ParallelLayoutDirection = 'horizontal' | 'vertical'; export interface ParallelCoordinateSystemOption extends @@ -65,7 +68,7 @@ export interface ParallelCoordinateSystemOption extends class ParallelModel extends ComponentModel<ParallelCoordinateSystemOption> { - static type = 'parallel'; + static type = COMPONENT_TYPE_PARALLEL; readonly type = ParallelModel.type; static dependencies = ['parallelAxis']; diff --git a/src/coord/parallel/parallelCreator.ts b/src/coord/parallel/parallelCreator.ts index 1f6ff7392..a9eaac5e8 100644 --- a/src/coord/parallel/parallelCreator.ts +++ b/src/coord/parallel/parallelCreator.ts @@ -25,17 +25,18 @@ import Parallel from './Parallel'; import GlobalModel from '../../model/Global'; import ExtensionAPI from '../../core/ExtensionAPI'; -import ParallelModel from './ParallelModel'; +import ParallelModel, { COMPONENT_TYPE_PARALLEL, COORD_SYS_TYPE_PARALLEL } from './ParallelModel'; import { CoordinateSystemMaster } from '../CoordinateSystem'; import ParallelSeriesModel from '../../chart/parallel/ParallelSeries'; import { SINGLE_REFERRING } from '../../util/model'; import { each } from 'zrender/src/core/util'; -import {scaleRawExtentInfoRequireCreate} from '../scaleRawExtentInfo'; +import { associateSeriesWithAxis } from '../axisStatistics'; + function createParallelCoordSys(ecModel: GlobalModel, api: ExtensionAPI): CoordinateSystemMaster[] { const coordSysList: CoordinateSystemMaster[] = []; - ecModel.eachComponent('parallel', function (parallelModel: ParallelModel, idx: number) { + ecModel.eachComponent(COMPONENT_TYPE_PARALLEL, function (parallelModel: ParallelModel, idx: number) { const coordSys = new Parallel(parallelModel, ecModel, api); coordSys.name = 'parallel_' + idx; @@ -49,14 +50,14 @@ function createParallelCoordSys(ecModel: GlobalModel, api: ExtensionAPI): Coordi // Inject the coordinateSystems into seriesModel ecModel.eachSeries(function (seriesModel) { - if ((seriesModel as ParallelSeriesModel).get('coordinateSystem') === 'parallel') { + if ((seriesModel as ParallelSeriesModel).get('coordinateSystem') === COORD_SYS_TYPE_PARALLEL) { const parallelModel = seriesModel.getReferringComponents( - 'parallel', SINGLE_REFERRING + COMPONENT_TYPE_PARALLEL, SINGLE_REFERRING ).models[0] as ParallelModel; const parallel = seriesModel.coordinateSystem = parallelModel.coordinateSystem; if (parallel) { each(parallel.dimensions, function (dim) { - scaleRawExtentInfoRequireCreate(parallel.getAxis(dim), seriesModel); + associateSeriesWithAxis(parallel.getAxis(dim), seriesModel, COORD_SYS_TYPE_PARALLEL); }); } } diff --git a/src/coord/polar/PolarModel.ts b/src/coord/polar/PolarModel.ts index da158d377..96a6fb174 100644 --- a/src/coord/polar/PolarModel.ts +++ b/src/coord/polar/PolarModel.ts @@ -22,7 +22,7 @@ import { ComponentOnMatrixOptionMixin } from '../../util/types'; import ComponentModel from '../../model/Component'; -import Polar from './Polar'; +import type Polar from './Polar'; import { AngleAxisModel, RadiusAxisModel } from './AxisModel'; export interface PolarOption extends @@ -33,6 +33,7 @@ export interface PolarOption extends } export const COORD_SYS_TYPE_POLAR = 'polar'; +export const COMPONENT_TYPE_POLAR = COORD_SYS_TYPE_POLAR; class PolarModel extends ComponentModel<PolarOption> { static type = COORD_SYS_TYPE_POLAR; diff --git a/src/coord/polar/polarCreator.ts b/src/coord/polar/polarCreator.ts index 152d08506..f1f3a45e2 100644 --- a/src/coord/polar/polarCreator.ts +++ b/src/coord/polar/polarCreator.ts @@ -27,7 +27,7 @@ import { determineAxisType, } from '../../coord/axisHelper'; -import PolarModel from './PolarModel'; +import PolarModel, { COMPONENT_TYPE_POLAR, COORD_SYS_TYPE_POLAR } from './PolarModel'; import ExtensionAPI from '../../core/ExtensionAPI'; import GlobalModel from '../../model/Global'; import OrdinalScale from '../../scale/Ordinal'; @@ -42,8 +42,9 @@ import { CategoryAxisBaseOption } from '../axisCommonTypes'; import { createBoxLayoutReference } from '../../util/layout'; import { scaleCalcNice } from '../axisNiceTicks'; import { - AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE, scaleRawExtentInfoReallyCreate, scaleRawExtentInfoRequireCreate + AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE, scaleRawExtentInfoCreate } from '../scaleRawExtentInfo'; +import { associateSeriesWithAxis } from '../axisStatistics'; /** * Resize method bound to the polar @@ -85,8 +86,8 @@ function updatePolarScale(this: Polar, ecModel: GlobalModel, api: ExtensionAPI) const angleAxis = polar.getAngleAxis(); const radiusAxis = polar.getRadiusAxis(); - scaleRawExtentInfoReallyCreate(ecModel, angleAxis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); - scaleRawExtentInfoReallyCreate(ecModel, radiusAxis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); + scaleRawExtentInfoCreate(ecModel, angleAxis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); + scaleRawExtentInfoCreate(ecModel, radiusAxis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); scaleCalcNice(angleAxis); scaleCalcNice(radiusAxis); @@ -131,7 +132,7 @@ const polarCreator = { create: function (ecModel: GlobalModel, api: ExtensionAPI) { const polarList: Polar[] = []; - ecModel.eachComponent('polar', function (polarModel: PolarModel, idx: number) { + ecModel.eachComponent(COMPONENT_TYPE_POLAR, function (polarModel: PolarModel, idx: number) { const polar = new Polar(idx + ''); // Inject resize and update method polar.update = updatePolarScale; @@ -157,9 +158,9 @@ const polarCreator = { polarIndex?: number polarId?: string }>) { - if (seriesModel.get('coordinateSystem') === 'polar') { + if (seriesModel.get('coordinateSystem') === COORD_SYS_TYPE_POLAR) { const polarModel = seriesModel.getReferringComponents( - 'polar', SINGLE_REFERRING + COMPONENT_TYPE_POLAR, SINGLE_REFERRING ).models[0] as PolarModel; if (__DEV__) { @@ -175,8 +176,8 @@ const polarCreator = { } const polar = seriesModel.coordinateSystem = polarModel.coordinateSystem; if (polar) { - scaleRawExtentInfoRequireCreate(polar.getRadiusAxis(), seriesModel); - scaleRawExtentInfoRequireCreate(polar.getAngleAxis(), seriesModel); + associateSeriesWithAxis(polar.getRadiusAxis(), seriesModel, COORD_SYS_TYPE_POLAR); + associateSeriesWithAxis(polar.getAngleAxis(), seriesModel, COORD_SYS_TYPE_POLAR); } } }); diff --git a/src/coord/radar/Radar.ts b/src/coord/radar/Radar.ts index 242b5d0b0..0710772b3 100644 --- a/src/coord/radar/Radar.ts +++ b/src/coord/radar/Radar.ts @@ -23,7 +23,9 @@ import IndicatorAxis from './IndicatorAxis'; import IntervalScale from '../../scale/Interval'; import * as numberUtil from '../../util/number'; import { CoordinateSystemMaster, CoordinateSystem } from '../CoordinateSystem'; -import RadarModel from './RadarModel'; +import RadarModel, { + COMPONENT_TYPE_RADAR, COORD_SYS_TYPE_RADAR, RADAR_DEFAULT_SPLIT_NUMBER, SERIES_TYPE_RADAR +} from './RadarModel'; import GlobalModel from '../../model/Global'; import ExtensionAPI from '../../core/ExtensionAPI'; import { ScaleDataValue } from '../../util/types'; @@ -32,16 +34,15 @@ import { map, each, isString, isNumber } from 'zrender/src/core/util'; import { scaleCalcAlign } from '../axisAlignTicks'; import { createBoxLayoutReference } from '../../util/layout'; import { - AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE, scaleRawExtentInfoReallyCreate, scaleRawExtentInfoRequireCreate + AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE, scaleRawExtentInfoCreate } from '../scaleRawExtentInfo'; import { ensureValidSplitNumber } from '../../scale/helper'; +import { associateSeriesWithAxis } from '../axisStatistics'; -export const RADAR_DEFAULT_SPLIT_NUMBER = 5; - class Radar implements CoordinateSystem, CoordinateSystemMaster { - readonly type: 'radar'; + readonly type = COORD_SYS_TYPE_RADAR; /** * * Radar dimensions @@ -168,7 +169,7 @@ class Radar implements CoordinateSystem, CoordinateSystemMaster { dummyScale.setConfig({interval: 1}); // Force all the axis fixing the maxSplitNumber. each(indicatorAxes, function (indicatorAxis) { - scaleRawExtentInfoReallyCreate(ecModel, indicatorAxis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); + scaleRawExtentInfoCreate(ecModel, indicatorAxis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); scaleCalcAlign(indicatorAxis, dummyScale); }); } @@ -192,19 +193,19 @@ class Radar implements CoordinateSystem, CoordinateSystemMaster { static create(ecModel: GlobalModel, api: ExtensionAPI) { const radarList: Radar[] = []; - ecModel.eachComponent('radar', function (radarModel: RadarModel) { + ecModel.eachComponent(COMPONENT_TYPE_RADAR, function (radarModel: RadarModel) { const radar = new Radar(radarModel, ecModel, api); radarList.push(radar); radarModel.coordinateSystem = radar; }); - ecModel.eachSeriesByType('radar', function (radarSeries) { - if (radarSeries.get('coordinateSystem') === 'radar') { + ecModel.eachSeriesByType(SERIES_TYPE_RADAR, function (radarSeries) { + if (radarSeries.get('coordinateSystem') === COORD_SYS_TYPE_RADAR) { // Inject coordinate system // @ts-ignore const radar = radarSeries.coordinateSystem = radarList[radarSeries.get('radarIndex') || 0]; if (radar) { each(radar.getIndicatorAxes(), function (indicatorAxis) { - scaleRawExtentInfoRequireCreate(indicatorAxis, radarSeries); + associateSeriesWithAxis(indicatorAxis, radarSeries, COORD_SYS_TYPE_RADAR); }); } } diff --git a/src/coord/radar/RadarModel.ts b/src/coord/radar/RadarModel.ts index 7fb1fbea0..de7254f4e 100644 --- a/src/coord/radar/RadarModel.ts +++ b/src/coord/radar/RadarModel.ts @@ -32,13 +32,19 @@ import { } from '../../util/types'; import { AxisBaseOption, CategoryAxisBaseOption, ValueAxisBaseOption } from '../axisCommonTypes'; import { AxisBaseModel } from '../AxisBaseModel'; -import Radar, { RADAR_DEFAULT_SPLIT_NUMBER } from './Radar'; +import type Radar from './Radar'; import {CoordinateSystemHostModel} from '../../coord/CoordinateSystem'; import tokens from '../../visual/tokens'; import { getUID } from '../../util/component'; const valueAxisDefault = axisDefault.value; +export const COORD_SYS_TYPE_RADAR = 'radar'; +export const COMPONENT_TYPE_RADAR = COORD_SYS_TYPE_RADAR; +export const SERIES_TYPE_RADAR = COORD_SYS_TYPE_RADAR; + +export const RADAR_DEFAULT_SPLIT_NUMBER = 5; + function defaultsShow(opt: object, show: boolean) { return zrUtil.defaults({ show: show @@ -104,7 +110,7 @@ export type InnerIndicatorAxisOption = AxisBaseOption & { }; class RadarModel extends ComponentModel<RadarOption> implements CoordinateSystemHostModel { - static readonly type = 'radar'; + static readonly type = COMPONENT_TYPE_RADAR; readonly type = RadarModel.type; coordinateSystem: Radar; diff --git a/src/coord/scaleRawExtentInfo.ts b/src/coord/scaleRawExtentInfo.ts index f378b5da5..607e3639e 100644 --- a/src/coord/scaleRawExtentInfo.ts +++ b/src/coord/scaleRawExtentInfo.ts @@ -27,9 +27,8 @@ import { NumericAxisBaseOptionCommon, NumericAxisBoundaryGapOptionItemValue, } from './axisCommonTypes'; -import { ComponentSubType, DimensionIndex, DimensionName, NullUndefined, ScaleDataValue } from '../util/types'; +import { DimensionIndex, DimensionName, NullUndefined, ScaleDataValue } from '../util/types'; import { isIntervalScale, isLogScale, isOrdinalScale, isTimeScale } from '../scale/helper'; -import type SeriesModel from '../model/Series'; import { makeInner, initExtentForUnion, unionExtentFromNumber, isValidNumberForExtent, extentHasValue, @@ -46,7 +45,7 @@ import { error } from '../util/log'; import type Axis from './Axis'; import { mathMax, mathMin } from '../util/number'; import { SCALE_EXTENT_KIND_MAPPING } from '../scale/scaleMapper'; -import { AxisStatKey, eachAxisStatKey } from './axisStatistics'; +import { AxisStatKey, eachKeyOnAxis, eachSeriesOnAxis } from './axisStatistics'; /** @@ -58,8 +57,6 @@ import { AxisStatKey, eachAxisStatKey } from './axisStatistics'; */ const scaleInner = makeInner<{ extent: number[]; - // series on this axis to union data extent. - seriesList: SeriesModel[]; dimIdxInCoord: number; }, Scale>(); @@ -502,31 +499,58 @@ function parseBoundaryGapOptionItem( ) || 0; } +/** + * NOTE: `associateSeriesWithAxis` is not necessarily called, e.g., when + * an axis is not used by any series. + */ +function ensureScaleStore(axisLike: {scale: Scale}) { + const store = scaleInner(axisLike.scale); + if (!store.extent) { + store.extent = initExtentForUnion(); + } + return store; +} + +/** + * This supports union extent on case like: pie (or other similar series) + * lays out on cartesian2d. + * @see scaleRawExtentInfoCreate + */ +export function scaleRawExtentInfoEnableBoxCoordSysUsage( + axisLike: { + scale: Scale; + dim: DimensionName; + }, + coordSysDimIdxMap: HashMap<DimensionIndex, DimensionName> | NullUndefined +): void { + ensureScaleStore(axisLike).dimIdxInCoord = coordSysDimIdxMap.get(axisLike.dim); +} + /** * @usage * class SomeCoordSys { * static create() { * ecModel.eachSeries(function (seriesModel) { - * scaleRawExtentInfoRequireCreate(axis1, seriesModel, ...); - * scaleRawExtentInfoRequireCreate(axis2, seriesModel, ...); + * associateSeriesWithAxis(axis1, seriesModel, ...); + * associateSeriesWithAxis(axis2, seriesModel, ...); * // ... * }); * } * update() { - * scaleRawExtentInfoReallyCreate(axis1); - * scaleRawExtentInfoReallyCreate(axis2); + * scaleRawExtentInfoCreate(axis1); + * scaleRawExtentInfoCreate(axis2); * } * } * class AxisProxy { * reset() { - * scaleRawExtentInfoReallyCreate(axis1); + * scaleRawExtentInfoCreate(axis1); * } * } * * NOTICE: - * - `scaleRawExtentInfoRequireCreate` should be typically called in: + * - `associateSeriesWithAxis`(in `axisStatistics.ts`) should be called in: * - Coord sys create method. - * - `scaleRawExtentInfoReallyCreate` should be typically called in: + * - `scaleRawExtentInfoCreate` should be typically called in: * - `dataZoom` processor. It require processing like: * 1. Filter series data by dataZoom1; * 2. Union the filtered data and init the extent of the orthogonal axes, which is the 100% of dataZoom2; @@ -536,49 +560,9 @@ function parseBoundaryGapOptionItem( * NOTE: If `dataZoom` exists can cover this series, this data and its extent * has been dataZoom-filtered. Therefore this handling should not before dataZoom. * - The callback of `min`/`max` in ec option should NOT be called multiple times, - * therefore, we initialize `ScaleRawExtentInfo` uniformly in `scaleRawExtentInfoReallyCreate`. + * therefore, we initialize `ScaleRawExtentInfo` uniformly in `scaleRawExtentInfoCreate`. */ -export function scaleRawExtentInfoRequireCreate( - axisLike: { - scale: Scale; - }, - seriesModel: SeriesModel -): void { - ensureScaleStore(axisLike).seriesList.push(seriesModel); -} - -/** - * NOTE: `scaleRawExtentInfoRequireCreate` is not necessarily called, e.g., when - * an axis is not used by any series. - */ -function ensureScaleStore(axisLike: {scale: Scale}) { - const store = scaleInner(axisLike.scale); - if (!store.extent) { - store.extent = initExtentForUnion(); - store.seriesList = []; - } - return store; -} - -/** - * This supports union extent on case like: pie (or other similar series) - * lays out on cartesian2d. - * @see scaleRawExtentInfoRequireCreate - */ -export function scaleRawExtentInfoEnableBoxCoordSysUsage( - axisLike: { - scale: Scale; - dim: DimensionName; - }, - coordSysDimIdxMap: HashMap<DimensionIndex, DimensionName> | NullUndefined -): void { - ensureScaleStore(axisLike).dimIdxInCoord = coordSysDimIdxMap.get(axisLike.dim); -} - -/** - * @see scaleRawExtentInfoRequireCreate - */ -export function scaleRawExtentInfoReallyCreate( +export function scaleRawExtentInfoCreate( ecModel: GlobalModel, axis: Axis, from: AxisExtentInfoBuildFrom @@ -602,12 +586,12 @@ export function scaleRawExtentInfoReallyCreate( return; } - scaleRawExtentInfoReallyCreateDeal(scale, axis, axisDim, model, ecModel, from); + scaleRawExtentInfoCreateDeal(scale, axis, axisDim, model, ecModel, from); calcContainShape(scale, axis, ecModel, scale.rawExtentInfo); } -function scaleRawExtentInfoReallyCreateDeal( +function scaleRawExtentInfoCreateDeal( scale: Scale, axis: Axis, axisDim: DimensionName, @@ -618,11 +602,7 @@ function scaleRawExtentInfoReallyCreateDeal( const scaleStore = ensureScaleStore(axis); const extent = scaleStore.extent; - each(scaleStore.seriesList, function (seriesModel) { - // Legend-filtered series need to be ignored since series are registered before `legendFilter`. - if (ecModel.isSeriesFiltered(seriesModel)) { - return; - } + eachSeriesOnAxis(axis, function (seriesModel) { if (seriesModel.boxCoordinateSystem) { // This supports union extent on case like: pie (or other similar series) // lays out on cartesian2d. @@ -658,7 +638,7 @@ function scaleRawExtentInfoReallyCreateDeal( const rawExtentInfo = new ScaleRawExtentInfo(scale, model, extent); injectScaleRawExtentInfo(scale, rawExtentInfo, from); - scaleStore.seriesList = scaleStore.extent = null; // Clean up + scaleStore.extent = null; // Clean up } /** @@ -791,7 +771,7 @@ function calcContainShape( // `NullUndefined` indicates that `linearSupplement` is not introduced. let linearSupplement: number[] | NullUndefined; - eachAxisStatKey(axis, function (axisStatKey) { + eachKeyOnAxis(axis, function (axisStatKey) { const handler = axisContainShapeHandlerMap.get(axisStatKey); if (handler) { const singleLinearSupplement = handler(axis, scale, ecModel); diff --git a/src/coord/single/AxisModel.ts b/src/coord/single/AxisModel.ts index a65e149c8..581edf1bb 100644 --- a/src/coord/single/AxisModel.ts +++ b/src/coord/single/AxisModel.ts @@ -29,6 +29,10 @@ import { import { AxisBaseModel } from '../AxisBaseModel'; import { mixin } from 'zrender/src/core/util'; + +export const COORD_SYS_TYPE_SINGLE_AXIS = 'singleAxis'; +export const COMPONENT_TYPE_SINGLE_AXIS = COORD_SYS_TYPE_SINGLE_AXIS; + export type SingleAxisPosition = 'top' | 'bottom' | 'left' | 'right'; export type SingleAxisOption = AxisBaseOption & BoxLayoutOptionMixin & { @@ -39,7 +43,7 @@ export type SingleAxisOption = AxisBaseOption & BoxLayoutOptionMixin & { class SingleAxisModel extends ComponentModel<SingleAxisOption> implements AxisBaseModel<SingleAxisOption> { - static type = 'singleAxis'; + static type = COMPONENT_TYPE_SINGLE_AXIS; type = SingleAxisModel.type; static readonly layoutMode = 'box'; diff --git a/src/coord/single/Single.ts b/src/coord/single/Single.ts index d69004e8f..7c2e804e4 100644 --- a/src/coord/single/Single.ts +++ b/src/coord/single/Single.ts @@ -28,14 +28,14 @@ import { CoordinateSystem, CoordinateSystemMaster } from '../CoordinateSystem'; import GlobalModel from '../../model/Global'; import ExtensionAPI from '../../core/ExtensionAPI'; import BoundingRect from 'zrender/src/core/BoundingRect'; -import SingleAxisModel from './AxisModel'; +import SingleAxisModel, { COORD_SYS_TYPE_SINGLE_AXIS } from './AxisModel'; import { ParsedModelFinder, ParsedModelFinderKnown } from '../../util/model'; import { ScaleDataValue } from '../../util/types'; import { AxisBaseModel } from '../AxisBaseModel'; import { CategoryAxisBaseOption } from '../axisCommonTypes'; import { scaleCalcNice } from '../axisNiceTicks'; import { - AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE, scaleRawExtentInfoReallyCreate + AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE, scaleRawExtentInfoCreate } from '../scaleRawExtentInfo'; export const singleDimensions = ['single']; @@ -44,7 +44,7 @@ export const singleDimensions = ['single']; */ class Single implements CoordinateSystem, CoordinateSystemMaster { - readonly type = 'single'; + readonly type = COORD_SYS_TYPE_SINGLE_AXIS; readonly dimension = 'single'; /** @@ -101,7 +101,7 @@ class Single implements CoordinateSystem, CoordinateSystemMaster { */ update(ecModel: GlobalModel, api: ExtensionAPI) { const axis = this._axis; - scaleRawExtentInfoReallyCreate(ecModel, axis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); + scaleRawExtentInfoCreate(ecModel, axis, AXIS_EXTENT_INFO_BUILD_FROM_COORD_SYS_UPDATE); scaleCalcNice(axis); } diff --git a/src/coord/single/singleCreator.ts b/src/coord/single/singleCreator.ts index 88c41cd13..e1821cb20 100644 --- a/src/coord/single/singleCreator.ts +++ b/src/coord/single/singleCreator.ts @@ -24,11 +24,11 @@ import Single, { singleDimensions } from './Single'; import GlobalModel from '../../model/Global'; import ExtensionAPI from '../../core/ExtensionAPI'; -import SingleAxisModel from './AxisModel'; +import SingleAxisModel, { COMPONENT_TYPE_SINGLE_AXIS, COORD_SYS_TYPE_SINGLE_AXIS } from './AxisModel'; import SeriesModel from '../../model/Series'; import { SeriesOption } from '../../util/types'; import { SINGLE_REFERRING } from '../../util/model'; -import { scaleRawExtentInfoRequireCreate } from '../scaleRawExtentInfo'; +import { associateSeriesWithAxis } from '../axisStatistics'; /** * Create single coordinate system and inject it into seriesModel. @@ -36,7 +36,7 @@ import { scaleRawExtentInfoRequireCreate } from '../scaleRawExtentInfo'; function create(ecModel: GlobalModel, api: ExtensionAPI) { const singles: Single[] = []; - ecModel.eachComponent('singleAxis', function (axisModel: SingleAxisModel, idx: number) { + ecModel.eachComponent(COMPONENT_TYPE_SINGLE_AXIS, function (axisModel: SingleAxisModel, idx: number) { const single = new Single(axisModel, ecModel, api); single.name = 'single_' + idx; @@ -50,13 +50,13 @@ function create(ecModel: GlobalModel, api: ExtensionAPI) { singleAxisIndex?: number singleAxisId?: string }>) { - if (seriesModel.get('coordinateSystem') === 'singleAxis') { + if (seriesModel.get('coordinateSystem') === COORD_SYS_TYPE_SINGLE_AXIS) { const singleAxisModel = seriesModel.getReferringComponents( - 'singleAxis', SINGLE_REFERRING + COMPONENT_TYPE_SINGLE_AXIS, SINGLE_REFERRING ).models[0] as SingleAxisModel; const single = seriesModel.coordinateSystem = singleAxisModel && singleAxisModel.coordinateSystem; if (single) { - scaleRawExtentInfoRequireCreate(single.getAxis(), seriesModel); + associateSeriesWithAxis(single.getAxis(), seriesModel, COORD_SYS_TYPE_SINGLE_AXIS); } } }); diff --git a/src/core/echarts.ts b/src/core/echarts.ts index a68bddad0..b2a887293 100644 --- a/src/core/echarts.ts +++ b/src/core/echarts.ts @@ -154,17 +154,18 @@ export const dependencies = { const TEST_FRAME_REMAIN_TIME = 1; const PRIORITY_PROCESSOR_SERIES_FILTER = 800; -// Axis statistics require filtered series. -const PRIORITY_PROCESSOR_AXIS_STATISTICS = 810; // In the current impl, "data stack" will modifies the original "series data extent". Some data // processors rely on the stack result dimension to calculate extents. So data stack // should be in front of other data processors. const PRIORITY_PROCESSOR_DATASTACK = 900; +// AXIS_STATISTICS should be after SERIES_FILTER, as it may change the statistics result (like min gap). +// AXIS_STATISTICS should be before filter (dataZoom), as dataZoom require the result in "containShape" calculation. +const PRIORITY_PROCESSOR_AXIS_STATISTICS = 920; // `PRIORITY_PROCESSOR_FILTER` is typically used by `dataZoom` (see `AxisProxy`), which relies // on the initialized "axis extent". const PRIORITY_PROCESSOR_FILTER = 1000; const PRIORITY_PROCESSOR_DEFAULT = 2000; -const PRIORITY_PROCESSOR_STATISTIC = 5000; +const PRIORITY_PROCESSOR_STATISTICS = 5000; // NOTICE: Data processors above block the stream (especially time-consuming processors like data filters). const PRIORITY_VISUAL_LAYOUT = 1000; @@ -188,7 +189,8 @@ export const PRIORITY = { SERIES_FILTER: PRIORITY_PROCESSOR_SERIES_FILTER, AXIS_STATISTICS: PRIORITY_PROCESSOR_AXIS_STATISTICS, FILTER: PRIORITY_PROCESSOR_FILTER, - STATISTIC: PRIORITY_PROCESSOR_STATISTIC + STATISTIC: PRIORITY_PROCESSOR_STATISTICS, // naming - backward compatibility. + STATISTICS: PRIORITY_PROCESSOR_STATISTICS, }, VISUAL: { LAYOUT: PRIORITY_VISUAL_LAYOUT, diff --git a/src/layout/barCommon.ts b/src/layout/barCommon.ts index 3c9d7699c..0f8dd1b8c 100644 --- a/src/layout/barCommon.ts +++ b/src/layout/barCommon.ts @@ -17,7 +17,7 @@ * under the License. */ -import { getMetricsMinGapOnNonCategoryAxis } from '../chart/helper/axisSnippets'; +import { getMetricsNonOrdinalLinearPositiveMinGap } from '../chart/helper/axisSnippets'; import type Axis from '../coord/Axis'; import { AxisStatKey, requireAxisStatistics } from '../coord/axisStatistics'; import { EChartsExtensionInstallRegisters } from '../extension'; @@ -28,28 +28,19 @@ export type BaseBarSeriesSubType = 'bar' | 'pictorialBar'; export const BAR_SERIES_TYPE = 'bar'; -export function registerAxisStatisticsForBaseBar( +export function requireAxisStatisticsForBaseBar( registers: EChartsExtensionInstallRegisters, axisStatKey: AxisStatKey, seriesType: BaseBarSeriesSubType, coordSysType: 'cartesian2d' | 'polar' -) { +): void { requireAxisStatistics( registers, - axisStatKey, { - collectAxisSeries(ecModel, saveAxisSeries) { - // NOTICE: The order of series matters - must be respected to the declaration on ec option, - // because for historical reason, in `barGrid.ts`, the last series holds the effective ec option. - // (See `calcBarWidthAndOffset` in `barGrid.ts`). - ecModel.eachSeriesByType(seriesType, function (seriesModel) { - const coordSys = seriesModel.coordinateSystem; - if (coordSys && coordSys.type === coordSysType) { - saveAxisSeries(seriesModel.getBaseAxis(), seriesModel); - } - }); - }, - getMetrics: getMetricsMinGapOnNonCategoryAxis, + key: axisStatKey, + seriesType, + coordSysType, + getMetrics: getMetricsNonOrdinalLinearPositiveMinGap } ); } diff --git a/src/layout/barGrid.ts b/src/layout/barGrid.ts index a1fa83b4a..dd1368aa2 100644 --- a/src/layout/barGrid.ts +++ b/src/layout/barGrid.ts @@ -43,15 +43,15 @@ import { } from '../coord/scaleRawExtentInfo'; import { EChartsExtensionInstallRegisters } from '../extension'; import { - AxisStatKey, - eachCollectedAxis, - eachCollectedSeries, getCollectedSeriesLength + eachAxisOnKey, + eachSeriesOnAxisOnKey, countSeriesOnAxisOnKey, } from '../coord/axisStatistics'; import { AxisBandWidthResult, calcBandWidth } from '../coord/axisBand'; -import { BaseBarSeriesSubType, getStartValue, registerAxisStatisticsForBaseBar } from './barCommon'; +import { BaseBarSeriesSubType, getStartValue, requireAxisStatisticsForBaseBar } from './barCommon'; import { COORD_SYS_TYPE_CARTESIAN_2D } from '../coord/cartesian/GridModel'; +import { makeAxisStatKey2 } from '../chart/helper/axisSnippets'; const callOnlyOnce = makeCallOnlyOnce(); @@ -171,14 +171,15 @@ function createLayoutInfoListOnAxis( seriesType: BaseBarSeriesSubType ): BarGridLayoutAxisInfo { + const axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D); const seriesInfoOnAxis: BarGridLayoutAxisSeriesInfo[] = []; const bandWidthResult = calcBandWidth( baseAxis, - {fromStat: {key: makeAxisStatKey(seriesType)}, min: 1} + {fromStat: {key: axisStatKey}, min: 1} ); const bandWidth = bandWidthResult.w; - eachCollectedSeries(baseAxis, makeAxisStatKey(seriesType), function (seriesModel: BaseBarSeriesModel) { + eachSeriesOnAxisOnKey(baseAxis, axisStatKey, function (seriesModel: BaseBarSeriesModel) { seriesInfoOnAxis.push({ barWidth: parsePercent(seriesModel.get('barWidth'), bandWidth), barMaxWidth: parsePercent(seriesModel.get('barMaxWidth'), bandWidth), @@ -353,14 +354,14 @@ function calcBarWidthAndOffset( } export function layout(seriesType: BaseBarSeriesSubType, ecModel: GlobalModel): void { - const axisStatKey = makeAxisStatKey(seriesType); - eachCollectedAxis(ecModel, axisStatKey, function (axis: Axis2D) { + const axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D); + eachAxisOnKey(ecModel, axisStatKey, function (axis: Axis2D) { if (__DEV__) { assert(axis instanceof Axis2D); } const columnLayout = makeColumnLayoutOnAxisReal(axis, seriesType); - eachCollectedSeries(axis, axisStatKey, function (seriesModel) { + eachSeriesOnAxisOnKey(axis, axisStatKey, function (seriesModel) { const columnLayoutInfo = columnLayout.columnMap[getSeriesStackId(seriesModel)]; seriesModel.getData().setLayout({ bandWidth: columnLayoutInfo.bandWidth, @@ -520,7 +521,7 @@ function barGridCreateAxisContainShapeHandler(seriesType: BaseBarSeriesSubType): // If bars are placed on 'time', 'value', 'log' axis, handle bars overflow here. // See #6728, #4862, `test/bar-overflow-time-plot.html` if (axis && axis instanceof Axis2D && !isOrdinalScale(scale)) { - if (!getCollectedSeriesLength(axis, makeAxisStatKey(seriesType))) { + if (!countSeriesOnAxisOnKey(axis, makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D))) { return; // Quick path - in most cases there is no bar on non-ordinal axis. } const columnLayout = makeColumnLayoutOnAxisReal(axis, seriesType); @@ -571,16 +572,12 @@ function calcShapeOverflowSupplement( } } -function makeAxisStatKey(seriesType: BaseBarSeriesSubType): AxisStatKey { - return `barGrid-${seriesType}` as AxisStatKey; -} - export function registerBarGridAxisHandlers(registers: EChartsExtensionInstallRegisters) { callOnlyOnce(registers, function () { function register(seriesType: BaseBarSeriesSubType): void { - const axisStatKey = makeAxisStatKey(seriesType); - registerAxisStatisticsForBaseBar( + const axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_CARTESIAN_2D); + requireAxisStatisticsForBaseBar( registers, axisStatKey, seriesType, diff --git a/src/layout/barPolar.ts b/src/layout/barPolar.ts index 357e24c6f..2cdf2eae1 100644 --- a/src/layout/barPolar.ts +++ b/src/layout/barPolar.ts @@ -27,12 +27,12 @@ import GlobalModel from '../model/Global'; import ExtensionAPI from '../core/ExtensionAPI'; import { Dictionary } from '../util/types'; import { calcBandWidth } from '../coord/axisBand'; -import { createBandWidthBasedAxisContainShapeHandler } from '../chart/helper/axisSnippets'; +import { createBandWidthBasedAxisContainShapeHandler, makeAxisStatKey2 } from '../chart/helper/axisSnippets'; import { makeCallOnlyOnce } from '../util/model'; import { EChartsExtensionInstallRegisters } from '../extension'; import { registerAxisContainShapeHandler } from '../coord/scaleRawExtentInfo'; -import { getStartValue, registerAxisStatisticsForBaseBar } from './barCommon'; -import { AxisStatKey, eachCollectedAxis, eachCollectedSeries } from '../coord/axisStatistics'; +import { getStartValue, requireAxisStatisticsForBaseBar } from './barCommon'; +import { eachAxisOnKey, eachSeriesOnAxisOnKey } from '../coord/axisStatistics'; import { COORD_SYS_TYPE_POLAR } from '../coord/polar/PolarModel'; import type Axis from '../coord/Axis'; import { assert, each } from 'zrender/src/core/util'; @@ -64,9 +64,9 @@ function getSeriesStackId(seriesModel: BarSeriesModel) { } export function barLayoutPolar(seriesType: 'bar', ecModel: GlobalModel, api: ExtensionAPI) { - const axisStatKey = makeAxisStatKey(seriesType); + const axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_POLAR); - eachCollectedAxis(ecModel, axisStatKey, function (axis: PolarAxis) { + eachAxisOnKey(ecModel, axisStatKey, function (axis: PolarAxis) { if (__DEV__) { assert((axis instanceof AngleAxis) || axis instanceof RadiusAxis); } @@ -74,7 +74,7 @@ export function barLayoutPolar(seriesType: 'bar', ecModel: GlobalModel, api: Ext const barWidthAndOffset = calcRadialBar(axis, seriesType); const lastStackCoords: LastStackCoords = {}; - eachCollectedSeries(axis, axisStatKey, function (seriesModel: BarSeriesModel) { + eachSeriesOnAxisOnKey(axis, axisStatKey, function (seriesModel: BarSeriesModel) { layoutPerAxisPerSeries(axis, seriesModel, barWidthAndOffset, lastStackCoords); }); }); @@ -93,7 +93,7 @@ function layoutPerAxisPerSeries( const columnWidth = columnLayoutInfo.width; const polar = seriesModel.coordinateSystem as Polar; if (__DEV__) { - assert(polar.type === 'polar'); + assert(polar.type === COORD_SYS_TYPE_POLAR); } const valueAxis = polar.getOtherAxis(baseAxis); @@ -209,9 +209,11 @@ function layoutPerAxisPerSeries( * Calculate bar width and offset for radial bar charts */ function calcRadialBar(axis: Axis, seriesType: 'bar'): BarWidthAndOffsetOnAxis { + const axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_POLAR); + const bandWidth = calcBandWidth( axis, - {fromStat: {key: makeAxisStatKey(seriesType)}, min: 1} + {fromStat: {key: axisStatKey}, min: 1} ).w; let remainedWidth: number = bandWidth; @@ -220,7 +222,7 @@ function calcRadialBar(axis: Axis, seriesType: 'bar'): BarWidthAndOffsetOnAxis { let gapOption: string | number = '30%'; const stacks: Dictionary<StackInfo> = {}; - eachCollectedSeries(axis, makeAxisStatKey(seriesType), function (seriesModel: BarSeriesModel, idx) { + eachSeriesOnAxisOnKey(axis, axisStatKey, function (seriesModel: BarSeriesModel) { const stackId = getSeriesStackId(seriesModel); if (!stacks[stackId]) { @@ -308,17 +310,13 @@ function calcRadialBar(axis: Axis, seriesType: 'bar'): BarWidthAndOffsetOnAxis { return result; } -function makeAxisStatKey(seriesType: 'bar'): AxisStatKey { - return `barPolar-${seriesType}` as AxisStatKey; -} - export function registerBarPolarAxisHandlers( registers: EChartsExtensionInstallRegisters, seriesType: 'bar' // Currently only 'bar' is supported. ): void { callOnlyOnce(registers, function () { - const axisStatKey = makeAxisStatKey(seriesType); - registerAxisStatisticsForBaseBar( + const axisStatKey = makeAxisStatKey2(seriesType, COORD_SYS_TYPE_POLAR); + requireAxisStatisticsForBaseBar( registers, axisStatKey, seriesType, diff --git a/src/model/Global.ts b/src/model/Global.ts index 33b92ba3f..1b5c741c2 100644 --- a/src/model/Global.ts +++ b/src/model/Global.ts @@ -849,6 +849,9 @@ echarts.use([${seriesImportName}]);`); return each(this.getSeriesByType(subType), cb, context); } + /** + * It means "filtered out". + */ isSeriesFiltered(seriesModel: SeriesModel): boolean { assertSeriesInitialized(this); return this._seriesIndicesMap.get(seriesModel.componentIndex) == null; diff --git a/src/scale/scaleMapper.ts b/src/scale/scaleMapper.ts index 845594430..da8b02bee 100644 --- a/src/scale/scaleMapper.ts +++ b/src/scale/scaleMapper.ts @@ -232,8 +232,8 @@ export interface ScaleMapperGeneric<This> { * * [The steps of extent construction in EC_MAIN_CYCLE]: * - step#1. At `CoordinateSystem#create` stage, requirements of collecting series data extents are - * committed to `scaleRawExtentInfoRequireCreate`, and `Scale` instances are created. - * - step#2. Call `scaleRawExtentInfoReallyCreate` to really collect series data extent and create + * committed to `associateSeriesWithAxis`, and `Scale` instances are created. + * - step#2. Call `scaleRawExtentInfoCreate` to really collect series data extent and create * `ScaleRawExtentInfo` instances to manage extent related configurations * - at "data processing" stage for dataZoom controlled axes, if any, or * - at "CoordinateSystem#update" stage for all other axes. diff --git a/src/util/jitter.ts b/src/util/jitter.ts index 36bbda5c0..89460bdaa 100644 --- a/src/util/jitter.ts +++ b/src/util/jitter.ts @@ -21,6 +21,8 @@ import type Axis from '../coord/Axis'; import { calcBandWidth } from '../coord/axisBand'; import type { AxisBaseModel } from '../coord/AxisBaseModel'; import Axis2D from '../coord/cartesian/Axis2D'; +import { COORD_SYS_TYPE_CARTESIAN_2D } from '../coord/cartesian/GridModel'; +import { COORD_SYS_TYPE_SINGLE_AXIS } from '../coord/single/AxisModel'; import type SingleAxis from '../coord/single/SingleAxis'; import type SeriesModel from '../model/Series'; import { isOrdinalScale } from '../scale/helper'; @@ -31,8 +33,8 @@ export function needFixJitter(seriesModel: SeriesModel, axis: Axis): boolean { const coordType = coordinateSystem && coordinateSystem.type; const baseAxis = coordinateSystem && coordinateSystem.getBaseAxis && coordinateSystem.getBaseAxis(); const scaleType = baseAxis && baseAxis.scale && baseAxis.scale.type; - const seriesValid = coordType === 'cartesian2d' && scaleType === 'ordinal' - || coordType === 'single'; + const seriesValid = coordType === COORD_SYS_TYPE_CARTESIAN_2D && scaleType === 'ordinal' + || coordType === COORD_SYS_TYPE_SINGLE_AXIS; const axisValid = (axis.model as AxisBaseModel).get('jitter') > 0; return seriesValid && axisValid; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
