| /* |
| * 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. |
| */ |
| // Poly path support NaN point |
| import Path from 'zrender/src/graphic/Path'; |
| import * as vec2 from 'zrender/src/core/vector'; |
| import fixClipWithShadow from 'zrender/src/graphic/helper/fixClipWithShadow'; |
| var vec2Min = vec2.min; |
| var vec2Max = vec2.max; |
| var scaleAndAdd = vec2.scaleAndAdd; |
| var v2Copy = vec2.copy; // Temporary variable |
| |
| var v = []; |
| var cp0 = []; |
| var cp1 = []; |
| |
| function isPointNull(p) { |
| return isNaN(p[0]) || isNaN(p[1]); |
| } |
| |
| function drawSegment(ctx, points, start, segLen, allLen, dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls) { |
| // if (smoothMonotone == null) { |
| // if (isMono(points, 'x')) { |
| // return drawMono(ctx, points, start, segLen, allLen, |
| // dir, smoothMin, smoothMax, smooth, 'x', connectNulls); |
| // } |
| // else if (isMono(points, 'y')) { |
| // return drawMono(ctx, points, start, segLen, allLen, |
| // dir, smoothMin, smoothMax, smooth, 'y', connectNulls); |
| // } |
| // else { |
| // return drawNonMono.apply(this, arguments); |
| // } |
| // } |
| // else if (smoothMonotone !== 'none' && isMono(points, smoothMonotone)) { |
| // return drawMono.apply(this, arguments); |
| // } |
| // else { |
| // return drawNonMono.apply(this, arguments); |
| // } |
| if (smoothMonotone === 'none' || !smoothMonotone) { |
| return drawNonMono.apply(this, arguments); |
| } else { |
| return drawMono.apply(this, arguments); |
| } |
| } |
| /** |
| * 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; |
| // } |
| // var dim = smoothMonotone === 'x' ? 0 : 1; |
| // var last = points[0][dim]; |
| // var lastDiff = 0; |
| // for (var i = 1; i < points.length; ++i) { |
| // var 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. |
| */ |
| |
| |
| function drawMono(ctx, points, start, segLen, allLen, dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls) { |
| var prevIdx = 0; |
| var idx = start; |
| |
| for (var k = 0; k < segLen; k++) { |
| var p = points[idx]; |
| |
| if (idx >= allLen || idx < 0) { |
| break; |
| } |
| |
| if (isPointNull(p)) { |
| if (connectNulls) { |
| idx += dir; |
| continue; |
| } |
| |
| break; |
| } |
| |
| if (idx === start) { |
| ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]); |
| } else { |
| if (smooth > 0) { |
| var prevP = points[prevIdx]; |
| var dim = smoothMonotone === 'y' ? 1 : 0; // Length of control point to p, either in x or y, but not both |
| |
| var 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; |
| } |
| |
| 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, points, start, segLen, allLen, dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls) { |
| var prevIdx = 0; |
| var idx = start; |
| |
| for (var k = 0; k < segLen; k++) { |
| var p = points[idx]; |
| |
| if (idx >= allLen || idx < 0) { |
| break; |
| } |
| |
| if (isPointNull(p)) { |
| if (connectNulls) { |
| idx += dir; |
| continue; |
| } |
| |
| break; |
| } |
| |
| if (idx === start) { |
| ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]); |
| v2Copy(cp0, p); |
| } else { |
| if (smooth > 0) { |
| var nextIdx = idx + dir; |
| var nextP = points[nextIdx]; |
| |
| if (connectNulls) { |
| // Find next point not null |
| while (nextP && isPointNull(points[nextIdx])) { |
| nextIdx += dir; |
| nextP = points[nextIdx]; |
| } |
| } |
| |
| var ratioNextSeg = 0.5; |
| var prevP = points[prevIdx]; |
| var nextP = points[nextIdx]; // Last point |
| |
| if (!nextP || isPointNull(nextP)) { |
| v2Copy(cp1, p); |
| } else { |
| // If next data is null in not connect case |
| if (isPointNull(nextP) && !connectNulls) { |
| nextP = p; |
| } |
| |
| vec2.sub(v, nextP, prevP); |
| var lenPrevSeg; |
| var lenNextSeg; |
| |
| if (smoothMonotone === 'x' || smoothMonotone === 'y') { |
| var dim = smoothMonotone === 'x' ? 0 : 1; |
| lenPrevSeg = Math.abs(p[dim] - prevP[dim]); |
| lenNextSeg = Math.abs(p[dim] - nextP[dim]); |
| } else { |
| lenPrevSeg = vec2.dist(p, prevP); |
| lenNextSeg = vec2.dist(p, nextP); |
| } // Use ratio of seg length |
| |
| |
| ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg); |
| scaleAndAdd(cp1, p, v, -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]); // cp0 of next segment |
| |
| scaleAndAdd(cp0, p, v, smooth * ratioNextSeg); |
| } else { |
| ctx.lineTo(p[0], p[1]); |
| } |
| } |
| |
| prevIdx = idx; |
| idx += dir; |
| } |
| |
| return k; |
| } |
| |
| function getBoundingBox(points, smoothConstraint) { |
| var ptMin = [Infinity, Infinity]; |
| var ptMax = [-Infinity, -Infinity]; |
| |
| if (smoothConstraint) { |
| for (var i = 0; i < points.length; i++) { |
| var pt = points[i]; |
| |
| if (pt[0] < ptMin[0]) { |
| ptMin[0] = pt[0]; |
| } |
| |
| if (pt[1] < ptMin[1]) { |
| ptMin[1] = pt[1]; |
| } |
| |
| if (pt[0] > ptMax[0]) { |
| ptMax[0] = pt[0]; |
| } |
| |
| if (pt[1] > ptMax[1]) { |
| ptMax[1] = pt[1]; |
| } |
| } |
| } |
| |
| return { |
| min: smoothConstraint ? ptMin : ptMax, |
| max: smoothConstraint ? ptMax : ptMin |
| }; |
| } |
| |
| export var Polyline = Path.extend({ |
| type: 'ec-polyline', |
| shape: { |
| points: [], |
| smooth: 0, |
| smoothConstraint: true, |
| smoothMonotone: null, |
| connectNulls: false |
| }, |
| style: { |
| fill: null, |
| stroke: '#000' |
| }, |
| brush: fixClipWithShadow(Path.prototype.brush), |
| buildPath: function (ctx, shape) { |
| var points = shape.points; |
| var i = 0; |
| var len = points.length; |
| var result = getBoundingBox(points, shape.smoothConstraint); |
| |
| if (shape.connectNulls) { |
| // Must remove first and last null values avoid draw error in polygon |
| for (; len > 0; len--) { |
| if (!isPointNull(points[len - 1])) { |
| break; |
| } |
| } |
| |
| for (; i < len; i++) { |
| if (!isPointNull(points[i])) { |
| break; |
| } |
| } |
| } |
| |
| while (i < len) { |
| i += drawSegment(ctx, points, i, len, len, 1, result.min, result.max, shape.smooth, shape.smoothMonotone, shape.connectNulls) + 1; |
| } |
| } |
| }); |
| export var Polygon = Path.extend({ |
| type: 'ec-polygon', |
| shape: { |
| points: [], |
| // Offset between stacked base points and points |
| stackedOnPoints: [], |
| smooth: 0, |
| stackedOnSmooth: 0, |
| smoothConstraint: true, |
| smoothMonotone: null, |
| connectNulls: false |
| }, |
| brush: fixClipWithShadow(Path.prototype.brush), |
| buildPath: function (ctx, shape) { |
| var points = shape.points; |
| var stackedOnPoints = shape.stackedOnPoints; |
| var i = 0; |
| var len = points.length; |
| var smoothMonotone = shape.smoothMonotone; |
| var bbox = getBoundingBox(points, shape.smoothConstraint); |
| var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint); |
| |
| if (shape.connectNulls) { |
| // Must remove first and last null values avoid draw error in polygon |
| for (; len > 0; len--) { |
| if (!isPointNull(points[len - 1])) { |
| break; |
| } |
| } |
| |
| for (; i < len; i++) { |
| if (!isPointNull(points[i])) { |
| break; |
| } |
| } |
| } |
| |
| while (i < len) { |
| var k = drawSegment(ctx, points, i, len, len, 1, bbox.min, bbox.max, shape.smooth, smoothMonotone, shape.connectNulls); |
| drawSegment(ctx, stackedOnPoints, i + k - 1, k, len, -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth, smoothMonotone, shape.connectNulls); |
| i += k + 1; |
| ctx.closePath(); |
| } |
| } |
| }); |