You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2020/09/24 06:57:49 UTC

[incubator-echarts] branch list-remove-chunk updated: perf: optimize lttb downsample performance.

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


The following commit(s) were added to refs/heads/list-remove-chunk by this push:
     new 03bf01c  perf: optimize lttb downsample performance.
03bf01c is described below

commit 03bf01c9d8f3769b2ace7d38cd130c0b4c213c21
Author: pissang <bm...@gmail.com>
AuthorDate: Thu Sep 24 14:57:15 2020 +0800

    perf: optimize lttb downsample performance.
---
 src/chart/line/poly.ts      |  2 +-
 src/data/List.ts            | 51 ++++++++++++++++++++-------------------------
 src/processor/dataSample.ts |  7 +++----
 3 files changed, 27 insertions(+), 33 deletions(-)

diff --git a/src/chart/line/poly.ts b/src/chart/line/poly.ts
index 24a14f6..2f341da 100644
--- a/src/chart/line/poly.ts
+++ b/src/chart/line/poly.ts
@@ -77,7 +77,7 @@ function drawSegment(
             const dy = y - prevY;
 
             // Ignore tiny segment.
-            if ((dx * dx + dy * dy) < 1) {
+            if ((dx * dx + dy * dy) < 0.5) {
                 idx += dir;
                 continue;
             }
diff --git a/src/data/List.ts b/src/data/List.ts
index d13ae70..d38e03a 100644
--- a/src/data/List.ts
+++ b/src/data/List.ts
@@ -1625,73 +1625,67 @@ class List<
 
     /**
      * Large data down sampling using largest-triangle-three-buckets
-     * @param {string} baseDimension
      * @param {string} valueDimension
      * @param {number} targetCount
      */
     lttbDownSample(
-        baseDimension: DimensionName,
         valueDimension: DimensionName,
-        targetCount: number
+        rate: number
     ) {
         const list = cloneListForMapAndSample(this, []);
         const targetStorage = list._storage;
-        const baseDimStore = targetStorage[baseDimension];
-        const valueDimStore = targetStorage[valueDimension];
+        const dimStore = targetStorage[valueDimension];
         const len = this.count();
         const newIndices = new (getIndicesCtor(this))(len);
 
         let sampledIndex = 0;
 
-        const frameSize = (len - 2) / (targetCount - 2);
+        const frameSize = mathFloor(1 / rate);
 
         let currentRawIndex = this.getRawIndex(0);
         let maxArea;
         let area;
         let nextRawIndex;
 
+        // First frame use the first data.
         newIndices[sampledIndex++] = currentRawIndex;
-        for (let i = 0; i < targetCount - 2; i++) {
-            let avgX = 0;
-            let avgY = 0;
-            const avgRangeStart = mathFloor((i + 1) * frameSize) + 1;
-            const avgRangeEnd = Math.min(mathFloor((i + 2) * frameSize) + 1, len);
+        for (let i = 1; i < len - 1; i += frameSize) {
+            const nextFrameStart = Math.min(i + frameSize, len - 1);
+            const nextFrameEnd = Math.min(i + frameSize * 2, len);
 
-            const avgRangeLength = avgRangeEnd - avgRangeStart;
+            const avgX = (nextFrameEnd + nextFrameStart) / 2;
+            let avgY = 0;
 
-            for (let idx = avgRangeStart; idx < avgRangeEnd; idx++) {
+            for (let idx = nextFrameStart; idx < nextFrameEnd; idx++) {
                 const rawIndex = this.getRawIndex(idx);
-                const x = baseDimStore[rawIndex] as number;
-                const y = valueDimStore[rawIndex] as number;
-                if (isNaN(x) || isNaN(y)) {
+                const y = dimStore[rawIndex] as number;
+                if (isNaN(y)) {
                     continue;
                 }
-                avgX += x as number;
                 avgY += y as number;
             }
-            avgX /= avgRangeLength;
-            avgY /= avgRangeLength;
+            avgY /= (nextFrameEnd - nextFrameStart);
 
-            const rangeOffs = mathFloor((i) * frameSize) + 1;
-            const rangeTo = mathFloor((i + 1) * frameSize) + 1;
+            const frameStart = i;
+            const frameEnd = Math.min(i + frameSize, len);
 
-            const pointAX = baseDimStore[currentRawIndex] as number;
-            const pointAY = valueDimStore[currentRawIndex] as number;
+            const pointAX = i - 1;
+            const pointAY = dimStore[currentRawIndex] as number;
 
             maxArea = -1;
 
+            nextRawIndex = frameStart;
             // 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++) {
+            for (let idx = frameStart; idx < frameEnd; idx++) {
                 const rawIndex = this.getRawIndex(idx);
-                const x = baseDimStore[rawIndex] as number;
-                const y = valueDimStore[rawIndex] as number;
-                if (isNaN(x) || isNaN(y)) {
+                const y = dimStore[rawIndex] as number;
+                if (isNaN(y)) {
                     continue;
                 }
                 // Calculate triangle area over three buckets
                 area = Math.abs((pointAX - avgX) * (y - pointAY)
-                    - (pointAX - x) * (avgY - pointAY)
+                    - (pointAX - idx) * (avgY - pointAY)
                 );
                 if (area > maxArea) {
                     maxArea = area;
@@ -1704,6 +1698,7 @@ class List<
             currentRawIndex = nextRawIndex; // This a is the next a (chosen b)
         }
 
+        // First frame use the last data.
         newIndices[sampledIndex++] = this.getRawIndex(len - 1);
         list._count = sampledIndex;
         list._indices = newIndices;
diff --git a/src/processor/dataSample.ts b/src/processor/dataSample.ts
index 485f6df..2e2fc4c 100644
--- a/src/processor/dataSample.ts
+++ b/src/processor/dataSample.ts
@@ -88,15 +88,14 @@ export default function (seriesType: string): StageHandler {
                 const baseAxis = coordSys.getBaseAxis();
                 const valueAxis = coordSys.getOtherAxis(baseAxis);
                 const extent = baseAxis.getExtent();
+                const dpr = api.getDevicePixelRatio();
                 // Coordinste system has been resized
-                const size = Math.abs(extent[1] - extent[0]);
+                const size = Math.abs(extent[1] - extent[0]) * (dpr || 1);
                 const rate = Math.round(data.count() / size);
 
                 if (rate > 1) {
                     if (sampling === 'lttb') {
-                        seriesModel.setData(data.lttbDownSample(
-                            data.mapDimension(baseAxis.dim), data.mapDimension(valueAxis.dim), size
-                        ));
+                        seriesModel.setData(data.lttbDownSample(data.mapDimension(valueAxis.dim), 1 / rate));
                     }
                     let sampler;
                     if (typeof sampling === 'string') {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org