echarts
Version:
Apache ECharts is a powerful, interactive charting and data visualization library for browser
219 lines (218 loc) • 12.1 kB
TypeScript
import { NullUndefined } from '../util/types.js';
import { AxisBreakParsingResult, BreakScaleMapper } from './break.js';
import type { ValueTransformLookupOpt } from './helper.js';
import { DataSanitizationFilter } from '../data/helper/dataValueHelper.js';
/**
* Illustration:
* SCALE_EXTENT_KIND_EFFECTIVE: |------------| (always exist)
* SCALE_EXTENT_KIND_MAPPING: |---|------------|--| (present only when it is specified by `setExtent2`)
*
* - [SCALE_EXTENT_KIND_EFFECTIVE]:
* It is a portion of a scale extent that is functional on most features, including:
* - All tick/label-related calculation.
* - `dataZoom` controlled ends.
* - Cartesian2D `clampData`.
* - line series start.
* - heatmap series range.
* - markerArea range.
* - etc.
* `SCALE_EXTENT_KIND_EFFECTIVE` always exists.
*
* - [SCALE_EXTENT_KIND_MAPPING]:
* It is an expanded extent from ends of `SCALE_EXTENT_KIND_EFFECTIVE` to accommodate shapes at edges to
* avoid overflow. They can be typically used by bar/candlestick series on category axis with
* `boundaryGap: false`, or on other numeric axes. ec option `xxxAxis.containShape` is the switch.
* In this case, we need to:
* - Do not render ticks and labels in the portion "SCALE_EXTENT_KIND_MAPPING - SCALE_EXTENT_KIND_EFFECTIVE",
* since they are considered meaningless there.
* - Prevent "nice strategy" from triggering unexpectedly by the "contain shape expansion".
* Otherwise, for example, the original extent is `[0, 1000]`, then the expanded
* extent, say `[-5, 1000]`, can cause a considerable negative expansion by "nice",
* like `[-200, 10000]`, which is commonly unexpected. And it is exacerbated in LogScale.
* - Prevent the min/max tick label from displaying, since they are commonly meaningless
* and probably misleading.
* Therefore, `SCALE_EXTENT_KIND_MAPPING` is only used for:
* - mapping between data and pixel, such as,
* - `scaleMapper.normalize/scale`;
* - Cartesian2D `calcAffineTransform` (a quick path of `scaleMapper.normalize/scale`).
* - `grid` boundary related calculation in view rendering, such as, `barGrid` calculates
* `barWidth` for numeric scales based on the data extent.
* - Axis line position determination (such as `canOnZeroToAxis`);
* - `axisPointer` triggering (otherwise users may be confused if using `SCALE_EXTENT_KIND_EFFECTIVE`).
* `SCALE_EXTENT_KIND_MAPPING` can be absent, which can be used to determine whether it is used.
*
* @see SCALE_EXTENT_CONSTRUCTION for the full processing flow.
*/
export declare type ScaleExtentKind = typeof SCALE_EXTENT_KIND_EFFECTIVE | typeof SCALE_EXTENT_KIND_MAPPING;
export declare const SCALE_EXTENT_KIND_EFFECTIVE = 0;
export declare const SCALE_EXTENT_KIND_MAPPING = 1;
/**
* - [SCALE_MAPPER_DEPTH_OUT_OF_BREAK]:
* In `transformIn`, it transforms a value from the outermost space to the space before break being applied.
* In `transformOut`, it transforms a value from the space before break being applied to the outermost space.
* Typically nice axis ticks are picked in that space due to the current design of nice ticks
* algorithm, while size related features may use `SCALE_MAPPER_DEPTH_INNERMOST`.
* - [SCALE_MAPPER_DEPTH_INNERMOST]:
* Currently only linear space is used as the innermost space.
*/
export declare type ScaleMapperDepthOpt = {
depth?: NullUndefined | typeof SCALE_MAPPER_DEPTH_OUT_OF_BREAK | typeof SCALE_MAPPER_DEPTH_INNERMOST;
};
export declare const SCALE_MAPPER_DEPTH_OUT_OF_BREAK = 2;
export declare const SCALE_MAPPER_DEPTH_INNERMOST = 3;
export declare type ScaleMapperTransformOutOpt = (ScaleMapperDepthOpt & ValueTransformLookupOpt);
export declare type ScaleMapperTransformInOpt = ScaleMapperDepthOpt;
/**
* @tutorial [SCALE_COMPOSITION_AND_TRANSFORMATION]:
* `ScaleMapper` is designed for multiple steps of numeric transformations from a certain space to a linear space,
* or vice versa. Each steps is implemented as a `ScaleMapper`, and composed like a decorator pattern. And some
* steps, such as "axis breaks transfromation", can be skipped when no breaks for performance consideration.
* Currently we support:
* - step#0: extent based linear scaling.
* This is implemented in `LinearScaleMapper`.
* It is mixed into `IntervalScale`, `TimeScale`;
* and it is also composited into `BreakScaleMapper`, `OrdinalScale`, `LogScale`.
* - step#1: axis breaks.
* This is implemented in `BreakScaleMapper`.
* This step may be absent if no breaks.
* - step#2: logarithmic (implemented in `LogScale`), or
* ordinal-related handling, or
* others to be supported, such as asinh ...
* Illustration of some currently supported cases:
* - linear_space(in an IntervalScale)
* - break_space(in an IntervalScale method bound by a BreakScaleMapper)
* └─break_transform─► linear_space(in a LinearScaleMapper owned by a BreakScaleMapper)
* - log_space(in a LogScale)
* └─log_transform─► linear_space(in an IntervalScale)
* - log_space(in a LogScale)
* └─log_transform─► break_space(in an IntervalScale method bound by a BreakScaleMapper)
* └─break_transform─► linear_space(in a LinearScaleMapper owned by a BreakScaleMapper)
* - linear_space(in a TimeScale)
* - break_space(in a TimeScale method bound by a BreakScaleMapper)
* └─break_transform─► linear_space(in a LinearScaleMapper owned by a BreakScaleMapper)
* - category_values(in a OrdinalScale)
* └─category_to_numeric─► linear_space(in a LinearScaleMapper owned by a BreakScaleMapper)
*
*
* @tutorial [SCALE_EXTENT_CONSTRUCTION]:
* The full construction processing of the scale extent in EC_FULL_UPDATE_CYCLE:
* - step#1. At `CoordinateSystem#create` stage, requirements of collecting series data extents are
* 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.
* - step#3. Perform "nice" (see `scaleCalcNice`) or "align" (see `scaleCalcAlign`) strategies to
* modify the original extent from `ScaleRawExtentInfo` instance, if needed, at
* "CoordinateSystem#update" stage.
* - step#4. `calcContainShape` is performed. Set `SCALE_EXTENT_KIND_MAPPING` if needed.
* See AXIS_CONTAIN_SHAPE_PROCESSING_ORDER for more details.
*
*/
export interface ScaleMapper extends ScaleMapperGeneric<ScaleMapper> {
}
export interface ScaleMapperGeneric<This> {
/**
* Enable a fast path in large data traversal - the call of `transformIn`/`transformOut`
* can be omitted, and this is the most case.
*/
needTransform(this: This): boolean;
/**
* Normalize a value to linear [0, 1], return 0.5 if extent span is 0.
* The typical logic is:
* `transformIn_self` -> `transformIn_inner` -> ... to the innermost space,
* then do linear normalization based on innermost extent.
*/
normalize: (this: This, val: number) => number;
/**
* Scale a normalized value to extent. It's the inverse of `normalize`.
*/
scale: (this: This, val: number) => number;
/**
* [NOTICE]:
* - This method must be available since the instance is constructed.
* - This method has nothing to do with extent - transforming out of extent is supported.
*
* This method transforms a value forward into a inner space.
* The typical logic is:
* `transformIn_self` -> `transformIn_inner` -> ... to the innermost space.
* In most cases axis ticks are laid out in linear space, and some features
* (such as LogScale, axis breaks) transform values from their own spaces into linear space.
*/
transformIn: (this: This, val: number, opt: ScaleMapperTransformInOpt | NullUndefined) => number;
/**
* [NOTICE]:
* - This method must be available since the instance is constructed.
* - This method has nothing to do with extent - transforming out of extent is supported.
*
* The inverse method of `transformIn`.
*/
transformOut: (this: This, val: number, opt: ScaleMapperTransformOutOpt | NullUndefined) => number;
/**
* Whether the extent contains the given value.
*/
contain: (this: This, val: number) => boolean;
/**
* [NOTICE]:
* In EC_FULL_UPDATE_CYCLE, scale extent is finally determined at `coordSys#update` stage.
*
* Get a clone of the scale extent.
* An extent is always in an increase order.
* It always returns an array - never be a null/undefined.
*
* @see SCALE_EXTENT_CONSTRUCTION for the full processing flow.
*/
getExtent(this: This): number[];
/**
* [NOTICE]:
* Callers must NOT modify the return.
*
* @see SCALE_EXTENT_CONSTRUCTION for the full processing flow.
*/
getExtentUnsafe(this: This, kind: ScaleExtentKind, depth: ScaleMapperDepthOpt['depth'] | NullUndefined): number[] | NullUndefined;
/**
* [NOTICE]:
* The caller must ensure `start <= end` and both are finite number!
*
* `setExtent` is identical to `setExtent2(SCALE_EXTENT_KIND_EFFECTIVE)`.
*
* @see SCALE_EXTENT_CONSTRUCTION for the full processing flow.
*/
setExtent(this: This, start: number, end: number): void;
setExtent2(this: This, kind: ScaleExtentKind, start: number, end: number): void;
/**
* Filter for sanitization.
*/
getFilter?: () => DataSanitizationFilter;
/**
* NOTICE:
* - Should not sanitize invalid values (e.g., NaN, Infinity, null, undefined),
* since it probably has special meaning, and always properly handled in every Scale.
* - Should not depend on the current `extent`, since it can be used before `extent` determined.
*
* Sanitize a value if possible. For example, for LogScale, the negative part will be clampped.
* This provides some permissiveness to ec option like `xxxAxis.min/max`.
*/
sanitize?: ((this: This, value: number | NullUndefined, dataExtent: number[]) => number | NullUndefined) | NullUndefined;
/**
* If not provided, use `0`.
*/
getDefaultStartValue?: (() => number) | NullUndefined;
/**
* Restrict the modification behavior of a scale for robustness. e.g., avoid subsequently
* modifying `SCALE_EXTENT_KIND_EFFECTIVE` but no sync to `SCALE_EXTENT_KIND_MAPPING`.
*/
freeze(this: This): void;
}
export declare function initBreakOrLinearMapper(mapper: ScaleMapper | NullUndefined, breakParsed: AxisBreakParsingResult | NullUndefined, initialExtent: number[] | NullUndefined): {
brk: BreakScaleMapper | NullUndefined;
mapper: ScaleMapper;
};
export declare type DecoratedScaleMapperMethods<THost extends ScaleMapper> = Omit<ScaleMapperGeneric<THost>, 'freeze'>;
export declare function decorateScaleMapper<THost extends ScaleMapper>(host: THost, decoratedMapperMethods: Omit<ScaleMapperGeneric<THost>, 'freeze'>): void;
export declare function enableScaleMapperFreeze(host: ScaleMapper, subMapper: ScaleMapper): void;
export declare function getScaleExtentForTickUnsafe(mapper: ScaleMapper): number[];
export declare function getScaleExtentForMappingUnsafe(mapper: ScaleMapper, depth: ScaleMapperDepthOpt['depth'] | NullUndefined): number[];
export declare function getScaleLinearSpanForMapping(mapper: ScaleMapper): number;
export declare function getScaleLinearSpanEffective(mapper: ScaleMapper): number;
export declare function initLinearScaleMapper(mapper: ScaleMapper | NullUndefined, initialExtent: number[] | NullUndefined): ScaleMapper;