project/node_modules/zrender/lib/core/PathProxy.js

698 lines
24 KiB
JavaScript

import * as vec2 from './vector.js';
import BoundingRect from './BoundingRect.js';
import { devicePixelRatio as dpr } from '../config.js';
import { fromLine, fromCubic, fromQuadratic, fromArc } from './bbox.js';
import { cubicLength, cubicSubdivide, quadraticLength, quadraticSubdivide } from './curve.js';
var CMD = {
M: 1,
L: 2,
C: 3,
Q: 4,
A: 5,
Z: 6,
R: 7
};
var tmpOutX = [];
var tmpOutY = [];
var min = [];
var max = [];
var min2 = [];
var max2 = [];
var mathMin = Math.min;
var mathMax = Math.max;
var mathCos = Math.cos;
var mathSin = Math.sin;
var mathAbs = Math.abs;
var PI = Math.PI;
var PI2 = PI * 2;
var hasTypedArray = typeof Float32Array !== 'undefined';
var tmpAngles = [];
function modPI2(radian) {
var n = Math.round(radian / PI * 1e8) / 1e8;
return (n % 2) * PI;
}
export function normalizeArcAngles(angles, anticlockwise) {
var newStartAngle = modPI2(angles[0]);
if (newStartAngle < 0) {
newStartAngle += PI2;
}
var delta = newStartAngle - angles[0];
var newEndAngle = angles[1];
newEndAngle += delta;
if (!anticlockwise && newEndAngle - newStartAngle >= PI2) {
newEndAngle = newStartAngle + PI2;
}
else if (anticlockwise && newStartAngle - newEndAngle >= PI2) {
newEndAngle = newStartAngle - PI2;
}
else if (!anticlockwise && newStartAngle > newEndAngle) {
newEndAngle = newStartAngle + (PI2 - modPI2(newStartAngle - newEndAngle));
}
else if (anticlockwise && newStartAngle < newEndAngle) {
newEndAngle = newStartAngle - (PI2 - modPI2(newEndAngle - newStartAngle));
}
angles[0] = newStartAngle;
angles[1] = newEndAngle;
}
var PathProxy = (function () {
function PathProxy(notSaveData) {
this.dpr = 1;
this._xi = 0;
this._yi = 0;
this._x0 = 0;
this._y0 = 0;
this._len = 0;
if (notSaveData) {
this._saveData = false;
}
if (this._saveData) {
this.data = [];
}
}
PathProxy.prototype.increaseVersion = function () {
this._version++;
};
PathProxy.prototype.getVersion = function () {
return this._version;
};
PathProxy.prototype.setScale = function (sx, sy, segmentIgnoreThreshold) {
segmentIgnoreThreshold = segmentIgnoreThreshold || 0;
if (segmentIgnoreThreshold > 0) {
this._ux = mathAbs(segmentIgnoreThreshold / dpr / sx) || 0;
this._uy = mathAbs(segmentIgnoreThreshold / dpr / sy) || 0;
}
};
PathProxy.prototype.setDPR = function (dpr) {
this.dpr = dpr;
};
PathProxy.prototype.setContext = function (ctx) {
this._ctx = ctx;
};
PathProxy.prototype.getContext = function () {
return this._ctx;
};
PathProxy.prototype.beginPath = function () {
this._ctx && this._ctx.beginPath();
this.reset();
return this;
};
PathProxy.prototype.reset = function () {
if (this._saveData) {
this._len = 0;
}
if (this._pathSegLen) {
this._pathSegLen = null;
this._pathLen = 0;
}
this._version++;
};
PathProxy.prototype.moveTo = function (x, y) {
this._drawPendingPt();
this.addData(CMD.M, x, y);
this._ctx && this._ctx.moveTo(x, y);
this._x0 = x;
this._y0 = y;
this._xi = x;
this._yi = y;
return this;
};
PathProxy.prototype.lineTo = function (x, y) {
var dx = mathAbs(x - this._xi);
var dy = mathAbs(y - this._yi);
var exceedUnit = dx > this._ux || dy > this._uy;
this.addData(CMD.L, x, y);
if (this._ctx && exceedUnit) {
this._ctx.lineTo(x, y);
}
if (exceedUnit) {
this._xi = x;
this._yi = y;
this._pendingPtDist = 0;
}
else {
var d2 = dx * dx + dy * dy;
if (d2 > this._pendingPtDist) {
this._pendingPtX = x;
this._pendingPtY = y;
this._pendingPtDist = d2;
}
}
return this;
};
PathProxy.prototype.bezierCurveTo = function (x1, y1, x2, y2, x3, y3) {
this._drawPendingPt();
this.addData(CMD.C, x1, y1, x2, y2, x3, y3);
if (this._ctx) {
this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
}
this._xi = x3;
this._yi = y3;
return this;
};
PathProxy.prototype.quadraticCurveTo = function (x1, y1, x2, y2) {
this._drawPendingPt();
this.addData(CMD.Q, x1, y1, x2, y2);
if (this._ctx) {
this._ctx.quadraticCurveTo(x1, y1, x2, y2);
}
this._xi = x2;
this._yi = y2;
return this;
};
PathProxy.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) {
this._drawPendingPt();
tmpAngles[0] = startAngle;
tmpAngles[1] = endAngle;
normalizeArcAngles(tmpAngles, anticlockwise);
startAngle = tmpAngles[0];
endAngle = tmpAngles[1];
var delta = endAngle - startAngle;
this.addData(CMD.A, cx, cy, r, r, startAngle, delta, 0, anticlockwise ? 0 : 1);
this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
this._xi = mathCos(endAngle) * r + cx;
this._yi = mathSin(endAngle) * r + cy;
return this;
};
PathProxy.prototype.arcTo = function (x1, y1, x2, y2, radius) {
this._drawPendingPt();
if (this._ctx) {
this._ctx.arcTo(x1, y1, x2, y2, radius);
}
return this;
};
PathProxy.prototype.rect = function (x, y, w, h) {
this._drawPendingPt();
this._ctx && this._ctx.rect(x, y, w, h);
this.addData(CMD.R, x, y, w, h);
return this;
};
PathProxy.prototype.closePath = function () {
this._drawPendingPt();
this.addData(CMD.Z);
var ctx = this._ctx;
var x0 = this._x0;
var y0 = this._y0;
if (ctx) {
ctx.closePath();
}
this._xi = x0;
this._yi = y0;
return this;
};
PathProxy.prototype.fill = function (ctx) {
ctx && ctx.fill();
this.toStatic();
};
PathProxy.prototype.stroke = function (ctx) {
ctx && ctx.stroke();
this.toStatic();
};
PathProxy.prototype.len = function () {
return this._len;
};
PathProxy.prototype.setData = function (data) {
var len = data.length;
if (!(this.data && this.data.length === len) && hasTypedArray) {
this.data = new Float32Array(len);
}
for (var i = 0; i < len; i++) {
this.data[i] = data[i];
}
this._len = len;
};
PathProxy.prototype.appendPath = function (path) {
if (!(path instanceof Array)) {
path = [path];
}
var len = path.length;
var appendSize = 0;
var offset = this._len;
for (var i = 0; i < len; i++) {
appendSize += path[i].len();
}
if (hasTypedArray && (this.data instanceof Float32Array)) {
this.data = new Float32Array(offset + appendSize);
}
for (var i = 0; i < len; i++) {
var appendPathData = path[i].data;
for (var k = 0; k < appendPathData.length; k++) {
this.data[offset++] = appendPathData[k];
}
}
this._len = offset;
};
PathProxy.prototype.addData = function (cmd, a, b, c, d, e, f, g, h) {
if (!this._saveData) {
return;
}
var data = this.data;
if (this._len + arguments.length > data.length) {
this._expandData();
data = this.data;
}
for (var i = 0; i < arguments.length; i++) {
data[this._len++] = arguments[i];
}
};
PathProxy.prototype._drawPendingPt = function () {
if (this._pendingPtDist > 0) {
this._ctx && this._ctx.lineTo(this._pendingPtX, this._pendingPtY);
this._pendingPtDist = 0;
}
};
PathProxy.prototype._expandData = function () {
if (!(this.data instanceof Array)) {
var newData = [];
for (var i = 0; i < this._len; i++) {
newData[i] = this.data[i];
}
this.data = newData;
}
};
PathProxy.prototype.toStatic = function () {
if (!this._saveData) {
return;
}
this._drawPendingPt();
var data = this.data;
if (data instanceof Array) {
data.length = this._len;
if (hasTypedArray && this._len > 11) {
this.data = new Float32Array(data);
}
}
};
PathProxy.prototype.getBoundingRect = function () {
min[0] = min[1] = min2[0] = min2[1] = Number.MAX_VALUE;
max[0] = max[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
var data = this.data;
var xi = 0;
var yi = 0;
var x0 = 0;
var y0 = 0;
var i;
for (i = 0; i < this._len;) {
var cmd = data[i++];
var isFirst = i === 1;
if (isFirst) {
xi = data[i];
yi = data[i + 1];
x0 = xi;
y0 = yi;
}
switch (cmd) {
case CMD.M:
xi = x0 = data[i++];
yi = y0 = data[i++];
min2[0] = x0;
min2[1] = y0;
max2[0] = x0;
max2[1] = y0;
break;
case CMD.L:
fromLine(xi, yi, data[i], data[i + 1], min2, max2);
xi = data[i++];
yi = data[i++];
break;
case CMD.C:
fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2);
xi = data[i++];
yi = data[i++];
break;
case CMD.Q:
fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2);
xi = data[i++];
yi = data[i++];
break;
case CMD.A:
var cx = data[i++];
var cy = data[i++];
var rx = data[i++];
var ry = data[i++];
var startAngle = data[i++];
var endAngle = data[i++] + startAngle;
i += 1;
var anticlockwise = !data[i++];
if (isFirst) {
x0 = mathCos(startAngle) * rx + cx;
y0 = mathSin(startAngle) * ry + cy;
}
fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2);
xi = mathCos(endAngle) * rx + cx;
yi = mathSin(endAngle) * ry + cy;
break;
case CMD.R:
x0 = xi = data[i++];
y0 = yi = data[i++];
var width = data[i++];
var height = data[i++];
fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
break;
case CMD.Z:
xi = x0;
yi = y0;
break;
}
vec2.min(min, min, min2);
vec2.max(max, max, max2);
}
if (i === 0) {
min[0] = min[1] = max[0] = max[1] = 0;
}
return new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]);
};
PathProxy.prototype._calculateLength = function () {
var data = this.data;
var len = this._len;
var ux = this._ux;
var uy = this._uy;
var xi = 0;
var yi = 0;
var x0 = 0;
var y0 = 0;
if (!this._pathSegLen) {
this._pathSegLen = [];
}
var pathSegLen = this._pathSegLen;
var pathTotalLen = 0;
var segCount = 0;
for (var i = 0; i < len;) {
var cmd = data[i++];
var isFirst = i === 1;
if (isFirst) {
xi = data[i];
yi = data[i + 1];
x0 = xi;
y0 = yi;
}
var l = -1;
switch (cmd) {
case CMD.M:
xi = x0 = data[i++];
yi = y0 = data[i++];
break;
case CMD.L: {
var x2 = data[i++];
var y2 = data[i++];
var dx = x2 - xi;
var dy = y2 - yi;
if (mathAbs(dx) > ux || mathAbs(dy) > uy || i === len - 1) {
l = Math.sqrt(dx * dx + dy * dy);
xi = x2;
yi = y2;
}
break;
}
case CMD.C: {
var x1 = data[i++];
var y1 = data[i++];
var x2 = data[i++];
var y2 = data[i++];
var x3 = data[i++];
var y3 = data[i++];
l = cubicLength(xi, yi, x1, y1, x2, y2, x3, y3, 10);
xi = x3;
yi = y3;
break;
}
case CMD.Q: {
var x1 = data[i++];
var y1 = data[i++];
var x2 = data[i++];
var y2 = data[i++];
l = quadraticLength(xi, yi, x1, y1, x2, y2, 10);
xi = x2;
yi = y2;
break;
}
case CMD.A:
var cx = data[i++];
var cy = data[i++];
var rx = data[i++];
var ry = data[i++];
var startAngle = data[i++];
var delta = data[i++];
var endAngle = delta + startAngle;
i += 1;
if (isFirst) {
x0 = mathCos(startAngle) * rx + cx;
y0 = mathSin(startAngle) * ry + cy;
}
l = mathMax(rx, ry) * mathMin(PI2, Math.abs(delta));
xi = mathCos(endAngle) * rx + cx;
yi = mathSin(endAngle) * ry + cy;
break;
case CMD.R: {
x0 = xi = data[i++];
y0 = yi = data[i++];
var width = data[i++];
var height = data[i++];
l = width * 2 + height * 2;
break;
}
case CMD.Z: {
var dx = x0 - xi;
var dy = y0 - yi;
l = Math.sqrt(dx * dx + dy * dy);
xi = x0;
yi = y0;
break;
}
}
if (l >= 0) {
pathSegLen[segCount++] = l;
pathTotalLen += l;
}
}
this._pathLen = pathTotalLen;
return pathTotalLen;
};
PathProxy.prototype.rebuildPath = function (ctx, percent) {
var d = this.data;
var ux = this._ux;
var uy = this._uy;
var len = this._len;
var x0;
var y0;
var xi;
var yi;
var x;
var y;
var drawPart = percent < 1;
var pathSegLen;
var pathTotalLen;
var accumLength = 0;
var segCount = 0;
var displayedLength;
var pendingPtDist = 0;
var pendingPtX;
var pendingPtY;
if (drawPart) {
if (!this._pathSegLen) {
this._calculateLength();
}
pathSegLen = this._pathSegLen;
pathTotalLen = this._pathLen;
displayedLength = percent * pathTotalLen;
if (!displayedLength) {
return;
}
}
lo: for (var i = 0; i < len;) {
var cmd = d[i++];
var isFirst = i === 1;
if (isFirst) {
xi = d[i];
yi = d[i + 1];
x0 = xi;
y0 = yi;
}
if (cmd !== CMD.L && pendingPtDist > 0) {
ctx.lineTo(pendingPtX, pendingPtY);
pendingPtDist = 0;
}
switch (cmd) {
case CMD.M:
x0 = xi = d[i++];
y0 = yi = d[i++];
ctx.moveTo(xi, yi);
break;
case CMD.L: {
x = d[i++];
y = d[i++];
var dx = mathAbs(x - xi);
var dy = mathAbs(y - yi);
if (dx > ux || dy > uy) {
if (drawPart) {
var l = pathSegLen[segCount++];
if (accumLength + l > displayedLength) {
var t = (displayedLength - accumLength) / l;
ctx.lineTo(xi * (1 - t) + x * t, yi * (1 - t) + y * t);
break lo;
}
accumLength += l;
}
ctx.lineTo(x, y);
xi = x;
yi = y;
pendingPtDist = 0;
}
else {
var d2 = dx * dx + dy * dy;
if (d2 > pendingPtDist) {
pendingPtX = x;
pendingPtY = y;
pendingPtDist = d2;
}
}
break;
}
case CMD.C: {
var x1 = d[i++];
var y1 = d[i++];
var x2 = d[i++];
var y2 = d[i++];
var x3 = d[i++];
var y3 = d[i++];
if (drawPart) {
var l = pathSegLen[segCount++];
if (accumLength + l > displayedLength) {
var t = (displayedLength - accumLength) / l;
cubicSubdivide(xi, x1, x2, x3, t, tmpOutX);
cubicSubdivide(yi, y1, y2, y3, t, tmpOutY);
ctx.bezierCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2], tmpOutX[3], tmpOutY[3]);
break lo;
}
accumLength += l;
}
ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
xi = x3;
yi = y3;
break;
}
case CMD.Q: {
var x1 = d[i++];
var y1 = d[i++];
var x2 = d[i++];
var y2 = d[i++];
if (drawPart) {
var l = pathSegLen[segCount++];
if (accumLength + l > displayedLength) {
var t = (displayedLength - accumLength) / l;
quadraticSubdivide(xi, x1, x2, t, tmpOutX);
quadraticSubdivide(yi, y1, y2, t, tmpOutY);
ctx.quadraticCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2]);
break lo;
}
accumLength += l;
}
ctx.quadraticCurveTo(x1, y1, x2, y2);
xi = x2;
yi = y2;
break;
}
case CMD.A:
var cx = d[i++];
var cy = d[i++];
var rx = d[i++];
var ry = d[i++];
var startAngle = d[i++];
var delta = d[i++];
var psi = d[i++];
var anticlockwise = !d[i++];
var r = (rx > ry) ? rx : ry;
var isEllipse = mathAbs(rx - ry) > 1e-3;
var endAngle = startAngle + delta;
var breakBuild = false;
if (drawPart) {
var l = pathSegLen[segCount++];
if (accumLength + l > displayedLength) {
endAngle = startAngle + delta * (displayedLength - accumLength) / l;
breakBuild = true;
}
accumLength += l;
}
if (isEllipse && ctx.ellipse) {
ctx.ellipse(cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise);
}
else {
ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
}
if (breakBuild) {
break lo;
}
if (isFirst) {
x0 = mathCos(startAngle) * rx + cx;
y0 = mathSin(startAngle) * ry + cy;
}
xi = mathCos(endAngle) * rx + cx;
yi = mathSin(endAngle) * ry + cy;
break;
case CMD.R:
x0 = xi = d[i];
y0 = yi = d[i + 1];
x = d[i++];
y = d[i++];
var width = d[i++];
var height = d[i++];
if (drawPart) {
var l = pathSegLen[segCount++];
if (accumLength + l > displayedLength) {
var d_1 = displayedLength - accumLength;
ctx.moveTo(x, y);
ctx.lineTo(x + mathMin(d_1, width), y);
d_1 -= width;
if (d_1 > 0) {
ctx.lineTo(x + width, y + mathMin(d_1, height));
}
d_1 -= height;
if (d_1 > 0) {
ctx.lineTo(x + mathMax(width - d_1, 0), y + height);
}
d_1 -= width;
if (d_1 > 0) {
ctx.lineTo(x, y + mathMax(height - d_1, 0));
}
break lo;
}
accumLength += l;
}
ctx.rect(x, y, width, height);
break;
case CMD.Z:
if (drawPart) {
var l = pathSegLen[segCount++];
if (accumLength + l > displayedLength) {
var t = (displayedLength - accumLength) / l;
ctx.lineTo(xi * (1 - t) + x0 * t, yi * (1 - t) + y0 * t);
break lo;
}
accumLength += l;
}
ctx.closePath();
xi = x0;
yi = y0;
}
}
};
PathProxy.prototype.clone = function () {
var newProxy = new PathProxy();
var data = this.data;
newProxy.data = data.slice ? data.slice()
: Array.prototype.slice.call(data);
newProxy._len = this._len;
return newProxy;
};
PathProxy.CMD = CMD;
PathProxy.initDefaultProps = (function () {
var proto = PathProxy.prototype;
proto._saveData = true;
proto._ux = 0;
proto._uy = 0;
proto._pendingPtDist = 0;
proto._version = 0;
})();
return PathProxy;
}());
export default PathProxy;