From a1498d6b5abe658a299345fd3b72f803c59d9ea2 Mon Sep 17 00:00:00 2001 From: Marco Ribeiro <41395992+ribeirompl@users.noreply.github.com> Date: Mon, 2 Sep 2024 23:48:38 +0200 Subject: [PATCH 1/2] fix(sample): fix minmax sampling behaviour --- src/data/DataStore.ts | 64 +++++++++++++++++++++++++++++++++++++ src/data/SeriesData.ts | 21 ++++++++++-- src/processor/dataSample.ts | 19 ++--------- 3 files changed, 86 insertions(+), 18 deletions(-) diff --git a/src/data/DataStore.ts b/src/data/DataStore.ts index 36f7373a74..2a94549c08 100644 --- a/src/data/DataStore.ts +++ b/src/data/DataStore.ts @@ -1013,6 +1013,70 @@ class DataStore { return target; } + /** + * Large data down sampling using min-max + * @param {string} valueDimension + * @param {number} rate + */ + minmaxDownSample( + valueDimension: DimensionIndex, + rate: number + ): DataStore { + const target = this.clone([valueDimension], true); + const targetStorage = target._chunks; + + const frameSize = Math.floor(1 / rate); + + const dimStore = targetStorage[valueDimension]; + const len = this.count(); + + // Each frame results in 2 data points, one for min and one for max + const newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize) * 2); + + let offset = 0; + for (let i = 0; i < len; i += frameSize) { + let minIndex = this.getRawIndex(i); + let minValue = dimStore[minIndex]; + let maxIndex = this.getRawIndex(i); + let maxValue = dimStore[maxIndex]; + + // Determine min and max within the current frame + for (let k = 0; k < frameSize; k++) { + const rawIndex = this.getRawIndex(i + k); + const value = dimStore[rawIndex]; + + if (value < minValue) { + minValue = value; + minIndex = i + k; + } + if (value > maxValue) { + maxValue = value; + maxIndex = i + k; + } + } + + const rawMinIndex = this.getRawIndex(minIndex); + const rawMaxIndex = this.getRawIndex(maxIndex); + + // Set the order of the min and max values, based on their ordering in the frame + if (minIndex < maxIndex) { + newIndices[offset++] = rawMinIndex; + newIndices[offset++] = rawMaxIndex; + } + else { + newIndices[offset++] = rawMaxIndex; + newIndices[offset++] = rawMinIndex; + } + } + + target._count = offset; + target._indices = newIndices; + + target._updateGetRawIdx(); + + return target; + } + /** * Large data down sampling on given dimension diff --git a/src/data/SeriesData.ts b/src/data/SeriesData.ts index 9267177778..ecf3e03f81 100644 --- a/src/data/SeriesData.ts +++ b/src/data/SeriesData.ts @@ -254,10 +254,10 @@ class SeriesData< // Methods that create a new list based on this list should be listed here. // Notice that those method should `RETURN` the new list. - TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map'] as const; + TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'minmaxDownSample', 'lttbDownSample', 'map'] as const; // Methods that change indices of this list should be listed here. CHANGABLE_METHODS = ['filterSelf', 'selectRange'] as const; - DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample'] as const; + DOWNSAMPLE_METHODS = ['downSample', 'minmaxDownSample', 'lttbDownSample'] as const; /** * @param dimensionsInput.dimensions @@ -1098,6 +1098,23 @@ class SeriesData< return list as SeriesData; } + /** + * Large data down sampling using min-max + * @param {string} valueDimension + * @param {number} rate + */ + minmaxDownSample( + valueDimension: DimensionLoose, + rate: number + ): SeriesData { + const list = cloneListForMapAndSample(this); + list._store = this._store.minmaxDownSample( + this._getStoreDimIndex(valueDimension), + rate + ); + return list as SeriesData; + } + /** * Large data down sampling using largest-triangle-three-buckets * @param {string} valueDimension diff --git a/src/processor/dataSample.ts b/src/processor/dataSample.ts index cbd370f2ba..3c19572e9a 100644 --- a/src/processor/dataSample.ts +++ b/src/processor/dataSample.ts @@ -61,22 +61,6 @@ const samplers: Dictionary = { // NaN will cause illegal axis extent. return isFinite(min) ? min : NaN; }, - minmax: function (frame) { - let turningPointAbsoluteValue = -Infinity; - let turningPointOriginalValue = -Infinity; - - for (let i = 0; i < frame.length; i++) { - const originalValue = frame[i]; - const absoluteValue = Math.abs(originalValue); - - if (absoluteValue > turningPointAbsoluteValue) { - turningPointAbsoluteValue = absoluteValue; - turningPointOriginalValue = originalValue; - } - } - - return isFinite(turningPointOriginalValue) ? turningPointOriginalValue : NaN; - }, // TODO // Median nearest: function (frame) { @@ -115,6 +99,9 @@ export default function dataSample(seriesType: string): StageHandler { if (sampling === 'lttb') { seriesModel.setData(data.lttbDownSample(data.mapDimension(valueAxis.dim), 1 / rate)); } + else if (sampling === 'minmax') { + seriesModel.setData(data.minmaxDownSample(data.mapDimension(valueAxis.dim), 1 / rate)); + } let sampler; if (isString(sampling)) { sampler = samplers[sampling]; From e928a4c2f76e1e6034f7dfd88f1cf5911b738813 Mon Sep 17 00:00:00 2001 From: Marco Ribeiro <41395992+ribeirompl@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:06:27 +0200 Subject: [PATCH 2/2] fix(sample): fix minmax final frame handling --- src/data/DataStore.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/data/DataStore.ts b/src/data/DataStore.ts index 2a94549c08..a0f306bd0e 100644 --- a/src/data/DataStore.ts +++ b/src/data/DataStore.ts @@ -1035,13 +1035,18 @@ class DataStore { let offset = 0; for (let i = 0; i < len; i += frameSize) { - let minIndex = this.getRawIndex(i); - let minValue = dimStore[minIndex]; - let maxIndex = this.getRawIndex(i); - let maxValue = dimStore[maxIndex]; - + let minIndex = i; + let minValue = dimStore[this.getRawIndex(minIndex)]; + let maxIndex = i; + let maxValue = dimStore[this.getRawIndex(maxIndex)]; + + let thisFrameSize = frameSize; + // Handle final smaller frame + if (i + frameSize > len) { + thisFrameSize = len - i; + } // Determine min and max within the current frame - for (let k = 0; k < frameSize; k++) { + for (let k = 0; k < thisFrameSize; k++) { const rawIndex = this.getRawIndex(i + k); const value = dimStore[rawIndex];