// Poly path support NaN point
define(function (require) {
var Path = require('zrender/graphic/Path');
var vec2 = require('zrender/core/vector');
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
) {
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
};
}
return {
Polyline: Path.extend({
type: 'ec-polyline',
shape: {
points: [],
smooth: 0,
smoothConstraint: true,
smoothMonotone: null,
connectNulls: false
},
style: {
fill: null,
stroke: '#000'
},
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;
}
}
}),
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
},
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();
}
}
})
};
});
|