This is an automated email from the ASF dual-hosted git repository. shenyi pushed a commit to branch list-remove-chunk in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit 41988f298f6931891cf8f40d9d718f1dda87caef Author: pissang <bm2736...@gmail.com> AuthorDate: Thu Sep 24 13:12:50 2020 +0800 refact: remove chunks in List. --- src/data/List.ts | 448 ++++++++++++++++------------------------ src/data/helper/dataProvider.ts | 46 ++++- 2 files changed, 225 insertions(+), 269 deletions(-) diff --git a/src/data/List.ts b/src/data/List.ts index 4298f9e..d13ae70 100644 --- a/src/data/List.ts +++ b/src/data/List.ts @@ -47,6 +47,7 @@ import { isSourceInstance } from './Source'; const mathFloor = Math.floor; const isObject = zrUtil.isObject; +const map = zrUtil.map; const UNDEFINED = 'undefined'; const INDEX_NOT_FOUND = -1; @@ -88,7 +89,7 @@ type DimValueGetter = ( ) => ParsedValue; type DataValueChunk = ArrayLike<ParsedValue>; -type DataStorage = {[dimName: string]: DataValueChunk[]}; +type DataStorage = {[dimName: string]: DataValueChunk}; type NameRepeatCount = {[name: string]: number}; @@ -161,8 +162,8 @@ let defaultDimValueGetters: {[sourceFormat: string]: DimValueGetter}; let prepareInvertedIndex: (list: List) => void; let getRawValueFromStore: (list: List, dimIndex: number, rawIndex: number) => ParsedValue | OrdinalRawValue; let getIndicesCtor: (list: List) => DataArrayLikeConstructor; -let prepareChunks: ( - storage: DataStorage, dimInfo: DataDimensionInfo, chunkSize: number, chunkCount: number, end: number +let prepareStorage: ( + storage: DataStorage, dimInfo: DataDimensionInfo, end: number, append?: boolean ) => void; let getRawIndexWithoutIndices: (this: List, idx: number) => number; let getRawIndexWithIndices: (this: List, idx: number) => number; @@ -170,7 +171,6 @@ let getId: (list: List, rawIndex: number) => string; let normalizeDimensions: (dimensions: ItrParamDims) => Array<DimensionLoose>; let validateDimensions: (list: List, dims: DimensionName[]) => void; let cloneListForMapAndSample: (original: List, excludeDimensions: DimensionName[]) => List; -let cloneDimStore: (originalDimStore: DataValueChunk[]) => DataValueChunk[]; let getInitialExtent: () => [number, number]; let setItemDataAndSeriesIndex: (this: Element, child: Element) => void; let transferProperties: (target: List, source: List) => void; @@ -213,7 +213,7 @@ class List< private _count: number = 0; private _rawCount: number = 0; private _storage: DataStorage = {}; - private _storageArr: DataValueChunk[][] = []; + private _storageArr: DataValueChunk[] = []; private _nameList: string[] = []; private _idList: string[] = []; @@ -236,11 +236,6 @@ class List< // Graphic elemnents private _graphicEls: Element[] = []; - // Max size of each chunk. - private _chunkSize: number = 1e5; - - private _chunkCount: number = 0; - private _rawData: DataProvider; // Raw extent will not be cloned, but only transfered. @@ -446,7 +441,6 @@ class List< // Clear this._storage = {}; - this._storageArr = []; this._indices = null; this._nameList = nameList || []; @@ -516,48 +510,40 @@ class List< * Each item is exaclty cooresponding to a dimension. */ appendValues(values: any[][], names?: string[]): void { - const chunkSize = this._chunkSize; const storage = this._storage; - const storageArr = this._storageArr; const dimensions = this.dimensions; const dimLen = dimensions.length; const rawExtent = this._rawExtent; const start = this.count(); const end = start + Math.max(values.length, names ? names.length : 0); - const originalChunkCount = this._chunkCount; for (let i = 0; i < dimLen; i++) { const dim = dimensions[i]; if (!rawExtent[dim]) { rawExtent[dim] = getInitialExtent(); } - if (!storage[dim]) { - const store: DataValueChunk[] = []; - storage[dim] = store; - storageArr.push(store); - } - prepareChunks(storage, this._dimensionInfos[dim], chunkSize, originalChunkCount, end); - this._chunkCount = storage[dim].length; + prepareStorage(storage, this._dimensionInfos[dim], end, true); } - const rawExtentArr = zrUtil.map(dimensions, (dim) => { + const rawExtentArr = map(dimensions, (dim) => { return rawExtent[dim]; }); - const emptyDataItem = new Array(dimLen); + const storageArr = this._storageArr = map(dimensions, (dim) => { + return storage[dim]; + }); + + const emptyDataItem: number[] = []; for (let idx = start; idx < end; idx++) { const sourceIdx = idx - start; - const chunkIndex = mathFloor(idx / chunkSize); - const chunkOffset = idx % chunkSize; - // Store the data by dimensions for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) { const dim = dimensions[dimIdx]; const val = this._dimValueGetterArrayRows( values[sourceIdx] || emptyDataItem, dim, sourceIdx, dimIdx ) as ParsedValueNumeric; - storageArr[dimIdx][chunkIndex][chunkOffset] = val; + storageArr[dimIdx][idx] = val; const dimRawExtent = rawExtentArr[dimIdx]; val < dimRawExtent[0] && (dimRawExtent[0] = val); @@ -582,10 +568,8 @@ class List< return; } - const chunkSize = this._chunkSize; const rawData = this._rawData; const storage = this._storage; - const storageArr = this._storageArr; const dimensions = this.dimensions; const dimLen = dimensions.length; const dimensionInfoMap = this._dimensionInfos; @@ -595,8 +579,6 @@ class List< const nameRepeatCount: NameRepeatCount = this._nameRepeatCount = {}; let nameDimIdx; - const originalChunkCount = this._chunkCount; - for (let i = 0; i < dimLen; i++) { const dim = dimensions[i]; if (!rawExtent[dim]) { @@ -611,92 +593,99 @@ class List< this._idDimIdx = i; } - if (!storage[dim]) { - const store: DataValueChunk[] = []; - storage[dim] = store; - storageArr.push(store); + if (!rawData.getStorage) { + prepareStorage(storage, dimInfo, end); } - - prepareChunks(storage, dimInfo, chunkSize, originalChunkCount, end); - - this._chunkCount = storage[dim].length; } - const rawExtentArr = zrUtil.map(dimensions, (dim) => { - return rawExtent[dim]; + const storageArr = this._storageArr = map(dimensions, (dim) => { + return storage[dim]; }); - let dataItem = [] as OptionDataItem; - for (let idx = start; idx < end; idx++) { - // NOTICE: Try not to write things into dataItem - dataItem = rawData.getItem(idx, dataItem); - // Each data item is value - // [1, 2] - // 2 - // Bar chart, line chart which uses category axis - // only gives the 'y' value. 'x' value is the indices of category - // Use a tempValue to normalize the value to be a (x, y) value - const chunkIndex = mathFloor(idx / chunkSize); - const chunkOffset = idx % chunkSize; - - // Store the data by dimensions - for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) { - const dim = dimensions[dimIdx]; - const dimStorage = storageArr[dimIdx][chunkIndex]; - // PENDING NULL is empty or zero - const val = this._dimValueGetter(dataItem, dim, idx, dimIdx) as ParsedValueNumeric; - dimStorage[chunkOffset] = val; + const rawExtentArr = map(dimensions, (dim) => { + return rawExtent[dim]; + }); - const dimRawExtent = rawExtentArr[dimIdx]; - val < dimRawExtent[0] && (dimRawExtent[0] = val); - val > dimRawExtent[1] && (dimRawExtent[1] = val); + if (rawData.getStorage) { + const ret = rawData.getStorage(start, end); + const rawStorage = ret.storage; + const extent = ret.extent; + for (let dimIdx = 0; dimIdx < rawStorage.length; dimIdx++) { + storage[dimensions[dimIdx]] = storageArr[dimIdx] = rawStorage[dimIdx]; + rawExtentArr[dimIdx][0] = Math.min(rawExtentArr[dimIdx][0], extent[dimIdx][0]); + rawExtentArr[dimIdx][1] = Math.max(rawExtentArr[dimIdx][1], extent[dimIdx][1]); } + } + else { + let dataItem = [] as OptionDataItem; + for (let idx = start; idx < end; idx++) { + // NOTICE: Try not to write things into dataItem + dataItem = rawData.getItem(idx, dataItem); + // Each data item is value + // [1, 2] + // 2 + // Bar chart, line chart which uses category axis + // only gives the 'y' value. 'x' value is the indices of category + // Use a tempValue to normalize the value to be a (x, y) value + + // Store the data by dimensions + for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) { + const dim = dimensions[dimIdx]; + const dimStorage = storageArr[dimIdx]; + // PENDING NULL is empty or zero + const val = this._dimValueGetter(dataItem, dim, idx, dimIdx) as ParsedValueNumeric; + dimStorage[idx] = val; + + const dimRawExtent = rawExtentArr[dimIdx]; + val < dimRawExtent[0] && (dimRawExtent[0] = val); + val > dimRawExtent[1] && (dimRawExtent[1] = val); + } - // ??? FIXME not check by pure but sourceFormat? - // TODO refactor these logic. - if (!rawData.pure) { - let name: string = nameList[idx]; - - if (dataItem && name == null) { - // If dataItem is {name: ...}, it has highest priority. - // That is appropriate for many common cases. - if ((dataItem as any).name != null) { - // There is no other place to persistent dataItem.name, - // so save it to nameList. - nameList[idx] = name = convertOptionIdName((dataItem as any).name, null); - } - else if (nameDimIdx != null) { - const nameDim = dimensions[nameDimIdx]; - const nameDimChunk = storage[nameDim][chunkIndex]; - if (nameDimChunk) { - const ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta; - name = convertOptionIdName( - (ordinalMeta && ordinalMeta.categories.length) - ? ordinalMeta.categories[nameDimChunk[chunkOffset] as number] - : nameDimChunk[chunkOffset], - null - ); + // ??? FIXME not check by pure but sourceFormat? + // TODO refactor these logic. + if (!rawData.pure) { + let name: string = nameList[idx]; + + if (dataItem && name == null) { + // If dataItem is {name: ...}, it has highest priority. + // That is appropriate for many common cases. + if ((dataItem as any).name != null) { + // There is no other place to persistent dataItem.name, + // so save it to nameList. + nameList[idx] = name = convertOptionIdName((dataItem as any).name, null); + } + else if (nameDimIdx != null) { + const nameDim = dimensions[nameDimIdx]; + const nameDimChunk = storage[nameDim]; + if (nameDimChunk) { + const ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta; + name = convertOptionIdName( + (ordinalMeta && ordinalMeta.categories.length) + ? ordinalMeta.categories[nameDimChunk[idx] as number] + : nameDimChunk[idx], + null + ); + } } } - } - // Try using the id in option - // id or name is used on dynamical data, mapping old and new items. - let id: string = dataItem == null ? null : convertOptionIdName((dataItem as any).id, null); + // Try using the id in option + // id or name is used on dynamical data, mapping old and new items. + let id: string = dataItem == null ? null : convertOptionIdName((dataItem as any).id, null); - if (id == null && name != null) { - // Use name as id and add counter to avoid same name - nameRepeatCount[name] = nameRepeatCount[name] || 0; - id = name; - if (nameRepeatCount[name] > 0) { - id += '__ec__' + nameRepeatCount[name]; + if (id == null && name != null) { + // Use name as id and add counter to avoid same name + nameRepeatCount[name] = nameRepeatCount[name] || 0; + id = name; + if (nameRepeatCount[name] > 0) { + id += '__ec__' + nameRepeatCount[name]; + } + nameRepeatCount[name]++; } - nameRepeatCount[name]++; + id != null && (idList[idx] = id); } - id != null && (idList[idx] = id); } } - if (!rawData.persistent && rawData.clean) { // Clean unused data if data source is typed array. rawData.clean(); @@ -752,18 +741,8 @@ class List< if (!(idx >= 0 && idx < this._count)) { return NaN; } - const dimStore = this._storageArr[dimIdx]; - const chunkSize = this._chunkSize; - if (!dimStore) { - return NaN; - } - idx = this.getRawIndex(idx); - - const chunkIndex = mathFloor(idx / chunkSize); - const chunkOffset = idx % chunkSize; - - return dimStore[chunkIndex][chunkOffset]; + return dimStore ? dimStore[this.getRawIndex(idx)] : NaN; } /** @@ -775,17 +754,7 @@ class List< return NaN; } const dimStore = this._storage[dim]; - const chunkSize = this._chunkSize; - if (!dimStore) { - return NaN; - } - - idx = this.getRawIndex(idx); - - const chunkIndex = mathFloor(idx / chunkSize); - const chunkOffset = idx % chunkSize; - - return dimStore[chunkIndex][chunkOffset]; + return dimStore ? dimStore[this.getRawIndex(idx)] : NaN; } /** @@ -796,27 +765,7 @@ class List< return NaN; } const dimStore = this._storage[dim]; - const chunkSize = this._chunkSize; - if (!dimStore) { - // TODO Warn ? - return NaN; - } - - const chunkIndex = mathFloor(rawIdx / chunkSize); - const chunkOffset = rawIdx % chunkSize; - const chunkStore = dimStore[chunkIndex]; - return chunkStore[chunkOffset]; - } - - /** - * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange). - * Hack a much simpler _getFast - */ - private _getFast(dimIdx: number, rawIdx: number): ParsedValue { - const chunkSize = this._chunkSize; - const chunkIndex = mathFloor(rawIdx / chunkSize); - const chunkOffset = rawIdx % chunkSize; - return this._storageArr[dimIdx][chunkIndex][chunkOffset]; + return dimStore ? dimStore[rawIdx] : NaN; } /** @@ -866,7 +815,6 @@ class List< dim = this.getDimension(dim); const dimData = this._storage[dim]; const initialExtent = getInitialExtent(); - const chunkSize = this._chunkSize; // stack = !!((stack || false) && this.getCalculationInfo(dim)); @@ -899,9 +847,7 @@ class List< for (let i = 0; i < currEnd; i++) { const rawIdx = this.getRawIndex(i); - const chunkIndex = mathFloor(rawIdx / chunkSize); - const chunkOffset = rawIdx % chunkSize; - const value = dimData[chunkIndex][chunkOffset] as ParsedValueNumeric; + const value = dimData[rawIdx] as ParsedValueNumeric; value < min && (min = value); value > max && (max = value); } @@ -1120,7 +1066,6 @@ class List< const storage = this._storage; const dimData = storage[dim]; const nearestIndices: number[] = []; - const chunkSize = this._chunkSize; if (!dimData) { return nearestIndices; @@ -1138,9 +1083,7 @@ class List< // Check the test case of `test/ut/spec/data/List.js`. for (let i = 0, len = this.count(); i < len; i++) { const dataIndex = this.getRawIndex(i); - const chunkIndex = mathFloor(dataIndex / chunkSize); - const chunkOffset = dataIndex % chunkSize; - const diff = value - (dimData[chunkIndex][chunkOffset] as number); + const diff = value - (dimData[dataIndex] as number); const dist = Math.abs(diff); if (dist <= maxDistance) { // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`, @@ -1248,36 +1191,37 @@ class List< // ctxCompat just for compat echarts3 const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>; - const dimNames = zrUtil.map(normalizeDimensions(dims), this.getDimension, this); + const dimNames = map(normalizeDimensions(dims), this.getDimension, this); if (__DEV__) { validateDimensions(this, dimNames); } const dimSize = dimNames.length; - const dimIndices = zrUtil.map(dimNames, (dimName) => { + const dimIndices = map(dimNames, (dimName) => { return this._dimensionInfos[dimName].index; }); + const storageArr = this._storageArr; - for (let i = 0; i < this.count(); i++) { + for (let i = 0, len = this.count(); i < len; i++) { // Simple optimization switch (dimSize) { case 0: (cb as EachCb0<Ctx>).call(fCtx, i); break; case 1: - (cb as EachCb1<Ctx>).call(fCtx, this._getFast(dimIndices[0], i), i); + (cb as EachCb1<Ctx>).call(fCtx, storageArr[dimIndices[0]][i], i); break; case 2: (cb as EachCb2<Ctx>).call( - fCtx, this._getFast(dimIndices[0], i), this._getFast(dimIndices[1], i), i + fCtx, storageArr[dimIndices[0]][i], storageArr[dimIndices[1]][i], i ); break; default: let k = 0; const value = []; for (; k < dimSize; k++) { - value[k] = this._getFast(dimIndices[k], i); + value[k] = storageArr[dimIndices[k]][i]; } // Index value[k] = i; @@ -1316,7 +1260,7 @@ class List< // ctxCompat just for compat echarts3 const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>; - const dimNames = zrUtil.map( + const dimNames = map( normalizeDimensions(dims), this.getDimension, this ); @@ -1332,10 +1276,11 @@ class List< const dimSize = dimNames.length; let offset = 0; - const dimIndices = zrUtil.map(dimNames, (dimName) => { + const dimIndices = map(dimNames, (dimName) => { return this._dimensionInfos[dimName].index; }); const dim0 = dimIndices[0]; + const storageArr = this._storageArr; for (let i = 0; i < count; i++) { let keep; @@ -1345,13 +1290,13 @@ class List< keep = (cb as FilterCb0<Ctx>).call(fCtx, i); } else if (dimSize === 1) { - const val = this._getFast(dim0, rawIdx); + const val = storageArr[dim0][rawIdx]; keep = (cb as FilterCb1<Ctx>).call(fCtx, val, i); } else { let k = 0; for (; k < dimSize; k++) { - value[k] = this._getFast(dimIndices[k], rawIdx); + value[k] = storageArr[dimIndices[k]][rawIdx]; } value[k] = i; keep = (cb as FilterCb<Ctx>).apply(fCtx, value); @@ -1381,7 +1326,9 @@ class List< selectRange(range: {[dimName: string]: [number, number]}): List { 'use strict'; - if (!this._count) { + const len = this._count; + + if (!len) { return; } @@ -1407,63 +1354,55 @@ class List< let offset = 0; const dim0 = dimensions[0]; - const dimIndices = zrUtil.map(dimensions, (dimName) => { + const dimIndices = map(dimensions, (dimName) => { return this._dimensionInfos[dimName].index; }); const min = range[dim0][0]; const max = range[dim0][1]; + const storageArr = this._storageArr; let quickFinished = false; if (!this._indices) { // Extreme optimization for common case. About 2x faster in chrome. let idx = 0; if (dimSize === 1) { - const dimStorage = this._storage[dim0]; - for (let k = 0; k < this._chunkCount; k++) { - const chunkStorage = dimStorage[k]; - const len = Math.min(this._count - k * this._chunkSize, this._chunkSize); - for (let i = 0; i < len; i++) { - const val = chunkStorage[i]; - // NaN will not be filtered. Consider the case, in line chart, empty - // value indicates the line should be broken. But for the case like - // scatter plot, a data item with empty value will not be rendered, - // but the axis extent may be effected if some other dim of the data - // item has value. Fortunately it is not a significant negative effect. - if ( - (val >= min && val <= max) || isNaN(val as any) - ) { - newIndices[offset++] = idx; - } - idx++; + const dimStorage = storageArr[dimIndices[0]]; + for (let i = 0; i < len; i++) { + const val = dimStorage[i]; + // NaN will not be filtered. Consider the case, in line chart, empty + // value indicates the line should be broken. But for the case like + // scatter plot, a data item with empty value will not be rendered, + // but the axis extent may be effected if some other dim of the data + // item has value. Fortunately it is not a significant negative effect. + if ( + (val >= min && val <= max) || isNaN(val as any) + ) { + newIndices[offset++] = idx; } + idx++; } quickFinished = true; } else if (dimSize === 2) { - const dimStorage = this._storage[dim0]; - const dimStorage2 = this._storage[dimensions[1]]; + const dimStorage = storageArr[dimIndices[0]]; + const dimStorage2 = storageArr[dimIndices[1]]; const min2 = range[dimensions[1]][0]; const max2 = range[dimensions[1]][1]; - for (let k = 0; k < this._chunkCount; k++) { - const chunkStorage = dimStorage[k]; - const chunkStorage2 = dimStorage2[k]; - const len = Math.min(this._count - k * this._chunkSize, this._chunkSize); - for (let i = 0; i < len; i++) { - const val = chunkStorage[i]; - const val2 = chunkStorage2[i]; - // Do not filter NaN, see comment above. - if (( - (val >= min && val <= max) || isNaN(val as any) - ) - && ( - (val2 >= min2 && val2 <= max2) || isNaN(val2 as any) - ) - ) { - newIndices[offset++] = idx; - } - idx++; + for (let i = 0; i < len; i++) { + const val = dimStorage[i]; + const val2 = dimStorage2[i]; + // Do not filter NaN, see comment above. + if (( + (val >= min && val <= max) || isNaN(val as any) + ) + && ( + (val2 >= min2 && val2 <= max2) || isNaN(val2 as any) + ) + ) { + newIndices[offset++] = idx; } + idx++; } quickFinished = true; } @@ -1472,7 +1411,7 @@ class List< if (dimSize === 1) { for (let i = 0; i < originalCount; i++) { const rawIndex = this.getRawIndex(i); - const val = this._getFast(dimIndices[0], rawIndex); + const val = storageArr[dimIndices[0]][rawIndex]; // Do not filter NaN, see comment above. if ( (val >= min && val <= max) || isNaN(val as any) @@ -1487,7 +1426,7 @@ class List< const rawIndex = this.getRawIndex(i); for (let k = 0; k < dimSize; k++) { const dimk = dimensions[k]; - const val = this._getFast(dimIndices[k], rawIndex); + const val = storageArr[dimIndices[k]][rawIndex]; // Do not filter NaN, see comment above. if (val < range[dimk][0] || val > range[dimk][1]) { keep = false; @@ -1565,7 +1504,7 @@ class List< // ctxCompat just for compat echarts3 const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>; - const dimNames = zrUtil.map( + const dimNames = map( normalizeDimensions(dims), this.getDimension, this ); @@ -1574,16 +1513,14 @@ class List< } const list = cloneListForMapAndSample(this, dimNames); + const storage = list._storage; // Following properties are all immutable. // So we can reference to the same value list._indices = this._indices; list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices; - const storage = list._storage; - const tmpRetValue = []; - const chunkSize = this._chunkSize; const dimSize = dimNames.length; const dataCount = this.count(); const values = []; @@ -1604,8 +1541,6 @@ class List< } const rawIndex = this.getRawIndex(dataIndex); - const chunkIndex = mathFloor(rawIndex / chunkSize); - const chunkOffset = rawIndex % chunkSize; for (let i = 0; i < retValue.length; i++) { const dim = dimNames[i]; @@ -1614,7 +1549,7 @@ class List< const dimStore = storage[dim]; if (dimStore) { - dimStore[chunkIndex][chunkOffset] = val; + dimStore[rawIndex] = val; } if (val < rawExtentOnDim[0]) { @@ -1648,7 +1583,6 @@ class List< const dimStore = targetStorage[dimension]; const len = this.count(); - const chunkSize = this._chunkSize; const rawExtentOnDim = list._rawExtent[dimension]; const newIndices = new (getIndicesCtor(this))(len); @@ -1662,18 +1596,14 @@ class List< } for (let k = 0; k < frameSize; k++) { const dataIdx = this.getRawIndex(i + k); - const originalChunkIndex = mathFloor(dataIdx / chunkSize); - const originalChunkOffset = dataIdx % chunkSize; - frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset]; + frameValues[k] = dimStore[dataIdx]; } const value = sampleValue(frameValues); const sampleFrameIdx = this.getRawIndex( Math.min(i + sampleIndex(frameValues, value) || 0, len - 1) ); - const sampleChunkIndex = mathFloor(sampleFrameIdx / chunkSize); - const sampleChunkOffset = sampleFrameIdx % chunkSize; // Only write value on the filtered data - dimStore[sampleChunkIndex][sampleChunkOffset] = value; + dimStore[sampleFrameIdx] = value; if (value < rawExtentOnDim[0]) { rawExtentOnDim[0] = value; @@ -1704,12 +1634,11 @@ class List< valueDimension: DimensionName, targetCount: number ) { - const list = cloneListForMapAndSample(this, [baseDimension, valueDimension]); + const list = cloneListForMapAndSample(this, []); const targetStorage = list._storage; const baseDimStore = targetStorage[baseDimension]; const valueDimStore = targetStorage[valueDimension]; const len = this.count(); - const chunkSize = this._chunkSize; const newIndices = new (getIndicesCtor(this))(len); let sampledIndex = 0; @@ -1732,15 +1661,13 @@ class List< for (let idx = avgRangeStart; idx < avgRangeEnd; idx++) { const rawIndex = this.getRawIndex(idx); - const chunkIndex = mathFloor(rawIndex / chunkSize); - const chunkOffset = rawIndex % chunkSize; - const x = baseDimStore[chunkIndex][chunkOffset] as number; - const y = valueDimStore[chunkIndex][chunkOffset] as number; + const x = baseDimStore[rawIndex] as number; + const y = valueDimStore[rawIndex] as number; if (isNaN(x) || isNaN(y)) { continue; } - avgX += baseDimStore[chunkIndex][chunkOffset] as number; - avgY += valueDimStore[chunkIndex][chunkOffset] as number; + avgX += x as number; + avgY += y as number; } avgX /= avgRangeLength; avgY /= avgRangeLength; @@ -1748,21 +1675,17 @@ class List< const rangeOffs = mathFloor((i) * frameSize) + 1; const rangeTo = mathFloor((i + 1) * frameSize) + 1; - const chunkIndex = mathFloor(currentRawIndex / chunkSize); - const chunkOffset = currentRawIndex % chunkSize; - const pointAX = baseDimStore[chunkIndex][chunkOffset] as number; - const pointAY = valueDimStore[chunkIndex][chunkOffset] as number; + const pointAX = baseDimStore[currentRawIndex] as number; + const pointAY = valueDimStore[currentRawIndex] as number; - maxArea = area = -1; + maxArea = -1; // Find a point from current frame that construct a triangel with largest area with previous selected point // And the average of next frame. for (let idx = rangeOffs; idx < rangeTo; idx++) { const rawIndex = this.getRawIndex(idx); - const chunkIndex = mathFloor(rawIndex / chunkSize); - const chunkOffset = rawIndex % chunkSize; - const x = baseDimStore[chunkIndex][chunkOffset] as number; - const y = valueDimStore[chunkIndex][chunkOffset] as number; + const x = baseDimStore[rawIndex] as number; + const y = valueDimStore[rawIndex] as number; if (isNaN(x) || isNaN(y)) { continue; } @@ -2023,7 +1946,7 @@ class List< */ cloneShallow(list?: List<HostModel>): List<HostModel> { if (!list) { - const dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this); + const dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this); list = new List(dimensionInfoList, this.hostModel); } @@ -2157,13 +2080,10 @@ class List< ): ParsedValue | OrdinalRawValue { let val; if (dimIndex != null) { - const chunkSize = list._chunkSize; - const chunkIndex = mathFloor(rawIndex / chunkSize); - const chunkOffset = rawIndex % chunkSize; const dim = list.dimensions[dimIndex]; - const chunk = list._storage[dim][chunkIndex]; + const chunk = list._storage[dim]; if (chunk) { - val = chunk[chunkOffset]; + val = chunk[rawIndex]; const ordinalMeta = list._dimensionInfos[dim].ordinalMeta; if (ordinalMeta && ordinalMeta.categories.length) { val = ordinalMeta.categories[val as OrdinalNumber]; @@ -2178,30 +2098,30 @@ class List< return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array; }; - prepareChunks = function ( + prepareStorage = function ( storage: DataStorage, dimInfo: DataDimensionInfo, - chunkSize: number, - chunkCount: number, - end: number + end: number, + append?: boolean ): void { const DataCtor = dataCtors[dimInfo.type]; - const lastChunkIndex = chunkCount - 1; const dim = dimInfo.name; - const resizeChunkArray = storage[dim][lastChunkIndex]; - if (resizeChunkArray && resizeChunkArray.length < chunkSize) { - const newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize)); - // The cost of the copy is probably inconsiderable - // within the initial chunkSize. - for (let j = 0; j < resizeChunkArray.length; j++) { - newStore[j] = resizeChunkArray[j]; + + if (append) { + const oldStore = storage[dim]; + const oldLen = oldStore.length; + if (oldStore && oldLen < end) { + const newStore = new DataCtor(end); + // The cost of the copy is probably inconsiderable + // within the initial chunkSize. + for (let j = 0; j < oldLen; j++) { + newStore[j] = oldStore[j]; + } + storage[dim] = newStore; } - storage[dim][lastChunkIndex] = newStore; } - - // Create new chunks. - for (let k = chunkCount * chunkSize; k < end; k += chunkSize) { - storage[dim].push(new DataCtor(Math.min(end - k, chunkSize))); + else { + storage[dim] = new DataCtor(end); } }; @@ -2256,7 +2176,7 @@ class List< ): List { const allDimensions = original.dimensions; const list = new List( - zrUtil.map(allDimensions, original.getDimensionInfo, original), + map(allDimensions, original.getDimensionInfo, original), original.hostModel ); // FIXME If needs stackedOn, value may already been stacked @@ -2264,7 +2184,7 @@ class List< const storage = list._storage = {} as DataStorage; const originalStorage = original._storage; - const storageArr: DataValueChunk[][] = list._storageArr = []; + const storageArr: DataValueChunk[] = list._storageArr = []; // Init storage for (let i = 0; i < allDimensions.length; i++) { @@ -2273,7 +2193,7 @@ class List< // Notice that we do not reset invertedIndicesMap here, becuase // there is no scenario of mapping or sampling ordinal dimension. if (zrUtil.indexOf(excludeDimensions, dim) >= 0) { - storage[dim] = cloneDimStore(originalStorage[dim]); + storage[dim] = cloneChunk(originalStorage[dim]); list._rawExtent[dim] = getInitialExtent(); list._extent[dim] = null; } @@ -2287,14 +2207,6 @@ class List< return list; }; - cloneDimStore = function (originalDimStore: DataValueChunk[]): DataValueChunk[] { - const newDimStore = new Array(originalDimStore.length); - for (let j = 0; j < originalDimStore.length; j++) { - newDimStore[j] = cloneChunk(originalDimStore[j]); - } - return newDimStore; - }; - function cloneChunk(originalChunk: DataValueChunk): DataValueChunk { const Ctor = originalChunk.constructor; // Only shallow clone is enough when Array. diff --git a/src/data/helper/dataProvider.ts b/src/data/helper/dataProvider.ts index b3c5290..5f48956 100644 --- a/src/data/helper/dataProvider.ts +++ b/src/data/helper/dataProvider.ts @@ -38,7 +38,6 @@ import { } from '../../util/types'; import List from '../List'; - export interface DataProvider { // If data is pure without style configuration pure: boolean; @@ -48,6 +47,10 @@ export interface DataProvider { getSource(): Source; count(): number; getItem(idx: number, out?: OptionDataItem): OptionDataItem; + getStorage?(start: number, end: number): { + storage: ArrayLike<number>[] + extent: number[][] + } appendData(newData: ArrayLike<OptionDataItem>): void; clean(): void; } @@ -56,6 +59,12 @@ export interface DataProvider { let providerMethods: Dictionary<any>; let mountMethods: (provider: DefaultDataProvider, data: OptionSourceData, source: Source) => void; +export interface DefaultDataProvider { + getStorage?(start: number, end: number): { + storage: ArrayLike<number>[], + extent: number[][] + } +} /** * If normal array used, mutable chunk size is supported. * If typed array used, chunk size must be fixed. @@ -144,6 +153,7 @@ export class DefaultDataProvider implements DataProvider { if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { provider.getItem = getItemForTypedArray; provider.count = countForTypedArray; + provider.getStorage = getStorageForTypedArray; } else { const rawItemGetter = getRawSourceItemGetter(sourceFormat, seriesLayoutBy); @@ -167,6 +177,40 @@ export class DefaultDataProvider implements DataProvider { return out; }; + const getStorageForTypedArray: DefaultDataProvider['getStorage'] = function ( + this: DefaultDataProvider, start: number, end: number + ) { + const data = this._data as ArrayLike<number>; + const Ctor = data.constructor; + const dimSize = this._dimSize; + const offset = this._offset; + const storage: ArrayLike<number>[] = []; + const extent = []; + + start -= offset; + end -= offset; + + for (let dim = 0; dim < dimSize; dim++) { + let min = Infinity; + let max = -Infinity; + const count = end - start; + const arr = new (Ctor as any)(count); + for (let i = 0; i < count; i++) { + const val = data[(offset + start + i) * dimSize + dim]; + arr[i] = val; + val < min && (min = val); + val > max && (max = val); + } + storage.push(arr); + extent.push([min, max]); + } + + return { + storage, + extent + }; + }; + const countForTypedArray: DefaultDataProvider['count'] = function ( this: DefaultDataProvider ) { --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org For additional commands, e-mail: commits-h...@echarts.apache.org