This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch line-optimize
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git

commit 71a590b4b84d9dd34163f5af3cd4b3848ee78168
Author: pissang <[email protected]>
AuthorDate: Wed Sep 16 23:35:10 2020 +0800

    line: optimize memory cost and initialize time for the large line.
---
 src/chart/helper/LargeSymbolDraw.ts   |   6 +-
 src/chart/helper/SymbolDraw.ts        |  18 +-
 src/chart/line.ts                     |   2 +-
 src/chart/line/LineView.ts            | 128 ++++++++------
 src/chart/line/helper.ts              |   2 +-
 src/chart/line/lineAnimationDiff.ts   | 126 ++++++++------
 src/chart/line/poly.ts                | 302 +++++++++++-----------------------
 src/layout/points.ts                  |  12 +-
 src/{chart/line.ts => util/vendor.ts} |  27 ++-
 test/largeLine.html                   |  30 ++--
 10 files changed, 304 insertions(+), 349 deletions(-)

diff --git a/src/chart/helper/LargeSymbolDraw.ts 
b/src/chart/helper/LargeSymbolDraw.ts
index 381beea..432df1f 100644
--- a/src/chart/helper/LargeSymbolDraw.ts
+++ b/src/chart/helper/LargeSymbolDraw.ts
@@ -190,7 +190,7 @@ class LargeSymbolDraw {
         });
 
         symbolEl.setShape({
-            points: data.getLayout('symbolPoints')
+            points: data.getLayout('points')
         });
         this._setCommon(symbolEl, data, false, opt);
         this.group.add(symbolEl);
@@ -203,7 +203,7 @@ class LargeSymbolDraw {
             return;
         }
 
-        let points = data.getLayout('symbolPoints');
+        let points = data.getLayout('points');
         this.group.eachChild(function (child: LargeSymbolPath) {
             if (child.startIndex != null) {
                 const len = (child.endIndex - child.startIndex) * 2;
@@ -251,7 +251,7 @@ class LargeSymbolDraw {
         }
 
         symbolEl.setShape({
-            points: data.getLayout('symbolPoints')
+            points: data.getLayout('points')
         });
         this._setCommon(symbolEl, data, !!this._incremental, opt);
     }
diff --git a/src/chart/helper/SymbolDraw.ts b/src/chart/helper/SymbolDraw.ts
index fd864e6..94c6b3d 100644
--- a/src/chart/helper/SymbolDraw.ts
+++ b/src/chart/helper/SymbolDraw.ts
@@ -25,7 +25,6 @@ import type Displayable from 
'zrender/src/graphic/Displayable';
 import {
     StageHandlerProgressParams,
     LabelOption,
-    ColorString,
     SymbolOptionMixin,
     ItemStyleOption,
     ZRColor,
@@ -42,6 +41,7 @@ import { getLabelStatesModels } from '../../label/labelStyle';
 
 interface UpdateOpt {
     isIgnore?(idx: number): boolean
+    getSymbolPoint?(idx: number): number[]
     clipShape?: CoordinateSystemClipArea
 }
 
@@ -157,6 +157,8 @@ class SymbolDraw {
 
     private _seriesScope: SymbolDrawSeriesScope;
 
+    private _getSymbolPoint: UpdateOpt['getSymbolPoint'];
+
     constructor(SymbolCtor?: SymbolLikeCtor) {
         this._SymbolCtor = SymbolCtor || SymbolClz;
     }
@@ -174,6 +176,11 @@ class SymbolDraw {
 
         const seriesScope = makeSeriesScope(data);
 
+        const getSymbolPoint = opt.getSymbolPoint || function (idx: number) {
+            return data.getItemLayout(idx);
+        };
+
+
         // There is no oldLineData only when first rendering or switching from
         // stream mode to normal mode, where previous elements should be 
removed.
         if (!oldData) {
@@ -182,7 +189,7 @@ class SymbolDraw {
 
         data.diff(oldData)
             .add(function (newIdx) {
-                const point = data.getItemLayout(newIdx) as number[];
+                const point = getSymbolPoint(newIdx);
                 if (symbolNeedsDraw(data, point, newIdx, opt)) {
                     const symbolEl = new SymbolCtor(data, newIdx, seriesScope);
                     symbolEl.setPosition(point);
@@ -193,7 +200,7 @@ class SymbolDraw {
             .update(function (newIdx, oldIdx) {
                 let symbolEl = oldData.getItemGraphicEl(oldIdx) as SymbolLike;
 
-                const point = data.getItemLayout(newIdx) as number[];
+                const point = getSymbolPoint(newIdx) as number[];
                 if (!symbolNeedsDraw(data, point, newIdx, opt)) {
                     group.remove(symbolEl);
                     return;
@@ -223,6 +230,7 @@ class SymbolDraw {
             })
             .execute();
 
+        this._getSymbolPoint = getSymbolPoint;
         this._data = data;
     };
 
@@ -234,8 +242,8 @@ class SymbolDraw {
         const data = this._data;
         if (data) {
             // Not use animation
-            data.eachItemGraphicEl(function (el, idx) {
-                const point = data.getItemLayout(idx);
+            data.eachItemGraphicEl((el, idx) => {
+                const point = this._getSymbolPoint(idx);
                 el.setPosition(point);
                 el.markRedraw();
             });
diff --git a/src/chart/line.ts b/src/chart/line.ts
index 541b481..53be4a6 100644
--- a/src/chart/line.ts
+++ b/src/chart/line.ts
@@ -28,7 +28,7 @@ import dataSample from '../processor/dataSample';
 // In case developer forget to include grid component
 import '../component/gridSimple';
 
-echarts.registerLayout(layoutPoints('line'));
+echarts.registerLayout(layoutPoints('line', true));
 
 // Down sample after filter
 echarts.registerProcessor(
diff --git a/src/chart/line/LineView.ts b/src/chart/line/LineView.ts
index 2c77752..4616e2f 100644
--- a/src/chart/line/LineView.ts
+++ b/src/chart/line/LineView.ts
@@ -20,7 +20,6 @@
 // FIXME step not support polar
 
 import * as zrUtil from 'zrender/src/core/util';
-import {fromPoints} from 'zrender/src/core/bbox';
 import SymbolDraw from '../helper/SymbolDraw';
 import SymbolClz from '../helper/Symbol';
 import lineAnimationDiff from './lineAnimationDiff';
@@ -43,6 +42,8 @@ import type Axis2D from '../../coord/cartesian/Axis2D';
 import { CoordinateSystemClipArea } from '../../coord/CoordinateSystem';
 import { setStatesStylesFromModel, setStatesFlag, enableHoverEmphasis } from 
'../../util/states';
 import { getECData } from '../../util/ecData';
+import { createFloat32Array } from '../../util/vendor';
+import { createSymbol } from '../../util/symbol';
 
 
 type PolarArea = ReturnType<Polar['getArea']>;
@@ -52,29 +53,46 @@ interface SymbolExtended extends SymbolClz {
     __temp: boolean
 }
 
-function isPointsSame(points1: number[][], points2: number[][]) {
+function isPointsSame(points1: ArrayLike<number>, points2: ArrayLike<number>) {
     if (points1.length !== points2.length) {
         return;
     }
     for (let i = 0; i < points1.length; i++) {
-        const p1 = points1[i];
-        const p2 = points2[i];
-        if (p1[0] !== p2[0] || p1[1] !== p2[1]) {
+        if (points1[i] !== points2[i]) {
             return;
         }
     }
     return true;
 }
 
-function getBoundingDiff(points1: number[][], points2: number[][]): number {
-    const min1 = [] as number[];
-    const max1 = [] as number[];
+function bboxFromPoints(points: ArrayLike<number>) {
+    let minX = Infinity;
+    let minY = Infinity;
+    let maxX = -Infinity;
+    let maxY = -Infinity;
+
+    for (let i = 0; i < points.length;) {
+        const x = points[i++];
+        const y = points[i++];
+        if (!isNaN(x)) {
+            minX = Math.min(x, minX);
+            maxX = Math.max(x, maxX);
+        }
+        if (!isNaN(y)) {
+            minY = Math.min(y, minY);
+            maxY = Math.max(y, maxY);
+        }
+    }
+    return [
+        [minX, minY],
+        [maxX, maxY]
+    ];
+}
 
-    const min2 = [] as number[];
-    const max2 = [] as number[];
+function getBoundingDiff(points1: ArrayLike<number>, points2: 
ArrayLike<number>): number {
 
-    fromPoints(points1, min1, max1);
-    fromPoints(points2, min2, max2);
+    const [min1, max1] = bboxFromPoints(points1);
+    const [min2, max2] = bboxFromPoints(points2);
 
     // Get a max value from each corner of two boundings.
     return Math.max(
@@ -99,56 +117,61 @@ function getStackedOnPoints(
         return [];
     }
 
-    const points = [];
-    for (let idx = 0, len = data.count(); idx < len; idx++) {
-        points.push(getStackedOnPoint(dataCoordInfo, coordSys, data, idx));
+    const len = data.count();
+    const points = createFloat32Array(len * 2);
+    for (let idx = 0; idx < len; idx++) {
+        const pt = getStackedOnPoint(dataCoordInfo, coordSys, data, idx);
+        points[idx * 2] = pt[0];
+        points[idx * 2 + 1] = pt[1];
     }
 
     return points;
 }
 
 function turnPointsIntoStep(
-    points: number[][],
+    points: ArrayLike<number>,
     coordSys: Cartesian2D | Polar,
     stepTurnAt: 'start' | 'end' | 'middle'
-) {
+): number[] {
     const baseAxis = coordSys.getBaseAxis();
     const baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 
1;
 
-    const stepPoints = [];
+    const stepPoints: number[] = [];
     let i = 0;
-    for (; i < points.length - 1; i++) {
-        const nextPt = points[i + 1];
-        const pt = points[i];
-        stepPoints.push(pt);
+    const stepPt: number[] = [];
+    const pt: number[] = [];
+    const nextPt: number[] = [];
+    for (; i < points.length - 2; i += 2) {
+        nextPt[0] = points[i + 2];
+        nextPt[1] = points[i + 3];
+        pt[0] = points[i];
+        pt[1] = points[i + 1];
+        stepPoints.push(pt[0], pt[1]);
 
-        const stepPt = [];
         switch (stepTurnAt) {
             case 'end':
                 stepPt[baseIndex] = nextPt[baseIndex];
                 stepPt[1 - baseIndex] = pt[1 - baseIndex];
-                // default is start
-                stepPoints.push(stepPt);
+                stepPoints.push(stepPt[0], stepPt[1]);
                 break;
             case 'middle':
-                // default is start
                 const middle = (pt[baseIndex] + nextPt[baseIndex]) / 2;
                 const stepPt2 = [];
                 stepPt[baseIndex] = stepPt2[baseIndex] = middle;
                 stepPt[1 - baseIndex] = pt[1 - baseIndex];
                 stepPt2[1 - baseIndex] = nextPt[1 - baseIndex];
-                stepPoints.push(stepPt);
-                stepPoints.push(stepPt2);
+                stepPoints.push(stepPt[0], stepPt[1]);
+                stepPoints.push(stepPt2[0], stepPt[1]);
                 break;
             default:
+                // default is start
                 stepPt[baseIndex] = pt[baseIndex];
                 stepPt[1 - baseIndex] = nextPt[1 - baseIndex];
-                // default is start
-                stepPoints.push(stepPt);
+                stepPoints.push(stepPt[0], stepPt[1]);
         }
     }
     // Last points
-    points[i] && stepPoints.push(points[i]);
+    stepPoints.push(points[i++], points[i++]);
     return stepPoints;
 }
 
@@ -365,8 +388,8 @@ class LineView extends ChartView {
     _polyline: ECPolyline;
     _polygon: ECPolygon;
 
-    _stackedOnPoints: number[][];
-    _points: number[][];
+    _stackedOnPoints: ArrayLike<number>;
+    _points: ArrayLike<number>;
 
     _step: LineSeriesOption['step'];
     _valueOrigin: LineSeriesOption['areaStyle']['origin'];
@@ -392,7 +415,7 @@ class LineView extends ChartView {
         const lineStyleModel = seriesModel.getModel('lineStyle');
         const areaStyleModel = seriesModel.getModel('areaStyle');
 
-        let points = data.mapArray(data.getItemLayout);
+        let points = data.getLayout('points') as number[] || [];
 
         const isCoordSysPolar = coordSys.type === 'polar';
         const prevCoordSys = this._coordSys;
@@ -458,7 +481,10 @@ class LineView extends ChartView {
         ) {
             showSymbol && symbolDraw.updateData(data, {
                 isIgnore: isIgnoreFunc,
-                clipShape: clipShapeForSymbol
+                clipShape: clipShapeForSymbol,
+                getSymbolPoint(idx) {
+                    return [points[idx * 2], points[idx * 2 + 1]];
+                }
             });
 
             if (step) {
@@ -495,7 +521,10 @@ class LineView extends ChartView {
             // because points are not changed
             showSymbol && symbolDraw.updateData(data, {
                 isIgnore: isIgnoreFunc,
-                clipShape: clipShapeForSymbol
+                clipShape: clipShapeForSymbol,
+                getSymbolPoint(idx) {
+                    return [points[idx * 2], points[idx * 2 + 1]];
+                }
             });
 
             // Stop symbol animation and sync with line points
@@ -629,25 +658,27 @@ class LineView extends ChartView {
         this._changePolyState('emphasis');
 
         if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 
0) {
+            const points = data.getLayout('points');
             let symbol = data.getItemGraphicEl(dataIndex) as SymbolClz;
             if (!symbol) {
                 // Create a temporary symbol if it is not exists
-                const pt = data.getItemLayout(dataIndex) as number[];
-                if (!pt) {
+                const x = points[dataIndex * 2];
+                const y = points[dataIndex * 2 + 1];
+                if (isNaN(x) || isNaN(y)) {
                     // Null data
                     return;
                 }
                 // fix #11360: should't draw symbol outside clipShapeForSymbol
-                if (this._clipShapeForSymbol && 
!this._clipShapeForSymbol.contain(pt[0], pt[1])) {
+                if (this._clipShapeForSymbol && 
!this._clipShapeForSymbol.contain(x, y)) {
                     return;
                 }
                 symbol = new SymbolClz(data, dataIndex);
-                symbol.setPosition(pt);
+                symbol.x = x;
+                symbol.y = y;
                 symbol.setZ(
                     seriesModel.get('zlevel'),
                     seriesModel.get('z')
                 );
-                symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]);
                 (symbol as SymbolExtended).__temp = true;
                 data.setItemGraphicEl(dataIndex, symbol);
 
@@ -705,7 +736,7 @@ class LineView extends ChartView {
         polygon && setStatesFlag(polygon, toState);
     }
 
-    _newPolyline(points: number[][]) {
+    _newPolyline(points: ArrayLike<number>) {
         let polyline = this._polyline;
         // Remove previous created polyline
         if (polyline) {
@@ -714,7 +745,7 @@ class LineView extends ChartView {
 
         polyline = new ECPolyline({
             shape: {
-                points: points
+                points
             },
             segmentIgnoreThreshold: 2,
             z2: 10
@@ -727,7 +758,7 @@ class LineView extends ChartView {
         return polyline;
     }
 
-    _newPolygon(points: number[][], stackedOnPoints: number[][]) {
+    _newPolygon(points: ArrayLike<number>, stackedOnPoints: ArrayLike<number>) 
{
         let polygon = this._polygon;
         // Remove previous created polygon
         if (polygon) {
@@ -736,7 +767,7 @@ class LineView extends ChartView {
 
         polygon = new ECPolygon({
             shape: {
-                points: points,
+                points,
                 stackedOnPoints: stackedOnPoints
             },
             segmentIgnoreThreshold: 2
@@ -754,7 +785,7 @@ class LineView extends ChartView {
     // FIXME Two value axis
     _updateAnimation(
         data: List,
-        stackedOnPoints: number[][],
+        stackedOnPoints: ArrayLike<number>,
         coordSys: Cartesian2D | Polar,
         api: ExtensionAPI,
         step: LineSeriesOption['step'],
@@ -855,9 +886,12 @@ class LineView extends ChartView {
 
         if (polyline.animators && polyline.animators.length) {
             polyline.animators[0].during(function () {
+                const points = (polyline.shape as any).__points;
                 for (let i = 0; i < updatedDataInfo.length; i++) {
                     const el = updatedDataInfo[i].el;
-                    el.setPosition((polyline.shape as 
any).__points[updatedDataInfo[i].ptIdx]);
+                    const offset = updatedDataInfo[i].ptIdx * 2;
+                    el.x = points[offset];
+                    el.y = points[offset + 1];
                     el.markRedraw();
                 }
             });
diff --git a/src/chart/line/helper.ts b/src/chart/line/helper.ts
index edaccc5..200824f 100644
--- a/src/chart/line/helper.ts
+++ b/src/chart/line/helper.ts
@@ -111,7 +111,7 @@ export function getStackedOnPoint(
     coordSys: Cartesian2D | Polar,
     data: List,
     idx: number
-    ) {
+) {
     let value = NaN;
     if (dataCoordInfo.stacked) {
         value = data.get(data.getCalculationInfo('stackedOverDimension'), idx) 
as number;
diff --git a/src/chart/line/lineAnimationDiff.ts 
b/src/chart/line/lineAnimationDiff.ts
index 9e164ef..84cc409 100644
--- a/src/chart/line/lineAnimationDiff.ts
+++ b/src/chart/line/lineAnimationDiff.ts
@@ -22,6 +22,7 @@ import List from '../../data/List';
 import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
 import type Polar from '../../coord/polar/Polar';
 import { LineSeriesOption } from './LineSeries';
+import { createFloat32Array } from '../../util/vendor';
 
 interface DiffItem {
     cmd: '+' | '=' | '-'
@@ -49,7 +50,7 @@ function diffData(oldData: List, newData: List) {
 
 export default function (
     oldData: List, newData: List,
-    oldStackedOnPoints: number[][], newStackedOnPoints: number[][],
+    oldStackedOnPoints: ArrayLike<number>, newStackedOnPoints: 
ArrayLike<number>,
     oldCoordSys: Cartesian2D | Polar, newCoordSys: Cartesian2D | Polar,
     oldValueOrigin: LineSeriesOption['areaStyle']['origin'],
     newValueOrigin: LineSeriesOption['areaStyle']['origin']
@@ -64,11 +65,11 @@ export default function (
     // // FIXME One data ?
     // diff = arrayDiff(oldIdList, newIdList);
 
-    const currPoints: number[][] = [];
-    const nextPoints: number[][] = [];
+    const currPoints: number[] = [];
+    const nextPoints: number[] = [];
     // Points for stacking base line
-    const currStackedPoints: number[][] = [];
-    const nextStackedPoints: number[][] = [];
+    const currStackedPoints: number[] = [];
+    const nextStackedPoints: number[] = [];
 
     const status = [];
     const sortedIndices: number[] = [];
@@ -77,62 +78,78 @@ export default function (
     const newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, 
oldValueOrigin);
     const oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, 
newValueOrigin);
 
+    const oldPoints = oldData.getLayout('points') as number[] || [];
+    const newPoints = newData.getLayout('points') as number[] || [];
+
     for (let i = 0; i < diff.length; i++) {
         const diffItem = diff[i];
         let pointAdded = true;
 
+        let oldIdx2: number;
+        let newIdx2: number;
+
         // FIXME, animation is not so perfect when dataZoom window moves fast
         // Which is in case remvoing or add more than one data in the tail or 
head
         switch (diffItem.cmd) {
             case '=':
-                let currentPt = oldData.getItemLayout(diffItem.idx) as 
number[];
-                const nextPt = newData.getItemLayout(diffItem.idx1) as 
number[];
+                oldIdx2 = diffItem.idx * 2;
+                newIdx2 = diffItem.idx1 * 2;
+                let currentX = oldPoints[oldIdx2];
+                let currentY = oldPoints[oldIdx2 + 1];
+                const nextX = newPoints[newIdx2];
+                const nextY = newPoints[newIdx2 + 1];
+
                 // If previous data is NaN, use next point directly
-                if (isNaN(currentPt[0]) || isNaN(currentPt[1])) {
-                    currentPt = nextPt.slice();
+                if (isNaN(currentX) || isNaN(currentY)) {
+                    currentX = nextX;
+                    currentY = nextY;
                 }
-                currPoints.push(currentPt);
-                nextPoints.push(nextPt);
+                currPoints.push(currentX, currentY);
+                nextPoints.push(nextX, nextY);
 
-                currStackedPoints.push(oldStackedOnPoints[diffItem.idx]);
-                nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]);
+                currStackedPoints.push(oldStackedOnPoints[oldIdx2], 
oldStackedOnPoints[oldIdx2 + 1]);
+                nextStackedPoints.push(newStackedOnPoints[newIdx2], 
newStackedOnPoints[newIdx2 + 1]);
 
                 rawIndices.push(newData.getRawIndex(diffItem.idx1));
                 break;
             case '+':
-                const idxAdd = diffItem.idx;
-                currPoints.push(
-                    oldCoordSys.dataToPoint([
-                        newData.get(newDataOldCoordInfo.dataDimsForPoint[0], 
idxAdd),
-                        newData.get(newDataOldCoordInfo.dataDimsForPoint[1], 
idxAdd)
-                    ])
-                );
-
-                nextPoints.push((newData.getItemLayout(idxAdd) as 
number[]).slice());
-
-                currStackedPoints.push(
-                    getStackedOnPoint(newDataOldCoordInfo, oldCoordSys, 
newData, idxAdd)
-                );
-                nextStackedPoints.push(newStackedOnPoints[idxAdd]);
-
-                rawIndices.push(newData.getRawIndex(idxAdd));
+                const newIdx = diffItem.idx;
+                const newDataDimsForPoint = 
newDataOldCoordInfo.dataDimsForPoint;
+                const oldPt = oldCoordSys.dataToPoint([
+                    newData.get(newDataDimsForPoint[0], newIdx),
+                    newData.get(newDataDimsForPoint[1], newIdx)
+                ]);
+                newIdx2 = newIdx * 2;
+                currPoints.push(oldPt[0], oldPt[1]);
+
+                nextPoints.push(newPoints[newIdx2], newPoints[newIdx2 + 1]);
+
+                const stackedOnPoint = getStackedOnPoint(newDataOldCoordInfo, 
oldCoordSys, newData, newIdx);
+
+                currStackedPoints.push(stackedOnPoint[0], stackedOnPoint[1]);
+                nextStackedPoints.push(newStackedOnPoints[newIdx2], 
newStackedOnPoints[newIdx2 + 1]);
+
+                rawIndices.push(newData.getRawIndex(newIdx));
                 break;
             case '-':
-                const idxMinus = diffItem.idx;
-                const rawIndex = oldData.getRawIndex(idxMinus);
+                const oldIdx = diffItem.idx;
+                const rawIndex = oldData.getRawIndex(oldIdx);
+                const oldDataDimsForPoint = 
oldDataNewCoordInfo.dataDimsForPoint;
+                oldIdx2 = oldIdx * 2;
                 // Data is replaced. In the case of dynamic data queue
                 // FIXME FIXME FIXME
-                if (rawIndex !== idxMinus) {
-                    currPoints.push(oldData.getItemLayout(idxMinus) as 
number[]);
-                    nextPoints.push(newCoordSys.dataToPoint([
-                        oldData.get(oldDataNewCoordInfo.dataDimsForPoint[0], 
idxMinus),
-                        oldData.get(oldDataNewCoordInfo.dataDimsForPoint[1], 
idxMinus)
-                    ]));
-
-                    currStackedPoints.push(oldStackedOnPoints[idxMinus]);
-                    nextStackedPoints.push(
-                        getStackedOnPoint(oldDataNewCoordInfo, newCoordSys, 
oldData, idxMinus)
-                    );
+                if (rawIndex !== oldIdx) {
+                    const newPt = newCoordSys.dataToPoint([
+                        oldData.get(oldDataDimsForPoint[0], oldIdx),
+                        oldData.get(oldDataDimsForPoint[1], oldIdx)
+                    ]);
+                    const newStackedOnPt = 
getStackedOnPoint(oldDataNewCoordInfo, newCoordSys, oldData, oldIdx);
+
+                    currPoints.push(oldPoints[oldIdx2], oldPoints[oldIdx2 + 
1]);
+                    nextPoints.push(newPt[0], newPt[1]);
+
+                    currStackedPoints.push(oldStackedOnPoints[oldIdx2], 
oldStackedOnPoints[oldIdx2 + 1]);
+                    nextStackedPoints.push(newStackedOnPt[0], 
newStackedOnPt[1]);
 
                     rawIndices.push(rawIndex);
                 }
@@ -154,20 +171,27 @@ export default function (
         return rawIndices[a] - rawIndices[b];
     });
 
-    const sortedCurrPoints = [];
-    const sortedNextPoints = [];
+    const len = currPoints.length;
+    const sortedCurrPoints = createFloat32Array(len);
+    const sortedNextPoints = createFloat32Array(len);
 
-    const sortedCurrStackedPoints = [];
-    const sortedNextStackedPoints = [];
+    const sortedCurrStackedPoints = createFloat32Array(len);
+    const sortedNextStackedPoints = createFloat32Array(len);
 
     const sortedStatus = [];
     for (let i = 0; i < sortedIndices.length; i++) {
         const idx = sortedIndices[i];
-        sortedCurrPoints[i] = currPoints[idx];
-        sortedNextPoints[i] = nextPoints[idx];
-
-        sortedCurrStackedPoints[i] = currStackedPoints[idx];
-        sortedNextStackedPoints[i] = nextStackedPoints[idx];
+        const i2 = i * 2;
+        const idx2 = idx * 2;
+        sortedCurrPoints[i2] = currPoints[idx2];
+        sortedCurrPoints[i2 + 1] = currPoints[idx2 + 1];
+        sortedNextPoints[i2] = nextPoints[idx2];
+        sortedNextPoints[i2 + 1] = nextPoints[idx2 + 1];
+
+        sortedCurrStackedPoints[i2] = currStackedPoints[idx2];
+        sortedCurrStackedPoints[i2 + 1] = currStackedPoints[idx2 + 1];
+        sortedNextStackedPoints[i2] = nextStackedPoints[idx2];
+        sortedNextStackedPoints[i2 + 1] = nextStackedPoints[idx2 + 1];
 
         sortedStatus[i] = status[idx];
     }
diff --git a/src/chart/line/poly.ts b/src/chart/line/poly.ts
index adb61eb..1c9a4ce 100644
--- a/src/chart/line/poly.ts
+++ b/src/chart/line/poly.ts
@@ -20,94 +20,21 @@
 // Poly path support NaN point
 
 import Path, { PathProps } from 'zrender/src/graphic/Path';
-import * as vec2 from 'zrender/src/core/vector';
 
-const vec2Min = vec2.min;
-const vec2Max = vec2.max;
+const mathMin = Math.min;
+const mathMax = Math.max;
 
-const scaleAndAdd = vec2.scaleAndAdd;
-const v2Copy = vec2.copy;
-
-// Temporary variable
-const v: number[] = [];
-const cp0: number[] = [];
-const cp1: number[] = [];
-
-function isPointNull(p: number[]) {
-    return isNaN(p[0]) || isNaN(p[1]);
+function isPointNull(x: number, y: number) {
+    return isNaN(x) || isNaN(y);
 }
-
-function drawSegment(
-    ctx: CanvasRenderingContext2D,
-    points: number[][],
-    start: number,
-    segLen: number,
-    allLen: number,
-    dir: number,
-    smoothMin: number[],
-    smoothMax: number[],
-    smooth: number,
-    smoothMonotone: 'x' | 'y' | 'none',
-    connectNulls: boolean
-) {
-    return ((smoothMonotone === 'none' || !smoothMonotone) ? drawNonMono : 
drawMono)(
-        ctx,
-        points,
-        start,
-        segLen,
-        allLen,
-        dir,
-        smoothMin,
-        smoothMax,
-        smooth,
-        smoothMonotone,
-        connectNulls
-    );
-}
-
-/**
- * Check if points is in monotone.
- *
- * @param {number[][]} points         Array of points which is in [x, y] form
- * @param {string}     smoothMonotone 'x', 'y', or 'none', stating for which
- *                                    dimension that is checking.
- *                                    If is 'none', `drawNonMono` should be
- *                                    called.
- *                                    If is undefined, either being monotone
- *                                    in 'x' or 'y' will call `drawMono`.
- */
-// function isMono(points, smoothMonotone) {
-//     if (points.length <= 1) {
-//         return true;
-//     }
-
-//     let dim = smoothMonotone === 'x' ? 0 : 1;
-//     let last = points[0][dim];
-//     let lastDiff = 0;
-//     for (let i = 1; i < points.length; ++i) {
-//         let diff = points[i][dim] - last;
-//         if (!isNaN(diff) && !isNaN(lastDiff)
-//             && diff !== 0 && lastDiff !== 0
-//             && ((diff >= 0) !== (lastDiff >= 0))
-//         ) {
-//             return false;
-//         }
-//         if (!isNaN(diff) && diff !== 0) {
-//             lastDiff = diff;
-//             last = points[i][dim];
-//         }
-//     }
-//     return true;
-// }
-
 /**
- * Draw smoothed line in monotone, in which only vertical or horizontal bezier
- * control points will be used. This should be used when points are monotone
- * either in x or y dimension.
+ * Draw smoothed line in non-monotone, in may cause undesired curve in extreme
+ * situations. This should be used when points are non-monotone neither in x or
+ * y dimension.
  */
-function drawMono(
+function drawSegment(
     ctx: CanvasRenderingContext2D,
-    points: number[][],
+    points: ArrayLike<number>,
     start: number,
     segLen: number,
     allLen: number,
@@ -118,15 +45,23 @@ function drawMono(
     smoothMonotone: 'x' | 'y' | 'none',
     connectNulls: boolean
 ) {
-    let prevIdx = 0;
+    let px: number;
+    let py: number;
+    let cpx0: number;
+    let cpy0: number;
+    let cpx1: number;
+    let cpy1: number;
     let idx = start;
     let k = 0;
     for (; k < segLen; k++) {
-        const p = points[idx];
+
+        const x = points[idx * 2];
+        const y = points[idx * 2 + dir];
+
         if (idx >= allLen || idx < 0) {
             break;
         }
-        if (isPointNull(p)) {
+        if (isPointNull(x, y)) {
             if (connectNulls) {
                 idx += dir;
                 continue;
@@ -135,165 +70,118 @@ function drawMono(
         }
 
         if (idx === start) {
-            ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);
+            ctx[dir > 0 ? 'moveTo' : 'lineTo'](x, y);
+            cpx0 = x;
+            cpy0 = y;
         }
         else {
-            if (smooth > 0) {
-                const prevP = points[prevIdx];
-                const dim = smoothMonotone === 'y' ? 1 : 0;
-
-                // Length of control point to p, either in x or y, but not both
-                const ctrlLen = (p[dim] - prevP[dim]) * smooth;
-
-                v2Copy(cp0, prevP);
-                cp0[dim] = prevP[dim] + ctrlLen;
-
-                v2Copy(cp1, p);
-                cp1[dim] = p[dim] - ctrlLen;
-
-                ctx.bezierCurveTo(
-                    cp0[0], cp0[1],
-                    cp1[0], cp1[1],
-                    p[0], p[1]
-                );
-            }
-            else {
-                ctx.lineTo(p[0], p[1]);
-            }
-        }
-
-        prevIdx = idx;
-        idx += dir;
-    }
+            const dx = x - px;
+            const dy = y - py;
 
-    return k;
-}
-
-/**
- * Draw smoothed line in non-monotone, in may cause undesired curve in extreme
- * situations. This should be used when points are non-monotone neither in x or
- * y dimension.
- */
-function drawNonMono(
-    ctx: CanvasRenderingContext2D,
-    points: number[][],
-    start: number,
-    segLen: number,
-    allLen: number,
-    dir: number,
-    smoothMin: number[],
-    smoothMax: number[],
-    smooth: number,
-    smoothMonotone: 'x' | 'y' | 'none',
-    connectNulls: boolean
-) {
-    let prevIdx = 0;
-    let idx = start;
-    let k = 0;
-    for (; k < segLen; k++) {
-        const p = points[idx];
-        if (idx >= allLen || idx < 0) {
-            break;
-        }
-        if (isPointNull(p)) {
-            if (connectNulls) {
+            // Ignore tiny segment.
+            if ((dx * dx + dy * dy) < 1) {
                 idx += dir;
                 continue;
             }
-            break;
-        }
 
-        if (idx === start) {
-            ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]);
-            v2Copy(cp0, p);
-        }
-        else {
             if (smooth > 0) {
                 let nextIdx = idx + dir;
-                let nextP = points[nextIdx];
+                let nextX = points[nextIdx * 2];
+                let nextY = points[nextIdx * 2 + 1];
                 if (connectNulls) {
                     // Find next point not null
-                    while (nextP && isPointNull(points[nextIdx])) {
+                    while (isPointNull(nextX, nextY) && (nextIdx < (segLen + 
start)) || (dir < 0 && nextIdx >= start)) {
                         nextIdx += dir;
-                        nextP = points[nextIdx];
+                        nextX = points[nextIdx * 2];
+                        nextY = points[nextIdx * 2 + 1];
                     }
                 }
 
                 let ratioNextSeg = 0.5;
-                const prevP = points[prevIdx];
-                nextP = points[nextIdx];
-                // Last point
-                if (!nextP || isPointNull(nextP)) {
-                    v2Copy(cp1, p);
+                let vx: number = 0;
+                let vy: number = 0;
+                // Is last point
+                if ((dir > 0 && nextIdx >= (segLen + start)) || (dir < 0 && 
nextIdx < start)) {
+                    cpx1 = x;
+                    cpy1 = y;
                 }
                 else {
-                    // If next data is null in not connect case
-                    if (isPointNull(nextP) && !connectNulls) {
-                        nextP = p;
-                    }
-
-                    vec2.sub(v, nextP, prevP);
+                    vx = nextX - px;
+                    vy = nextY - py;
 
+                    const dx0 = x - px;
+                    const dx1 = nextX - x;
+                    const dy0 = y - py;
+                    const dy1 = nextY - y;
                     let lenPrevSeg;
                     let lenNextSeg;
-                    if (smoothMonotone === 'x' || smoothMonotone === 'y') {
-                        const dim = smoothMonotone === 'x' ? 0 : 1;
-                        lenPrevSeg = Math.abs(p[dim] - prevP[dim]);
-                        lenNextSeg = Math.abs(p[dim] - nextP[dim]);
+                    if (smoothMonotone === 'x') {
+                        lenPrevSeg = Math.abs(dx0);
+                        lenNextSeg = Math.abs(dx1);
+                    }
+                    else if (smoothMonotone === 'y') {
+                        lenPrevSeg = Math.abs(dy0);
+                        lenNextSeg = Math.abs(dy1);
                     }
                     else {
-                        lenPrevSeg = vec2.dist(p, prevP);
-                        lenNextSeg = vec2.dist(p, nextP);
+                        lenPrevSeg = Math.sqrt(dx0 * dx0 + dy0 * dy0);
+                        lenNextSeg = Math.sqrt(dx1 * dx1 + dy1 * dy1);
                     }
 
                     // Use ratio of seg length
                     ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);
 
-                    scaleAndAdd(cp1, p, v, -smooth * (1 - ratioNextSeg));
+                    cpx1 = x - vx * smooth * (1 - ratioNextSeg);
+                    cpy1 = y - vy * smooth * (1 - ratioNextSeg);
                 }
                 // Smooth constraint
-                vec2Min(cp0, cp0, smoothMax);
-                vec2Max(cp0, cp0, smoothMin);
-                vec2Min(cp1, cp1, smoothMax);
-                vec2Max(cp1, cp1, smoothMin);
-
-                ctx.bezierCurveTo(
-                    cp0[0], cp0[1],
-                    cp1[0], cp1[1],
-                    p[0], p[1]
-                );
+                cpx0 = mathMin(cpx0, smoothMax[0]);
+                cpy0 = mathMin(cpy0, smoothMax[1]);
+                cpx0 = mathMax(cpx0, smoothMin[0]);
+                cpy0 = mathMax(cpy0, smoothMin[1]);
+
+                cpx1 = mathMin(cpx1, smoothMax[0]);
+                cpy1 = mathMin(cpy1, smoothMax[1]);
+                cpx1 = mathMax(cpx1, smoothMin[0]);
+                cpy1 = mathMax(cpy1, smoothMin[1]);
+
+                ctx.bezierCurveTo(cpx0, cpy0, cpx1, cpy1, x, y);
+
                 // cp0 of next segment
-                scaleAndAdd(cp0, p, v, smooth * ratioNextSeg);
+                cpx0 = x + vx * smooth * ratioNextSeg;
+                cpy0 = y + vy * smooth * ratioNextSeg;
             }
             else {
-                ctx.lineTo(p[0], p[1]);
+                ctx.lineTo(x, y);
             }
         }
 
-        prevIdx = idx;
+        px = x;
+        py = y;
         idx += dir;
     }
 
     return k;
 }
 
-function getBoundingBox(points: number[][], smoothConstraint?: boolean) {
+function getBoundingBox(points: ArrayLike<number>, smoothConstraint?: boolean) 
{
     const ptMin = [Infinity, Infinity];
     const ptMax = [-Infinity, -Infinity];
     if (smoothConstraint) {
-        for (let i = 0; i < points.length; i++) {
-            const pt = points[i];
-            if (pt[0] < ptMin[0]) {
-                ptMin[0] = pt[0];
+        for (let i = 0; i < points.length;) {
+            const x = points[i++];
+            const y = points[i++];
+            if (x < ptMin[0]) {
+                ptMin[0] = x;
             }
-            if (pt[1] < ptMin[1]) {
-                ptMin[1] = pt[1];
+            if (y < ptMin[1]) {
+                ptMin[1] = y;
             }
-            if (pt[0] > ptMax[0]) {
-                ptMax[0] = pt[0];
+            if (x > ptMax[0]) {
+                ptMax[0] = x;
             }
-            if (pt[1] > ptMax[1]) {
-                ptMax[1] = pt[1];
+            if (y > ptMax[1]) {
+                ptMax[1] = y;
             }
         }
     }
@@ -304,7 +192,7 @@ function getBoundingBox(points: number[][], 
smoothConstraint?: boolean) {
 }
 
 class ECPolylineShape {
-    points: number[][];
+    points: ArrayLike<number>;
     smooth = 0;
     smoothConstraint = true;
     smoothMonotone: 'x' | 'y' | 'none';
@@ -346,13 +234,13 @@ export class ECPolyline extends Path<ECPolylineProps> {
 
         if (shape.connectNulls) {
             // Must remove first and last null values avoid draw error in 
polygon
-            for (; len > 0; len--) {
-                if (!isPointNull(points[len - 1])) {
+            for (; len > 0; len -= 2) {
+                if (!isPointNull(points[len - 2], points[len - 1])) {
                     break;
                 }
             }
-            for (; i < len; i++) {
-                if (!isPointNull(points[i])) {
+            for (; i < len; i += 2) {
+                if (!isPointNull(points[i], points[i + 1])) {
                     break;
                 }
             }
@@ -368,7 +256,7 @@ export class ECPolyline extends Path<ECPolylineProps> {
 }
 class ECPolygonShape extends ECPolylineShape {
     // Offset between stacked base points and points
-    stackedOnPoints: number[][];
+    stackedOnPoints: ArrayLike<number>;
     stackedOnSmooth: number;
 }
 
@@ -401,13 +289,13 @@ export class ECPolygon extends Path {
 
         if (shape.connectNulls) {
             // Must remove first and last null values avoid draw error in 
polygon
-            for (; len > 0; len--) {
-                if (!isPointNull(points[len - 1])) {
+            for (; len > 0; len -= 2) {
+                if (!isPointNull(points[len - 2], points[len - 1])) {
                     break;
                 }
             }
-            for (; i < len; i++) {
-                if (!isPointNull(points[i])) {
+            for (; i < len; i += 2) {
+                if (!isPointNull(points[i], points[i + 1])) {
                     break;
                 }
             }
@@ -425,7 +313,7 @@ export class ECPolygon extends Path {
             );
             i += k + 1;
 
-            ctx.closePath();
+            // ctx.closePath();
         }
     }
 }
\ No newline at end of file
diff --git a/src/layout/points.ts b/src/layout/points.ts
index 0808e10..e819581 100644
--- a/src/layout/points.ts
+++ b/src/layout/points.ts
@@ -24,8 +24,10 @@ import createRenderPlanner from 
'../chart/helper/createRenderPlanner';
 import {isDimensionStacked} from '../data/helper/dataStackHelper';
 import SeriesModel from '../model/Series';
 import { StageHandler, ParsedValueNumeric } from '../util/types';
+import { createFloat32Array } from '../util/vendor';
 
-export default function (seriesType?: string): StageHandler {
+
+export default function (seriesType: string, forceStoreInTypedArray?: 
boolean): StageHandler {
     return {
         seriesType: seriesType,
 
@@ -35,7 +37,7 @@ export default function (seriesType?: string): StageHandler {
             const data = seriesModel.getData();
             const coordSys = seriesModel.coordinateSystem;
             const pipelineContext = seriesModel.pipelineContext;
-            const isLargeRender = pipelineContext.large;
+            const useTypedArray = forceStoreInTypedArray || 
pipelineContext.large;
 
             if (!coordSys) {
                 return;
@@ -58,7 +60,7 @@ export default function (seriesType?: string): StageHandler {
             return dimLen && {
                 progress(params, data) {
                     const segCount = params.end - params.start;
-                    const points = isLargeRender && new Float32Array(segCount 
* dimLen);
+                    const points = useTypedArray && 
createFloat32Array(segCount * dimLen);
 
                     const tmpIn: ParsedValueNumeric[] = [];
                     const tmpOut: number[] = [];
@@ -76,7 +78,7 @@ export default function (seriesType?: string): StageHandler {
                             point = !isNaN(x) && !isNaN(y) && 
coordSys.dataToPoint(tmpIn, null, tmpOut);
                         }
 
-                        if (isLargeRender) {
+                        if (useTypedArray) {
                             points[offset++] = point ? point[0] : NaN;
                             points[offset++] = point ? point[1] : NaN;
                         }
@@ -85,7 +87,7 @@ export default function (seriesType?: string): StageHandler {
                         }
                     }
 
-                    isLargeRender && data.setLayout('symbolPoints', points);
+                    useTypedArray && data.setLayout('points', points);
                 }
             };
         }
diff --git a/src/chart/line.ts b/src/util/vendor.ts
similarity index 60%
copy from src/chart/line.ts
copy to src/util/vendor.ts
index 541b481..ab4db32 100644
--- a/src/chart/line.ts
+++ b/src/util/vendor.ts
@@ -17,21 +17,18 @@
 * under the License.
 */
 
-import * as echarts from '../echarts';
+import { isArray } from 'zrender/src/core/util';
 
-import './line/LineSeries';
-import './line/LineView';
+/* global Float32Array */
+const supportFloat32Array = typeof Float32Array !== 'undefined';
 
-import layoutPoints from '../layout/points';
-import dataSample from '../processor/dataSample';
+const Float32ArrayCtor = !supportFloat32Array ? Array : Float32Array;
 
-// In case developer forget to include grid component
-import '../component/gridSimple';
-
-echarts.registerLayout(layoutPoints('line'));
-
-// Down sample after filter
-echarts.registerProcessor(
-    echarts.PRIORITY.PROCESSOR.STATISTIC,
-    dataSample('line')
-);
+export function createFloat32Array(arg: number | number[]): number[] | 
Float32Array {
+    if (isArray(arg)) {
+        // Return self directly if don't support TypedArray.
+        return supportFloat32Array ? new Float32Array(arg) : arg;
+    }
+    // Else is number
+    return new Float32ArrayCtor(arg);
+}
\ No newline at end of file
diff --git a/test/largeLine.html b/test/largeLine.html
index dba5cd3..9a8cd9b 100644
--- a/test/largeLine.html
+++ b/test/largeLine.html
@@ -47,8 +47,8 @@ under the License.
             ], function (echarts) {
                 var myChart;
                 var lineCount = 20;
-                var pointCount = 1000;
-                var chartCount = 50;
+                var pointCount = 10000;
+                var chartCount = 20;
 
                 var option = {
                     tooltip : {
@@ -92,8 +92,7 @@ under the License.
                 var oneDay = 24 * 3600 * 1000;
                 var base = +new Date(1897, 9, 3);
                 for (var j = 0; j < pointCount; j++) {
-                    var now = new Date(base += oneDay);
-                    date.push([now.getFullYear(), now.getMonth() + 1, 
now.getDate()].join('-'));
+                    date.push(base += oneDay);
                 }
                 for (var i = 0; i < lineCount; i++) {
                     var y = Math.random() * 1000;
@@ -101,11 +100,9 @@ under the License.
                     for (var j = 0; j < pointCount; j++) {
                         y += Math.round(10 + Math.random() * (-10 - 10));
                         values.push(
-                            [
-                                date[j],
-                                // Math.random() < 0.1 ? '-' : y
-                                y
-                            ]
+                            date[j],
+                            // Math.random() < 0.1 ? '-' : y
+                            y
                         );
                     }
 
@@ -113,10 +110,14 @@ under the License.
                   option.series.push({
                     name: 'line' + i,
                     type: 'line',
-                    sampling: 'average',
                     hoverAnimation: false,
                     showSymbol: false,
-                    data: values,
+                    dimensions: ['date', 'value'],
+                    encode: {
+                        x: 'date',
+                        y: 'value'
+                    },
+                    data: new Float64Array(values),
                     lineStyle: lineStyle
                   });
                 }
@@ -131,9 +132,10 @@ under the License.
                       myChart = 
echarts.init(document.getElementById('chart'+n));
                       myChart.setOption(option, true);
                     }
-                    var end = new Date();
-
-                    document.getElementById('timing').innerHTML = 'Graphs 
loaded in ' + ( end - start ) + ' ms.';
+                    setTimeout(function () {
+                        var end = new Date();
+                        document.getElementById('timing').innerHTML = 'Graphs 
loaded in ' + ( end - start ) + ' ms.';
+                    });
                 };
 
                 refresh();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to