250 lines
7.7 KiB
Plaintext
250 lines
7.7 KiB
Plaintext
// 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();
|
|
}
|
|
}
|
|
})
|
|
};
|
|
}); |