import type { NumberArray } from "../../types/NumberArray"; import type { TSciChart } from "../../types/TSciChart"; import type { ExtremeResamplerHelper } from "../Numerics/Resamplers/ExtremeResamplerHelper"; import { EResamplingMode } from "../Numerics/Resamplers/ResamplingMode"; import type { ResamplingParams } from "../Numerics/Resamplers/ResamplingParams"; import type { IBaseDataSeriesOptions } from "./BaseDataSeries"; import { BaseDataSeries } from "./BaseDataSeries"; import { EColumnStorage, EColumnType } from "../../types/ColumnType"; import { EDataChangeType, EDataSeriesType } from "./IDataSeries"; import type { IPointSeries } from "./PointSeries/IPointSeries"; /** A named column of tabular data. String columns are dictionary-encoded into native storage; numeric columns are stored in WASM vectors. */ export interface ITableColumn { /** The name of the column. Used to address the column via {@link BaseDataSeries.getYValuesByName} (numeric) or {@link BaseDataSeries.getTextAt}/{@link BaseDataSeries.getStringValuesCopy} (string). */ name: string; /** * The column values. A numeric column is stored as a WASM vector; a string column is encoded into a * native dictionary-backed store (see {@link StringColumnStore}). * @remarks Numeric columns are copied into WASM and are not aliased. String-column arrays are also * copied on the way in (encoded into codes) — the passed array is not aliased and safe to mutate afterwards. */ values: NumberArray | string[]; /** * What the values MEAN — see {@link EColumnType}. Optional; when given it also DECIDES the * column's {@link EColumnStorage} via {@link storageForColumnType}, rather than the storage being * inferred from `typeof values[0]`. * @remarks That is what lets a string column be declared with `values: []`. Inference cannot see a * type in an empty array, so without a declared type an empty string column would be created as a * numeric one and every later text write would be lost — the reason consumers used to seed a * throwaway first row. * @remarks Declaring a type whose storage disagrees with the values supplied THROWS at construction. */ type?: EColumnType; } /** * Options to pass to the {@link TableDataSeries} constructor. * @remarks Two construction shapes are accepted. They are mutually exclusive and the constructor * THROWS if both `columns` and `yValuesArray` are supplied: * - **tabular**: pass {@link columns} (numeric and/or string), optionally with {@link xValues} as the * row positions. * - **columnar (the shape the deprecated `XyNDataSeries` name took)**: pass {@link yValuesArray} and/or * {@link arrayCount}, with {@link xValues}. */ export interface ITableDataSeriesOptions extends IBaseDataSeriesOptions { /** The columns of tabular data. All columns must have the same length (the row count). */ columns?: ITableColumn[]; /** Row positions on the X axis. Default: generated integer i per row */ xValues?: NumberArray; /** * The array of Y-value arrays to pre-populate the DataSeries with. An alternative to {@link columns} * for a numeric-only series; you must supply this or `arrayCount`. */ yValuesArray?: NumberArray[]; /** Per-column resampling modes, in `valueNames` order. Default: {@link EResamplingMode.Auto} for every column. */ resamplingModes?: EResamplingMode[]; } /** A single table row: column name → value. Numeric columns take number, string columns take string. */ export type TTableRow = Record; /** * @summary TableDataSeries holds tabular data — shared row positions (x) plus named value columns — * for rendering data grids with {@link FastTextRenderableSeries}, and any number of numeric Y columns * against one X for the multi-column renderable series, in SciChart's 2D * {@link https://www.scichart.com/javascript-chart-features | JavaScript Charts} * @description * Numeric columns are stored as WASM vectors, addressable via {@link BaseDataSeries.getYValuesByName}. * String columns are dictionary-encoded into native storage (an `SCRTIntVector` of per-row codes plus an * `SCRTStringDictionary` of distinct values) owned by {@link BaseDataSeries}, and addressable via * {@link BaseDataSeries.getTextAt} (single cell) or {@link BaseDataSeries.getStringValuesCopy} (whole-column copy). * One TableDataSeries is typically shared by one FastTextRenderableSeries per column, each selecting its * column via dataLabels.textField (and yArrayFilter for numeric positions if needed). * @remarks Rows can be added/inserted/removed after construction via the columnar * ({@link appendRange}/{@link insertRange}) or row-based ({@link appendRow}/{@link appendRows}/ * {@link insertRow}/{@link insertRows}) APIs, and removed via {@link removeAt}/{@link removeRange} * (or the {@link removeRow}/{@link removeRows} aliases). Row positions are kept as the compact * integer `i` sequence after every structural change. {@link setColumnValues} replaces a column's * values in place (row count unchanged). The numeric-only base mutators `appendN`/`appendRangeN`/ * `insertN`/`insertRangeN` are supported too: they leave the string columns' new cells empty. * @remarks Serialization via {@link getOptions} emits numeric columns first then string columns, so the * original declared column order is not preserved — column identity is by name, not position. * @remarks This type was called `GridDataSeries` while it was unreleased, and absorbs the released * `XyNDataSeries`, which is now a deprecated alias for it. */ export declare class TableDataSeries extends BaseDataSeries { /** @inheritDoc */ readonly type = EDataSeriesType.Table; protected readonly hasNumericColumns: boolean; /** * True when this series was constructed from a column list, i.e. its X is the ROW INDEX and the * row/columnar APIs own it. False for the columnar (deprecated `XyNDataSeries`) shape, where X is * real data supplied by the caller. * * The distinction is load-bearing, not cosmetic: {@link removeAt} / {@link removeRange} renumber * X to the compact integer sequence after a removal, which is the correct row semantics for a * table and would DESTROY the X values of a columnar series. */ protected readonly isTabular: boolean; protected resamplingModes: EResamplingMode[]; /** * Declared semantic type per column name, for the columns that declared one. Absent entries mean * the column declared no type — its storage was inferred and it has no formatting default. */ private readonly columnTypes; /** * The shared resampled buffers. One set of buffers holds every column, so N renderable series * bound to this data series with the same {@link ResamplingParams} are served by a single * resample rather than N of them. See {@link toPointSeries} for who is allowed to fill them. */ private pointSeries; /** * Hash of the {@link ResamplingParams} the shared {@link pointSeries} buffers currently hold, * or undefined when nothing has filled them since the last data change. */ private sharedResamplingHash; /** * Creates an instance of {@link TableDataSeries} * @param webAssemblyContext the {@link TSciChart | SciChart WebAssembly Context} containing native methods * and access to our underlying WebGL2 rendering engine * @param options the {@link ITableDataSeriesOptions} which configure the columns of the DataSeries at construct time */ constructor(webAssemblyContext: TSciChart, options: ITableDataSeriesOptions); /** * The declared semantic type of a column, or `undefined` when the column declared none (or the * name is unknown). See {@link EColumnType} and {@link ITableColumn.type}. * @param name the column name */ getColumnType(name: string): EColumnType | undefined; /** * How a column's values are physically stored, or `undefined` when no column of that name exists. * A declared {@link ITableColumn.type} decides this; otherwise it reflects how the column was * actually created from its values. See {@link EColumnStorage}. * @param name the column name */ getColumnStorage(name: string): EColumnStorage | undefined; /** * Rotates the freshly-filled fifo rings to the sweep phase `fifoStartIndex` records, which is * what makes a serialized {@link fifoSweeping} series restore to the sweep position it was saved * at rather than to 0. * * @remarks `getOptions` writes the rings out in RAW order (`vectorToArray` reads from * `dataPtrZero`) and emits `fifoStartIndex` alongside; appending that raw array into a fresh ring * reproduces the physical layout, and this reproduces the phase. Called from BOTH construction * shapes - it used to live only in the columnar one, and `getOptions` now always emits `columns`, * so a columnar series would otherwise have round-tripped its sweep phase away. */ private restoreFifoStartIndex; /** * @inheritDoc * @remarks Removes the row from all numeric and string columns. On a TABULAR series (constructed * from `columns`) it then renumbers row positions to the compact integer `i` sequence, because X * IS the row index there. On the columnar (deprecated `XyNDataSeries`) shape X is the caller's own * data, and renumbering would destroy it, so it is left alone. */ removeAt(index: number): void; /** * @inheritDoc * @remarks Removes the rows from all numeric and string columns. Renumbers row positions to the * compact integer `i` sequence on a TABULAR series only - see {@link removeAt}. */ removeRange(startIndex: number, count: number): void; /** Removes a single row by index (grid-friendly alias for {@link removeAt}). */ removeRow(index: number): void; /** Removes `count` rows starting at `startIndex` (grid-friendly alias for {@link removeRange}). */ removeRows(startIndex: number, count: number): void; /** * Deletes the table row at position `rowPosition` (the row whose X equals it), if present, then shifts * every remaining row above that position down by one. Unlike {@link removeAt}, it does NOT renumber to a * compact sequence, so a **sparse** column's gaps are preserved — this is the row-semantic delete (delete * "row N"), distinct from the array-index, compacting {@link removeAt}. For a dense series it is equivalent * to `removeAt(rowPosition)`. */ deleteRowAtPosition(rowPosition: number): void; /** Generates default row positions: integer row index i for each row */ static generateRowPositions(rowCount: number): Float64Array; /** Returns the values of a column (string columns materialized as a copy, numeric columns copied from WASM) */ getColumnValues(name: string): readonly string[] | number[] | undefined; /** * Returns the value of column `name` at the row whose X (row position) exactly equals `x`, or * `undefined` if no row sits at that position. * @remarks Locates the value by X (row position) rather than by array index — using the * {@link findIndex} numeric helper with {@link ESearchMode.Exact}. This is what lets columns of * different lengths share one row axis: a column can be queried for a given row position even when * it has no cell there (returns `undefined`). String columns return a decoded `string`, numeric * columns a `number`. An unknown column name also returns `undefined`. */ getValueAtX(name: string, x: number): number | string | undefined; /** * Replaces a column's values in place (row count must be unchanged). * Any changes of the DataSeries will trigger a redraw on the parent {@link SciChartSurface} * @param name The name of the column to replace * @param values The new column values. Must match the existing row count. * @remarks String-column values are re-encoded into the native dictionary-backed store — the passed * array is not aliased and is safe to mutate afterwards. Numeric columns are copied into WASM and are * not aliased either. */ setColumnValues(name: string, values: NumberArray | string[]): void; /** Rewrites native row positions to the compact integer sequence, from `fromIndex` to the end. */ private renumberRowPositions; /** User-facing column names: numeric columns (in valueNames order, if any) then string columns. */ private columnNames; /** * Validates a columnar batch (every declared column present, equal lengths, correct value type per * column) and returns the number of rows it represents. */ private validateColumnar; /** Transposes row records into a columnar map keyed by column name. Throws if a row omits a column. */ private rowsToColumns; /** Builds the numeric yValuesArray (in valueNames order) for the base mutators. String-only series get one placeholder array. */ private buildNumericArrays; /** * Writes the string values of a just-appended or just-inserted batch over the empty cells the base * mutator laid down for them. * @param columns the batch, keyed by column name * @param startIndex the LOGICAL index of the first row of the batch * @param rowCount how many rows the batch carried * @remarks On a fifo series the ring may have dropped the front of the batch before this runs, so * only the rows that are still addressable are written — `startIndex` is clamped to 0 and the * matching prefix of each column's values is skipped. */ private writeStringCells; /** * Appends rows supplied column-wise (SciChart-native / Arrow-style): column name → values array. * Every declared column must be present with equal lengths; numeric columns take a {@link NumberArray}, * string columns take a `string[]`. Row positions extend the compact integer `i` sequence. * @remarks String-column arrays are appended by value (copied element-by-element), so the passed * arrays are not aliased. */ appendRange(columns: Record): void; /** Appends a single row (row-based / SQL-style). Delegates to {@link appendRange}. */ appendRow(row: TTableRow): void; /** Appends rows (row-based / SQL-style); transposes to columnar and delegates to {@link appendRange}. */ appendRows(rows: TTableRow[]): void; /** * Inserts rows supplied column-wise at a row index (must be in `[0, count()-1]`; to add at the end use * {@link appendRange}). Validation matches {@link appendRange}. Tail positions are renumbered to the * compact integer `i` sequence. */ insertRange(index: number, columns: Record): void; /** Inserts a single row (row-based) at an index. Delegates to {@link insertRange}. */ insertRow(index: number, row: TTableRow): void; /** Inserts rows (row-based) at an index; transposes to columnar and delegates to {@link insertRange}. */ insertRows(index: number, rows: TTableRow[]): void; /** @inheritDoc */ notifyDataChanged(changeType: EDataChangeType, index: number, count: number, name?: string): void; /** @inheritDoc */ toPointSeries(rp?: ResamplingParams, _pointSeries?: IPointSeries, resamplerHelper?: ExtremeResamplerHelper): IPointSeries; private createPointSeries; /** * Returns the point series a consumer passed back in if it is a set of resampled buffers this * data series can fill, else undefined (e.g. the consumer was previously bound to a data series * of another type, or with a different number of columns). */ private asCompatiblePointSeries; /** * @inheritDoc * @remarks The serialized `columns` array emits numeric columns first then string columns, so the * original declared column order is not preserved — column identity is by name, not position. * @remarks Serializes back to the SHAPE it was constructed in - `columns` for a tabular series, * `xValues`/`yValuesArray` for the columnar (deprecated `XyNDataSeries`) one - because `columns` * is what the constructor keys {@link isTabular} off, and that decides whether a removal * renumbers X. * @remarks Each emitted column carries its declared {@link ITableColumn.type} (omitted when it * declared none), so the semantic type - and with it the storage decision it drives - survives the * round trip. * @remarks A columnar series carrying string columns emits their NAMES (via the base's * `stringColumns`) but not their values, because the columnar option shape has nowhere to put * them. Unchanged from the pre-rename `XyNDataSeries`; declare the series with `columns` if the * text has to round-trip. */ protected getOptions(excludeData?: boolean): ITableDataSeriesOptions; } /** Registers {@link TableDataSeries} for `EDataSeriesType.Table` definitions. */ export declare function registerTableDataSeries(overWrite?: boolean): void;