poly.ts 9.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

L
lang 已提交
20 21
// Poly path support NaN point

P
pissang 已提交
22
import Path, { PathProps } from 'zrender/src/graphic/Path';
S
sushuang 已提交
23

24 25
const mathMin = Math.min;
const mathMax = Math.max;
S
sushuang 已提交
26

27 28
function isPointNull(x: number, y: number) {
    return isNaN(x) || isNaN(y);
S
sushuang 已提交
29
}
O
Ovilia 已提交
30
/**
31 32 33
 * 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.
O
Ovilia 已提交
34
 */
35
function drawSegment(
P
pissang 已提交
36
    ctx: CanvasRenderingContext2D,
37
    points: ArrayLike<number>,
P
pissang 已提交
38 39 40 41 42 43 44
    start: number,
    segLen: number,
    allLen: number,
    dir: number,
    smooth: number,
    smoothMonotone: 'x' | 'y' | 'none',
    connectNulls: boolean
45
) {
46 47
    let prevX: number;
    let prevY: number;
48 49 50 51
    let cpx0: number;
    let cpy0: number;
    let cpx1: number;
    let cpy1: number;
52 53
    let idx = start;
    let k = 0;
1
100pah 已提交
54
    for (; k < segLen; k++) {
55 56

        const x = points[idx * 2];
57
        const y = points[idx * 2 + 1];
58

59 60 61
        if (idx >= allLen || idx < 0) {
            break;
        }
62
        if (isPointNull(x, y)) {
63 64 65 66 67 68 69 70
            if (connectNulls) {
                idx += dir;
                continue;
            }
            break;
        }

        if (idx === start) {
71 72 73
            ctx[dir > 0 ? 'moveTo' : 'lineTo'](x, y);
            cpx0 = x;
            cpy0 = y;
74 75
        }
        else {
76 77
            const dx = x - prevX;
            const dy = y - prevY;
78

79
            // Ignore tiny segment.
80
            if ((dx * dx + dy * dy) < 0.5) {
S
sushuang 已提交
81 82
                idx += dir;
                continue;
L
lang 已提交
83
            }
S
sushuang 已提交
84 85

            if (smooth > 0) {
86
                let nextIdx = idx + dir;
87 88
                let nextX = points[nextIdx * 2];
                let nextY = points[nextIdx * 2 + 1];
89
                let tmpK = k + 1;
L
lang 已提交
90
                if (connectNulls) {
S
sushuang 已提交
91
                    // Find next point not null
92 93
                    while (isPointNull(nextX, nextY) && tmpK < segLen) {
                        tmpK++;
S
sushuang 已提交
94
                        nextIdx += dir;
95 96
                        nextX = points[nextIdx * 2];
                        nextY = points[nextIdx * 2 + 1];
S
sushuang 已提交
97
                    }
L
lang 已提交
98
                }
L
lang 已提交
99

100
                let ratioNextSeg = 0.5;
101 102
                let vx: number = 0;
                let vy: number = 0;
103 104
                let nextCpx0;
                let nextCpy0;
105
                // Is last point
106
                if (tmpK >= segLen || isPointNull(nextX, nextY)) {
107 108
                    cpx1 = x;
                    cpy1 = y;
S
sushuang 已提交
109 110
                }
                else {
111 112
                    vx = nextX - prevX;
                    vy = nextY - prevY;
S
sushuang 已提交
113

114
                    const dx0 = x - prevX;
115
                    const dx1 = nextX - x;
116
                    const dy0 = y - prevY;
117
                    const dy1 = nextY - y;
118 119
                    let lenPrevSeg;
                    let lenNextSeg;
120 121 122
                    if (smoothMonotone === 'x') {
                        lenPrevSeg = Math.abs(dx0);
                        lenNextSeg = Math.abs(dx1);
123 124 125 126
                        cpx1 = x - lenPrevSeg * smooth;
                        cpy1 = y;
                        nextCpx0 = x + lenPrevSeg * smooth;
                        nextCpy0 = y;
127 128 129 130
                    }
                    else if (smoothMonotone === 'y') {
                        lenPrevSeg = Math.abs(dy0);
                        lenNextSeg = Math.abs(dy1);
131 132 133 134
                        cpx1 = x;
                        cpy1 = y - lenPrevSeg * smooth;
                        nextCpx0 = x;
                        nextCpy0 = y + lenPrevSeg * smooth;
L
lang 已提交
135 136
                    }
                    else {
137 138
                        lenPrevSeg = Math.sqrt(dx0 * dx0 + dy0 * dy0);
                        lenNextSeg = Math.sqrt(dx1 * dx1 + dy1 * dy1);
S
sushuang 已提交
139

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
                        // Use ratio of seg length
                        ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg);

                        cpx1 = x - vx * smooth * (1 - ratioNextSeg);
                        cpy1 = y - vy * smooth * (1 - ratioNextSeg);

                        // cp0 of next segment
                        nextCpx0 = x + vx * smooth * ratioNextSeg;
                        nextCpy0 = y + vy * smooth * ratioNextSeg;

                        // Smooth constraint between point and next point.
                        // Avoid exceeding extreme after smoothing.
                        nextCpx0 = mathMin(nextCpx0, mathMax(nextX, x));
                        nextCpy0 = mathMin(nextCpy0, mathMax(nextY, y));
                        nextCpx0 = mathMax(nextCpx0, mathMin(nextX, x));
                        nextCpy0 = mathMax(nextCpy0, mathMin(nextY, y));
                        // Reclaculate cp1 based on the adjusted cp0 of next seg.
                        vx = nextCpx0 - x;
                        vy = nextCpy0 - y;

                        cpx1 = x - vx * lenPrevSeg / lenNextSeg;
                        cpy1 = y - vy * lenPrevSeg / lenNextSeg;

                        // Smooth constraint between point and prev point.
                        // Avoid exceeding extreme after smoothing.
                        cpx1 = mathMin(cpx1, mathMax(prevX, x));
                        cpy1 = mathMin(cpy1, mathMax(prevY, y));
                        cpx1 = mathMax(cpx1, mathMin(prevX, x));
                        cpy1 = mathMax(cpy1, mathMin(prevY, y));

                        // Adjust next cp0 again.
                        vx = x - cpx1;
                        vy = y - cpy1;
                        nextCpx0 = x + vx * lenNextSeg / lenPrevSeg;
                        nextCpy0 = y + vy * lenNextSeg / lenPrevSeg;
                    }
L
lang 已提交
176
                }
177 178 179

                ctx.bezierCurveTo(cpx0, cpy0, cpx1, cpy1, x, y);

180 181
                cpx0 = nextCpx0;
                cpy0 = nextCpy0;
S
sushuang 已提交
182 183
            }
            else {
184
                ctx.lineTo(x, y);
L
lang 已提交
185 186 187
            }
        }

188 189
        prevX = x;
        prevY = y;
S
sushuang 已提交
190
        idx += dir;
L
lang 已提交
191
    }
L
lang 已提交
192

S
sushuang 已提交
193 194 195
    return k;
}

P
pissang 已提交
196
class ECPolylineShape {
197
    points: ArrayLike<number>;
1
100pah 已提交
198 199 200
    smooth = 0;
    smoothConstraint = true;
    smoothMonotone: 'x' | 'y' | 'none';
201
    connectNulls: boolean;
P
pissang 已提交
202
}
L
lang 已提交
203

P
pissang 已提交
204 205 206
interface ECPolylineProps extends PathProps {
    shape?: Partial<ECPolylineShape>
}
207

P
pissang 已提交
208
export class ECPolyline extends Path<ECPolylineProps> {
L
lang 已提交
209

1
100pah 已提交
210
    readonly type = 'ec-polyline';
L
lang 已提交
211

1
100pah 已提交
212
    shape: ECPolylineShape;
L
lang 已提交
213

P
pissang 已提交
214
    constructor(opts?: ECPolylineProps) {
P
pissang 已提交
215 216 217 218 219
        super(opts);
    }

    getDefaultStyle() {
        return {
P
pissang 已提交
220
            stroke: '#000',
P
pissang 已提交
221 222 223 224 225 226
            fill: null as string
        };
    }

    getDefaultShape() {
        return new ECPolylineShape();
P
pissang 已提交
227
    }
228

P
pissang 已提交
229
    buildPath(ctx: CanvasRenderingContext2D, shape: ECPolylineShape) {
230
        const points = shape.points;
L
lang 已提交
231

232
        let i = 0;
233
        let len = points.length / 2;
L
lang 已提交
234

235
        // const result = getBoundingBox(points, shape.smoothConstraint);
236

S
sushuang 已提交
237 238
        if (shape.connectNulls) {
            // Must remove first and last null values avoid draw error in polygon
239 240
            for (; len > 0; len--) {
                if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {
S
sushuang 已提交
241
                    break;
L
lang 已提交
242
                }
S
sushuang 已提交
243
            }
244 245
            for (; i < len; i++) {
                if (!isPointNull(points[i * 2], points[i * 2 + 1])) {
S
sushuang 已提交
246
                    break;
L
lang 已提交
247 248
                }
            }
S
sushuang 已提交
249 250 251 252
        }
        while (i < len) {
            i += drawSegment(
                ctx, points, i, len, len,
253 254 255
                1,
                // result.min, result.max,
                shape.smooth,
S
sushuang 已提交
256 257 258 259
                shape.smoothMonotone, shape.connectNulls
            ) + 1;
        }
    }
P
pissang 已提交
260 261 262
}
class ECPolygonShape extends ECPolylineShape {
    // Offset between stacked base points and points
263
    stackedOnPoints: ArrayLike<number>;
1
100pah 已提交
264
    stackedOnSmooth: number;
P
pissang 已提交
265
}
266

P
pissang 已提交
267 268 269 270
interface ECPolygonProps extends PathProps {
    shape?: Partial<ECPolygonShape>
}
export class ECPolygon extends Path {
271

1
100pah 已提交
272
    readonly type = 'ec-polygon';
L
lang 已提交
273

1
100pah 已提交
274
    shape: ECPolygonShape;
L
lang 已提交
275

P
pissang 已提交
276
    constructor(opts?: ECPolygonProps) {
P
pissang 已提交
277 278 279 280 281
        super(opts);
    }

    getDefaultShape() {
        return new ECPolygonShape();
P
pissang 已提交
282
    }
P
pissang 已提交
283

P
pissang 已提交
284
    buildPath(ctx: CanvasRenderingContext2D, shape: ECPolygonShape) {
285 286
        const points = shape.points;
        const stackedOnPoints = shape.stackedOnPoints;
L
lang 已提交
287

288
        let i = 0;
289
        let len = points.length / 2;
290
        const smoothMonotone = shape.smoothMonotone;
L
lang 已提交
291

S
sushuang 已提交
292 293
        if (shape.connectNulls) {
            // Must remove first and last null values avoid draw error in polygon
294 295
            for (; len > 0; len--) {
                if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) {
S
sushuang 已提交
296
                    break;
L
lang 已提交
297
                }
S
sushuang 已提交
298
            }
299 300
            for (; i < len; i++) {
                if (!isPointNull(points[i * 2], points[i * 2 + 1])) {
S
sushuang 已提交
301
                    break;
L
lang 已提交
302 303
                }
            }
S
sushuang 已提交
304 305
        }
        while (i < len) {
306
            const k = drawSegment(
S
sushuang 已提交
307
                ctx, points, i, len, len,
308 309 310
                1,
                // bbox.min, bbox.max,
                shape.smooth,
S
sushuang 已提交
311 312 313 314
                smoothMonotone, shape.connectNulls
            );
            drawSegment(
                ctx, stackedOnPoints, i + k - 1, k, len,
315 316 317
                -1,
                // stackedOnBBox.min, stackedOnBBox.max,
                shape.stackedOnSmooth,
S
sushuang 已提交
318 319 320 321
                smoothMonotone, shape.connectNulls
            );
            i += k + 1;

322
            ctx.closePath();
S
sushuang 已提交
323 324
        }
    }
P
pissang 已提交
325
}