import type { TDataSeriesDefinition } from "../../Builder/types/DataSeriesDefinitions"; import { DeletableEntity } from "../../Core/DeletableEntity"; import { EventHandler } from "../../Core/EventHandler"; import { NumberRange } from "../../Core/NumberRange"; import type { NumberArray } from "../../types/NumberArray"; import { ESearchMode } from "../../types/SearchMode"; import type { SCRTDoubleVector, SCRTIntVector, SCRTStringDictionary, TSciChart } from "../../types/TSciChart"; import { EYRangeMode } from "../../types/YRangeMode"; import type { ExtremeResamplerHelper } from "../Numerics/Resamplers/ExtremeResamplerHelper"; import type { ResamplingParams } from "../Numerics/Resamplers/ResamplingParams"; import type { SeriesAnimation } from "../Visuals/RenderableSeries/Animations/SeriesAnimation"; import type { IDataDistributionCalculator } from "./DataDistributionCalculator/IDataDistributionCalculator"; import type { IDoubleVectorProvider } from "./DoubleVectorProvider"; import type { EDataSeriesType, IDataChangeArgs, IDataSeries } from "./IDataSeries"; import { EDataChangeType, EDataSeriesValueType } from "./IDataSeries"; import type { IMetadataGenerator, IPointMetadata } from "./IPointMetadata"; import type { IPointSeries } from "./PointSeries/IPointSeries"; import { StringColumnStore } from "./StringColumnStore"; /** * Options to pass to the {@link BaseDataSeries} constructor */ export interface IBaseDataSeriesOptions { /** * A unique Id for the {@link IDataSeries} */ id?: string; /** * The DataSeries name, used in legends, tooltips to identify the chart series */ dataSeriesName?: string; /** * When true, the Data is sorted in X. Same as isSorted. * @remarks The user must specify this parameter if the data is not sorted in X * in order to have correct rendering. This parameter is used to choose the correct * algorithms for zooming, panning and ranging and ensure best performance. */ dataIsSortedInX?: boolean; /** * When true, the Data is sorted in X. Same as dataIsSortedInX. * @remarks The user must specify this parameter if the data is not sorted in X * in order to have correct rendering. This parameter is used to choose the correct * algorithms for zooming, panning and ranging and ensure best performance. */ isSorted?: boolean; /** * When true, the Data is evenly spaced in X. * @remarks * The user must specify this flag (defaults to true) in order to choose the correct, and * fastest algorithms for drawing, indexing and ranging. If you experience glitches or * strange drawing, it may be because you have set data with uneven spacing in X but not set this flag. */ dataEvenlySpacedInX?: boolean; /** * Gets or sets whether the Y data contains NaN values. */ containsNaN?: boolean; /** * Set the maximum size of the dataSeries in FIFO (First In First Out) mode. This can only be set in the constructor options. * If set, the dataSeries does not support insert/insertRange or remove/removeRange. Any data that is appended once the dataSeries has reached fifoCapacity will cause * the oldest data to be discarded. This is much more efficient than appending and removing for achieving scrolling data. * Spline series and Stacked series currently do not support fifo mode. * To get the scrolling effect, you need to consider the behaviour of your X Axis. You can either * Use a {@link CategoryAxis} * Use a {@link NumericAxis} with increasing x values, and update the visibleRange (or use zoomExtents) */ fifoCapacity?: number; /** * If true, data in fifo mode will not be "unwrapped" before drawing, giving ecg style sweeping mode. * To get the sweeping effect, you need to consider the behaviour of your X Axis. You can either * Use a {@link CategoryAxis} * Use a {@link NumericAxis} and make your x values an offset from the first value, eg by doing x % fifoCapcity */ fifoSweeping?: boolean; /** In fifo sweeping mode, the number of earliest points to skip to create a gap between the latest and earliest data */ fifoSweepingGap?: number; /** Sets the starting index of data for fifo mode. */ fifoStartIndex?: number; /** * Gets or sets the capacity of the data series. This is the amount of memory reserved for the data. For a normal dataSeries this will grow as data is appended. * You can avoid memory fragmentation by creating your series with a larger capacity if you know it will grow to that. */ capacity?: number; /** * The X-values array to pre-populate the {@link XyDataSeries} */ xValues?: NumberArray; /** * The Y-values array to pre-populate the {@link XyDataSeries} */ yValues?: NumberArray; /** * The Metadata values of type {@link IPointMetadata} to pre-populate the {@link IDataSeries} * If a single metadata value is supplied, this will be used as a template for all data values. * If type is specified, it should refer to a registered metadataGenerator {@link IMetadataGenerator}, * which can provide all metadata, based on the data provided, or a single object that will be used when adding data if no metadata is provided. */ metadata?: IPointMetadata[] | IPointMetadata | { type: string; data?: any; }; /** The number of y values arrays. Eg Xy = 1, Xyy = 2 xOHLC = 4 */ arrayCount?: number; /** The names for the y values arrays. Defaults to y, y1, y2 etc */ valueNames?: string[]; /** Whether each set of Y values should be included when calculating y Range */ includeInYRange?: boolean[]; /** * The names of string columns to declare on this DataSeries. Each is dictionary-encoded native * storage holding one string per row, kept in step with the numeric values by every positional * mutator. Declared columns start filled with empty cells, so appending numeric-only data never * throws - see {@link BaseDataSeries.getTextAt} and {@link BaseDataSeries.setStringColumnValues}. */ stringColumns?: string[]; } /** * The base class for DataSeries in SciChart's * {@link https://www.scichart.com/javascript-chart-features | JavaScript Charts} * @remarks * A DataSeries stores the data to render. This is independent from the {@link IRenderableSeries | RenderableSeries} * which defines how that data should be rendered. * * See derived types of {@link BaseDataSeries} to find out what data-series are available. * See derived types of {@link IRenderableSeries} to find out what 2D JavaScript Chart types are available. */ export declare abstract class BaseDataSeries extends DeletableEntity implements IDataSeries { /** @inheritDoc */ abstract readonly type: EDataSeriesType; /** @inheritDoc */ readonly dataChanged: EventHandler; /** @inheritDoc */ readonly id: string; /** @inheritDoc */ readonly dataDistributionCalculator: IDataDistributionCalculator; minXSpacing: number; /** * If set, these will be included in the serialised definition, so that it can be used with sharedData */ dataIds: Record; /** * The {@link TSciChart | SciChart WebAssembly Context} containing native methods and access to our WebGL2 Engine */ readonly webAssemblyContext: TSciChart; /** * X vector with initial animation values */ xInitialAnimationValues: SCRTDoubleVector; /** * Y vector with initial animation values */ yInitialAnimationValuesArray: SCRTDoubleVector[]; /** * X vector with final animation values */ xFinalAnimationValues: SCRTDoubleVector; /** * Y vector with final animation values */ yFinalAnimationValuesArray: SCRTDoubleVector[]; readonly valueNames: string[]; protected xValues: SCRTDoubleVector; protected indexes: SCRTDoubleVector; protected readonly yValuesArray: SCRTDoubleVector[]; /** The number of y values arrays in this DataSeries */ readonly arrayCount: number; protected isDeleted: boolean; protected doubleVectorProvider: IDoubleVectorProvider; protected includeInYRange: boolean[]; /** * The string columns declared on this DataSeries, by name. One {@link StringColumnStore} per * column owns that column's codes, dictionary and caches; this class only keeps them in step * with the numeric values, one row of codes per row of x. */ protected readonly stringColumns: Map; private dataSeriesNameProperty; private isSortedProperty; private containsNaNProperty; private isEvenlySpacedProperty; private metadataProperty; private metadataGeneratorProperty; private changeCountProperty; /** Rows appended since {@link compactStringColumnsIfNeeded} last asked the string columns. */ private rowsSinceStringCompactionCheck; private fifoCapacityProperty; private fifoSweepingProperty; private fifoSweepingGapProperty; /** * Creates an instance of {@link BaseDataSeries} * @param webAssemblyContext the {@link TSciChart | SciChart WebAssembly Context} containing native methods * and access to our underlying WebGL2 rendering engine * @param options the {@link IBaseDataSeriesOptions} which can be passed to config the DataSeries at construct time */ protected constructor(webAssemblyContext: TSciChart, options?: IBaseDataSeriesOptions); getYValuesByName(name: string, dataSeriesValueType?: EDataSeriesValueType): SCRTDoubleVector; /** * Appends a single X, ...YArray, point to the DataSeries * @remarks * For best performance on drawing large datasets, use the {@link appendRange} method * * Any changes of the DataSeries will trigger a redraw on the parent {@link SciChartSurface} * @param x The X-value * @param metadata The point metadata * @param yArray An array of the y values */ appendN(x: number, yArray: number[], metadata?: T, onAppend?: () => void): void; /** * Appends a range of X, yValuesArray points to the DataSeries * @remarks * This method is considerably higher performance than {@link append} which appends a single point * * Any changes of the DataSeries will trigger a redraw on the parent {@link SciChartSurface} * @param xValues The X-values * @param yValuesArray An array of Y-value arrays * @param metadata The array of point metadata */ appendRangeN(xValues: NumberArray, yValuesArray: NumberArray[], metadata?: T[], onAppend?: () => void): void; /** * Appends a range of X, yValuesArray points to the DataSeries * @remarks * This method is an optimized version of the {@link appendRangeN}. * It skips some data validation steps. * So, make sure to set the proper optimization flags manually ({@link isSorted}, {@link containsNaN}, etc.) when using this method. * * Any changes of the DataSeries will trigger a redraw on the parent {@link SciChartSurface} * @param xValues The X-values defined as 64bit float numbers contained within an ArrayBuffer * @param yValuesArray An array of Y-value arrays defined as 64bit float numbers contained within an ArrayBuffer * @param metadata The array of point metadata */ appendBufferRangeN(xValues: ArrayBuffer, yValuesArray: ArrayBuffer[], metadata?: T[], onAppend?: () => void): void; /** * Updates a single set of y values by X-index * @remarks Any changes of the DataSeries will trigger a redraw on the parent {@link SciChartSurface} * @param index the index to update * @param yArray The new Y values * @param metadata The point metadata */ updateN(index: number, yArray: number[], metadata?: T, onUpdate?: () => void): void; /** * Updates a single X, and set of Y-values by X-index. Might also need to set isSorted = false * @remarks Any changes of the DataSeries will trigger a redraw on the parent {@link SciChartSurface} * @param index The index to update * @param x The new X value * @param yArray The new Y values * @param metadata The point metadata */ updateXyN(index: number, x: number, yArray: number[], metadata?: T, onUpdate?: () => void): void; /** * @summary Inserts a single X and set of Y-values at the start index * @remarks * For best performance on drawing large datasets, use the {@link insertRange} method * * Any changes of the DataSeries will trigger a redraw on the parent {@link SciChartSurface} * @param startIndex the index to insert at * @param x the X value * @param yArray the Y values * @param metadata The point metadata */ insertN(startIndex: number, x: number, yArray: number[], metadata?: T, onInsert?: () => void): void; /** * @summary Inserts a range of X,Y values at the startIndex * @remarks * Any changes of the DataSeries will trigger a redraw on the parent {@link SciChartSurface} * @param startIndex the index to insert at * @param xValues the XValues * @param yValues yValuesArray An array of Y-value arrays * @param metadata The array of point metadata */ insertRangeN(startIndex: number, xValues: NumberArray, yValuesArray: NumberArray[], metadata?: T[], onInsert?: () => void): void; /** * Removes an X, and all associated y values at the specified index * @remarks Any changes of the DataSeries will trigger a redraw on the parent {@link SciChartSurface} * @param index the index to remove at */ removeAt(index: number): void; /** * @summary Removes a range of X, and all associated y values starting at the specified index * @remarks Any changes of the DataSeries will trigger a redraw on the parent {@link SciChartSurface} * @param startIndex the start index to remove at * @param count the number of points to remove */ removeRange(startIndex: number, count: number): void; /** @inheritDoc */ clear(): void; /** * Gets or sets the capacity of data-points in the DataSeries */ get capacity(): number; /** * Gets or sets the capacity of data-points in the DataSeries */ set capacity(value: number); /** @inheritDoc */ get containsNaN(): boolean; /** @inheritDoc */ set containsNaN(containsNaN: boolean); /** @inheritDoc */ get isSorted(): boolean; /** @inheritDoc */ set isSorted(isSorted: boolean); /** @inheritDoc */ get isEvenlySpaced(): boolean; /** @inheritDoc */ set isEvenlySpaced(isSorted: boolean); /** @inheritDoc */ get dataSeriesName(): string; /** @inheritDoc */ set dataSeriesName(dataSeriesName: string); /** @inheritDoc */ count(): number; /** @inheritDoc */ getIsDeleted(): boolean; /** @inheritDoc */ get fifoCapacity(): number; /** @inheritDoc */ get fifoStartIndex(): number; /** @inheritDoc */ get fifoSweeping(): boolean; /** @inheritDoc */ set fifoSweeping(enabled: boolean); /** @inheritDoc */ get fifoSweepingGap(): number; /** @inheritDoc */ set fifoSweepingGap(fifoSweepingGap: number); /** @inheritDoc */ getNativeIndexes(): SCRTDoubleVector; /** @inheritDoc */ getNativeXValues(): SCRTDoubleVector; /** @inheritDoc */ getNativeYValues(i?: number): SCRTDoubleVector; get yInitialAnimationValues(): SCRTDoubleVector; get yFinalAnimationValues(): SCRTDoubleVector; getNativeValue(values: SCRTDoubleVector, index: number): number; /** @inheritDoc */ delete(): void; /** * Call to notify subscribers of {@link dataChanged} that the data has changed and {@link SciChartSurface} needs redrawing */ notifyDataChanged(changeType: EDataChangeType, index: number, count: number, name?: string): void; /** @inheritDoc */ get xRange(): NumberRange; private memoizedgetXRange; /** @inheritDoc */ getXRange(dataSeriesValueType?: EDataSeriesValueType): NumberRange; getXRangeByName(dataSeriesValueType?: EDataSeriesValueType, valueName?: string): NumberRange; private memoizedGetWindowedYRange; /** @inheritDoc */ getWindowedYRange(xRange: NumberRange, getPositiveRange: boolean, isXCategoryAxis?: boolean, dataSeriesValueType?: EDataSeriesValueType, yRangeMode?: EYRangeMode): NumberRange; /** @inheritDoc */ getIndicesRange(xRange: NumberRange, isCategoryData?: boolean, downSearchMode?: ESearchMode, upSearchMode?: ESearchMode): NumberRange; /** @inheritDoc */ get hasValues(): boolean; /** * Check if the series has an existing metadataGenerator */ hasMetadataGenerator(): boolean; /** * Sets a function that will be used to generate metadata for values when they are appended/inserted, if no explicit metadata is supplied. * @param generator */ setMetadataGenerator(generator: IMetadataGenerator): void; /** * Gets the metadata by index * @param index The X index */ getMetadataAt(index: number, ignoreFifo?: boolean): T; /** * Gets the metadata array length */ getMetadataLength(): number; /** * Check if the series has an existing metadata */ get hasMetadata(): boolean; /** True when a string column of this name is declared on this DataSeries. */ isStringColumn(name: string): boolean; /** True when this DataSeries declares at least one string column. */ hasStringColumns(): boolean; /** * Gets the decoded string of one cell of a string column. * @param name The string column name * @param index The row index. LOGICAL (fifo-resolved) unless `ignoreFifo` is set. * @param ignoreFifo When true, reads the RAW ring slot rather than the logical row - the same * distinction {@link getMetadataAt} draws with its parameter of the same name. * @returns `""` for a row with no value, and `undefined` ONLY when the column name is unknown. * The two are different things and callers depend on telling them apart. */ getTextAt(name: string, index: number, ignoreFifo?: boolean): string | undefined; /** * Materialises a whole string column as JS strings (rows with no value become `""`). A copy, * for export and serialisation - the per-row read path is {@link getTextAt}. * @returns `undefined` when the column name is unknown. */ getStringValuesCopy(name: string): string[]; /** * A zero-copy Int32 view onto a string column's codes. Re-fetch it after any mutation. * @remarks On a fifo series this is the ring in PHYSICAL order - see * {@link StringColumnStore.getCodesView}. * @returns `undefined` when the column name is unknown. * @ignore Internal plumbing. Hands out a view onto native memory whose validity the caller * cannot check; documented API is {@link getStringValuesCopy} and {@link getTextAt}. */ getStringCodesView(name: string): Int32Array | undefined; /** * The decode-once code -> value cache of a string column. Heap-independent, so it survives a * heap growth. * @returns `undefined` when the column name is unknown. * @ignore Internal plumbing - see {@link getStringCodesView}. */ getStringDictCache(name: string): readonly string[] | undefined; /** * The native codes vector of a string column, for registering the column with a query store. * Borrowed, not transferred - this DataSeries still owns and deletes it. * @returns `undefined` when the column name is unknown. * @ignore Internal plumbing. Returns a borrowed native handle for the query layer to register; * it is not part of the documented surface and its lifetime is this DataSeries'. */ getStringCodes(name: string): SCRTIntVector | undefined; /** * The native dictionary of a string column, for registering the column with a query store. * Borrowed, not transferred - this DataSeries still owns and deletes it. * @returns `undefined` when the column name is unknown. * @ignore Internal plumbing - see {@link getStringCodes}. */ getStringDictionary(name: string): SCRTStringDictionary | undefined; /** * Replaces a string column's values in place, leaving the row count unchanged. Declares the * column if it does not exist yet, filled with empty cells first so the row-count invariant * holds before the first value lands. * @param name The string column name * @param values The new values, at most one per row. Rows past the end of `values` are blanked, * because this replaces the column rather than patching it. * @remarks Cannot add rows: a string column carries no x, so growing one on its own would break * the invariant that every column holds exactly one cell per row. */ setStringColumnValues(name: string, values: string[]): void; /** * Stops a string column reclaiming dead dictionary entries until the matching * {@link resumeStringCompaction} call. Nestable - a column stays suspended until every suspend * has been matched. * * @param name The string column name. Unknown names are ignored, so a caller need not know * whether a column is dictionary-backed. * * @remarks This is the public seam a consumer that HOLDS dictionary codes across a span needs. * Compaction renumbers codes, and a live `oneOf` filter holds its selection as a by-value * snapshot of codes taken when the filter was applied (`SCRTFilterSet::aOneOfCodes`), with no * hook to re-resolve it. Compacting while such a filter is live therefore silently selects the * wrong rows rather than failing. Nothing in this class can see filters, and must not learn * about the query layer to find out - so the query layer brackets its own exposure through * this pair. * @ignore Not documented public API. The pair carries a bracketing contract that only the * query layer can honour, and freezing it into v6's surface would commit us to that contract * for consumers who have no way to know when it applies. */ suspendStringCompaction(name: string): void; /** * Lets a string column reclaim dead dictionary entries again, after a matching * {@link suspendStringCompaction}. Unknown names are ignored. * @ignore Not documented public API - see {@link suspendStringCompaction}. */ resumeStringCompaction(name: string): void; /** * Appends `count` rows with no value to every string column, so an append that carries only * numeric values keeps one code per row rather than throwing. * @remarks Call AFTER the numeric append has succeeded - a numeric failure must not leave the * columns longer than the series - and BEFORE any subclass `onAppend` callback, which is where * a subclass like `XyTextDataSeries` writes its real values over these blanks. */ private appendEmptyToStringColumns; /** As {@link appendEmptyToStringColumns}, for an insert: shifts the later cells up with the rows. */ private insertEmptyIntoStringColumns; /** * Lets every string column reclaim its dead dictionary entries if its own threshold says that is * worth doing. Without this a fifo string column's dictionary grows without bound, because every * evicted row's value is dead the moment the ring wraps past it and nothing else reclaims it. * * Amortised over {@link STRING_COMPACTION_CHECK_INTERVAL} appended rows: asking on every append * would roughly double the boundary traffic of a single-point `appendN`, which is the path a * streaming `XyTextDataSeries` lives on. The interval bounds the deferral by a constant - see * that constant for why it cannot weaken the guarantee. * * @param appendedRows the number of rows the append that is calling this added * * @remarks Compaction MOVES dictionary codes, so it is deliberately driven from the append paths * - a point where nothing can be holding a code - rather than from a read. A caller that DOES * hold codes across a span (a live `oneOf` filter, whose selection `SCRTFilterSet` snapshots by * value) must bracket that span with {@link StringColumnStore.suspendCompaction} / * `resumeCompaction`, which makes this a no-op meanwhile. Nothing in this class can see filters, * and must not learn about the query layer to find out. */ private compactStringColumnsIfNeeded; createAnimationVectors(): void; /** * Sets initial values for the data animation * @param dataSeries The {@link BaseDataSeries} to be used for initial values */ setInitialAnimationVectors(dataSeries?: BaseDataSeries): void; /** * Sets final values for the data animation * @param dataSeries The {@link BaseDataSeries} to be used for final values */ setFinalAnimationVectors(dataSeries?: BaseDataSeries): void; /** * Puts the animation values back into the dataSeries after a reverse animation * @param dataSeries The {@link BaseDataSeries} to be used for target values */ revertAnimationVectors(dataSeries?: BaseDataSeries): void; /** * Validates the length of the animation vectors */ validateAnimationVectors(): void; /** * Updates the {@link BaseDataSeries} values for the animation * @param progress The animation progress from 0 to 1 * @param animation The animation */ updateAnimationProperties(progress: number, animation: SeriesAnimation): void; /** * Get if a named yValues array should be included when calculating yRange * @param name * @param isUsed */ isUsedForYRange(name: string): boolean; /** * Set if a named yValues array should be included when calculating yRange * @param name Usually an {@link EValueName} * @param isUsed */ setUsedForYRange(name: string, isUsed: boolean): void; /** @inheritDoc */ toJSON(excludeData?: boolean): TDataSeriesDefinition; /** @inheritDoc */ get changeCount(): number; protected getOptions(excludeData?: boolean): IBaseDataSeriesOptions; /** * Base behaviour is to return a wrapped pointSeries - ie no resampling. * @param rp * @param pointSeries * @param resamplerHelper * @returns */ toPointSeries(rp?: ResamplingParams, pointSeries?: IPointSeries, resamplerHelper?: ExtremeResamplerHelper): IPointSeries; /** * Finds the nearest index of the xValue passed in by performing binary or linear search on the X-Values array. * Returns -1 for index not found. Other negative numbers indicate an error condition * @param xValue the X-value to find * @param findMode the {@link ESearchMode} to use when searching. Defaults to {@link ESearchMode.Nearest}. * Mode {@link ESearchMode.Exact} will result in a slower search, other modes will result in fast binary search. * @return The index, or -1 if not found */ findIndex(xValue: number, searchMode?: ESearchMode): number; protected validateIndex(index: number, message?: string): void; protected setMetadataAt(index: number, metadata: T): void; protected appendMetadata(metadata: T): void; protected appendMetadataRange(metadata: T[], length: number): void; protected insertMetadata(startIndex: number, metadata: T): void; protected insertMetadataRange(startIndex: number, metadata: T[]): void; protected removeMetadataAt(index: number): void; protected removeMetadataRange(startIndex: number, count: number): void; protected setMetadata(value: T[]): void; getXValues(dataSeriesValueType: EDataSeriesValueType): SCRTDoubleVector; getYValuesArray(dataSeriesValueType: EDataSeriesValueType): SCRTDoubleVector[]; protected throwIfFifo(operation: string): void; protected reserve(size: number): void; protected calculateInitialCapacity(options: IBaseDataSeriesOptions): number; private fillMetadataIfUndefined; getYValues(dataSeriesValueType: EDataSeriesValueType, index?: number): SCRTDoubleVector; } /** @ignore */ export declare const getIndicesRange: (webAssemblyContext: TSciChart, xValues: SCRTDoubleVector, xRange: NumberRange, isSorted: boolean, downSearchMode?: ESearchMode, upSearchMode?: ESearchMode) => NumberRange; export declare const getWindowedYRange: (webAssemblyContext: TSciChart, xValues: SCRTDoubleVector, yValues: SCRTDoubleVector, xRange: NumberRange, getPositiveRange: boolean, isXCategoryAxis: boolean, isSorted: boolean, containsNaN: boolean, minSearchMode?: ESearchMode, maxSearchMode?: ESearchMode) => NumberRange;