@visactor/vrender-core
Version:
## Description
371 lines (339 loc) • 17.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: !0
}), exports.CustomPath2D = void 0;
const bounds_context_1 = require("./bounds-context"), path_1 = require("./segment/curve/path"), path_svg_1 = require("./path-svg"), vutils_1 = require("@visactor/vutils"), enums_1 = require("./enums"), arc_1 = require("./shape/arc"), render_command_list_1 = require("./render-command-list"), segment_1 = require("./segment");
class CustomPath2D extends path_1.CurvePath {
constructor(ctx) {
super(), this.commandList = [], ctx && (this._ctx = ctx), this._boundsContext = new bounds_context_1.BoundsContext(this.bounds);
}
get curves() {
return this.tryBuildCurves();
}
setCtx(ctx) {
this._ctx = ctx;
}
moveTo(x, y) {
return this.commandList.push([ path_svg_1.enumCommandMap.M, x, y ]), this._ctx && this._ctx.moveTo(x, y),
this;
}
lineTo(x, y) {
return this.commandList.push([ path_svg_1.enumCommandMap.L, x, y ]), this._ctx && this._ctx.lineTo(x, y),
this;
}
quadraticCurveTo(aCPx, aCPy, aX, aY) {
return this.commandList.push([ path_svg_1.enumCommandMap.Q, aCPx, aCPy, aX, aY ]),
this._ctx && this._ctx.quadraticCurveTo(aCPx, aCPy, aX, aY), this;
}
bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY) {
return this.commandList.push([ path_svg_1.enumCommandMap.C, aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ]),
this._ctx && this._ctx.bezierCurveTo(aCP1x, aCP1y, aCP2x, aCP2y, aX, aY), this;
}
arcTo(aX1, aY1, aX2, aY2, aRadius) {
return this.commandList.push([ path_svg_1.enumCommandMap.AT, aX1, aY1, aX2, aY2, aRadius ]),
this._ctx && this._ctx.arcTo(aX1, aY1, aX2, aY2, aRadius), this;
}
ellipse(aX, aY, xRadius, yRadius, aRotation, aStartAngle, aEndAngle, aClockwise) {
return this.commandList.push([ path_svg_1.enumCommandMap.E, aX, aY, xRadius, yRadius, aRotation, aStartAngle, aEndAngle, aClockwise ]),
this._ctx && this._ctx.ellipse(aX, aY, xRadius, yRadius, aRotation, aStartAngle, aEndAngle, aClockwise),
this;
}
rect(x, y, w, h) {
return this.commandList.push([ path_svg_1.enumCommandMap.R, x, y, w, h ]), this._ctx && this._ctx.rect(x, y, w, h),
this;
}
arc(x, y, radius, startAngle, endAngle, counterclockwise) {
return this.commandList.push([ path_svg_1.enumCommandMap.A, x, y, radius, startAngle, endAngle, counterclockwise ]),
this._ctx && this._ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise),
this;
}
closePath() {
return this.commandList.push([ path_svg_1.enumCommandMap.Z ]), this._ctx && this._ctx.closePath(),
this;
}
addCurve(curve) {
this._curves.push(curve);
}
clear() {
this.transformCbList = null, this.commandList.length = 0, this._curves.length = 0;
}
beginPath() {
this.clear();
}
tryBuildCurves() {
if (!this._curves || !this._curves.length) {
const curveContext = new segment_1.CurveContext(this);
(0, render_command_list_1.renderCommandList)(this.commandList, curveContext, 0, 0, 1, 1);
}
return this._curves;
}
toString() {
if (!this.toStringCbList) {
const list = [];
list[path_svg_1.enumCommandMap.M] = cmd => `M${cmd[1]} ${cmd[2]}`, list[path_svg_1.enumCommandMap.L] = cmd => `L${cmd[1]} ${cmd[2]}`,
list[path_svg_1.enumCommandMap.Q] = cmd => `Q${cmd[1]} ${cmd[2]} ${cmd[3]} ${cmd[4]}`,
list[path_svg_1.enumCommandMap.C] = cmd => `C${cmd[1]} ${cmd[2]} ${cmd[3]} ${cmd[4]} ${cmd[5]} ${cmd[6]}`,
list[path_svg_1.enumCommandMap.A] = cmd => {
const bezierPathList = [];
(0, arc_1.addArcToBezierPath)(bezierPathList, cmd[4], cmd[5], cmd[1], cmd[2], cmd[3], cmd[3]);
let path = "";
for (let i = 0; i < bezierPathList.length; i += 6) path += `C${bezierPathList[i]} ${bezierPathList[i + 1]} ${bezierPathList[i + 2]} ${bezierPathList[i + 3]} ${bezierPathList[i + 4]} ${bezierPathList[i + 5]}`;
return path;
}, list[path_svg_1.enumCommandMap.R] = cmd => `M${cmd[1]} ${cmd[2]} h${cmd[3]} v${cmd[4]} H${cmd[1]}Z`,
list[path_svg_1.enumCommandMap.Z] = cmd => "Z", this.toStringCbList = list;
}
const list = this.toStringCbList;
let path = "";
return this.commandList.forEach((c => {
path += list[c[0]](c);
})), path;
}
fromString(str, x, y, sX, sY) {
this.clear();
const commandStrList = (0, path_svg_1.parseSvgPath)(str);
return this._runCommandStrList(commandStrList, x, y, sX, sY), this._updateBounds(),
this;
}
fromLine(line) {
const {points: points, curveType: curveType, clipRangeByDimension: clipRangeByDimension} = line.attribute;
if (!points) return;
const cache = (0, segment_1.calcLineCache)(points, curveType);
"x" === clipRangeByDimension ? this.direction = enums_1.Direction.ROW : "y" === clipRangeByDimension ? this.direction = enums_1.Direction.COLUMN : "auto" === clipRangeByDimension && (this.direction = cache.direction),
this._curves = cache.curves;
}
fromCustomPath2D(path, x, y, sX, sY) {
return this.clear(), this._runCommandList(path.commandList, x, y, sX, sY), this._updateBounds(),
this;
}
transform(x, y, sx, sy) {
const commandList = this.commandList;
if (!this.transformCbList) {
const list = [];
list[path_svg_1.enumCommandMap.M] = this.moveToTransform, list[path_svg_1.enumCommandMap.L] = this.lineToTransform,
list[path_svg_1.enumCommandMap.Q] = this.quadraticCurveToTransform, list[path_svg_1.enumCommandMap.C] = this.bezierCurveToTransform,
list[path_svg_1.enumCommandMap.AT] = this.arcToTransform, list[path_svg_1.enumCommandMap.E] = this.ellipseTransform,
list[path_svg_1.enumCommandMap.R] = this.rectTransform, list[path_svg_1.enumCommandMap.A] = this.arcTransform,
list[path_svg_1.enumCommandMap.Z] = this.closePathTransform, this.transformCbList = list;
}
commandList.forEach((cmd => {
this.transformCbList[cmd[0]](cmd, x, y, sx, sy);
})), this._updateBounds();
}
moveToTransform(cmd, x, y, sx, sy) {
cmd[1] = cmd[1] * sx + x, cmd[2] = cmd[2] * sy + y;
}
lineToTransform(cmd, x, y, sx, sy) {
cmd[1] = cmd[1] * sx + x, cmd[2] = cmd[2] * sy + y;
}
quadraticCurveToTransform(cmd, x, y, sx, sy) {
cmd[1] = cmd[1] * sx + x, cmd[2] = cmd[2] * sy + y, cmd[3] = cmd[3] * sx + x, cmd[4] = cmd[4] * sy + y;
}
bezierCurveToTransform(cmd, x, y, sx, sy) {
cmd[1] = cmd[1] * sx + x, cmd[2] = cmd[2] * sy + y, cmd[3] = cmd[3] * sx + x, cmd[4] = cmd[4] * sy + y,
cmd[5] = cmd[5] * sx + x, cmd[6] = cmd[6] * sy + y;
}
arcToTransform(cmd, x, y, sx, sy) {
cmd[1] = cmd[1] * sx + x, cmd[2] = cmd[2] * sy + y, cmd[3] = cmd[3] * sx + x, cmd[4] = cmd[4] * sy + y,
cmd[5] = cmd[5] * (sx + sy) / 2;
}
ellipseTransform(cmd, x, y, sx, sy) {
cmd[1] = cmd[1] * sx + x, cmd[2] = cmd[2] * sy + y, cmd[3] = cmd[3] * sx, cmd[4] = cmd[4] * sy;
}
rectTransform(cmd, x, y, sx, sy) {
cmd[1] = cmd[1] * sx + x, cmd[2] = cmd[2] * sy + y, cmd[3] = cmd[3] * sx, cmd[4] = cmd[4] * sy;
}
arcTransform(cmd, x, y, sx, sy) {
cmd[1] = cmd[1] * sx + x, cmd[2] = cmd[2] * sy + y, cmd[3] = cmd[3] * (sx + sy) / 2;
}
closePathTransform() {}
_runCommandStrList(commandStrList, l = 0, t = 0, sX = 1, sY = 1) {
let current, tempX, tempY, tempControlX, tempControlY, previous = null, x = 0, y = 0, controlX = 0, controlY = 0;
for (let i = 0, len = commandStrList.length; i < len; ++i) {
switch (current = commandStrList[i], 1 === sX && 1 === sY || (current = scale(current, sX, sY)),
current[0]) {
case "l":
x += current[1], y += current[2], this.lineTo(x + l, y + t);
break;
case "L":
x = current[1], y = current[2], this.lineTo(x + l, y + t);
break;
case "h":
x += current[1], this.lineTo(x + l, y + t);
break;
case "H":
x = current[1], this.lineTo(x + l, y + t);
break;
case "v":
y += current[1], this.lineTo(x + l, y + t);
break;
case "V":
y = current[1], this.lineTo(x + l, y + t);
break;
case "m":
x += current[1], y += current[2], this.moveTo(x + l, y + t);
break;
case "M":
x = current[1], y = current[2], this.moveTo(x + l, y + t);
break;
case "c":
tempX = x + current[5], tempY = y + current[6], controlX = x + current[3], controlY = y + current[4],
this.bezierCurveTo(x + current[1] + l, y + current[2] + t, controlX + l, controlY + t, tempX + l, tempY + t),
x = tempX, y = tempY;
break;
case "C":
x = current[5], y = current[6], controlX = current[3], controlY = current[4], this.bezierCurveTo(current[1] + l, current[2] + t, controlX + l, controlY + t, x + l, y + t);
break;
case "s":
tempX = x + current[3], tempY = y + current[4], null === previous[0].match(/[CcSs]/) ? (controlX = x,
controlY = y) : (controlX = 2 * x - controlX, controlY = 2 * y - controlY), tempControlX = x + current[1],
tempControlY = y + current[2], this.bezierCurveTo(controlX + l, controlY + t, tempControlX + l, tempControlY + t, tempX + l, tempY + t),
controlX = tempControlX, controlY = tempControlY, x = tempX, y = tempY;
break;
case "S":
tempX = current[3], tempY = current[4], null === previous[0].match(/[CcSs]/) ? (controlX = x,
controlY = y) : (controlX = 2 * x - controlX, controlY = 2 * y - controlY), tempControlX = current[1],
tempControlY = current[2], this.bezierCurveTo(controlX + l, controlY + t, tempControlX + l, tempControlY + t, tempX + l, tempY + t),
controlX = tempControlX, controlY = tempControlY, x = tempX, y = tempY;
break;
case "q":
tempX = x + current[3], tempY = y + current[4], controlX = x + current[1], controlY = y + current[2],
this.quadraticCurveTo(controlX + l, controlY + t, tempX + l, tempY + t), x = tempX,
y = tempY;
break;
case "Q":
tempX = current[3], tempY = current[4], this.quadraticCurveTo(current[1] + l, current[2] + t, tempX + l, tempY + t),
x = tempX, y = tempY, controlX = current[1], controlY = current[2];
break;
case "t":
tempX = x + current[1], tempY = y + current[2], null === previous[0].match(/[QqTt]/) ? (controlX = x,
controlY = y) : "t" === previous[0] ? (controlX = 2 * x - tempControlX, controlY = 2 * y - tempControlY) : "q" === previous[0] && (controlX = 2 * x - controlX,
controlY = 2 * y - controlY), tempControlX = controlX, tempControlY = controlY,
this.quadraticCurveTo(controlX + l, controlY + t, tempX + l, tempY + t), x = tempX,
y = tempY, controlX = x + current[1], controlY = y + current[2];
break;
case "T":
tempX = current[1], tempY = current[2], controlX = 2 * x - controlX, controlY = 2 * y - controlY,
this.quadraticCurveTo(controlX + l, controlY + t, tempX + l, tempY + t), x = tempX,
y = tempY;
break;
case "a":
(0, arc_1.drawArc)(this, x + l, y + t, [ current[1], current[2], current[3], current[4], current[5], current[6] + x + l, current[7] + y + t ]),
x += current[6], y += current[7];
break;
case "A":
(0, arc_1.drawArc)(this, x + l, y + t, [ current[1], current[2], current[3], current[4], current[5], current[6] + l, current[7] + t ]),
x = current[6], y = current[7];
break;
case "z":
case "Z":
this.closePath();
}
previous = current;
}
}
_runCommandList(commandList, l = 0, t = 0, sX = 1, sY = 1) {
if (0 !== l || 0 !== t || 1 !== sX || 1 !== sY) for (let i = 0, len = commandList.length; i < len; ++i) {
const current = commandList[i].slice();
switch (current[0]) {
case path_svg_1.enumCommandMap.L:
this.lineToTransform(current, l, t, sX, sY);
break;
case path_svg_1.enumCommandMap.M:
this.moveToTransform(current, l, t, sX, sY);
break;
case path_svg_1.enumCommandMap.C:
this.bezierCurveToTransform(current, l, t, sX, sY);
break;
case path_svg_1.enumCommandMap.Q:
this.quadraticCurveToTransform(current, l, t, sX, sY);
break;
case path_svg_1.enumCommandMap.A:
this.arcToTransform(current, l, t, sX, sY);
break;
case path_svg_1.enumCommandMap.E:
this.ellipseTransform(current, l, t, sX, sY);
break;
case path_svg_1.enumCommandMap.R:
this.rectTransform(current, l, t, sX, sY);
break;
case path_svg_1.enumCommandMap.AT:
this.arcToTransform(current, l, t, sX, sY);
break;
case path_svg_1.enumCommandMap.Z:
this.closePath();
}
} else this.commandList = commandList.map((entry => entry.slice()));
}
_updateBounds() {
this.bounds.clear(), (0, render_command_list_1.renderCommandList)(this.commandList, this._boundsContext);
}
release() {
this.commandList = [], this._boundsContext = null, this._ctx = null;
}
getLength() {
if (this.direction === enums_1.Direction.COLUMN) {
if (!this._curves.length) return 0;
const sc = this._curves[0], ec = this._curves[this._curves.length - 1];
return (0, vutils_1.abs)(sc.p0.y - ec.p1.y);
}
if (this.direction === enums_1.Direction.ROW) {
if (!this._curves.length) return 0;
const sc = this._curves[0], ec = this._curves[this._curves.length - 1];
return (0, vutils_1.abs)(sc.p0.x - ec.p1.x);
}
return this._curves.reduce(((l, c) => l + c.getLength()), 0);
}
getYAt(x) {
if (!this.curves) return 1 / 0;
for (let i = 0; i < this.curves.length; i++) {
const curve = this.curves[i];
if (curve.includeX(x)) return curve.getYAt(x);
}
return 1 / 0;
}
getAttrAt(distance) {
if (!this._curves) return {
pos: {
x: 0,
y: 0
},
angle: 0
};
let curve, _dis = 0;
for (let i = 0; i < this._curves.length; i++) {
curve = this._curves[i];
const cl = curve.getLength(this.direction);
if (_dis + cl >= distance) break;
_dis += cl;
}
const t = (distance - _dis) / curve.getLength(this.direction);
return {
pos: curve.getPointAt(t),
angle: curve.getAngleAt(t)
};
}
drawWithClipRange(ctx, size, x, y, clipRange) {
this.tryBuildCurves();
const totalLen = this.getLength() * clipRange;
let currLen = 0;
for (let i = 0; i < this._curves.length; i++) {
const curve = this._curves[i], cl = curve.getLength(this.direction);
if (!(currLen + cl <= totalLen)) {
const percent = 1 - (currLen + cl - totalLen) / cl;
curve.draw(ctx, x, y, size, size, percent);
break;
}
curve.draw(ctx, x, y, size, size, 1), currLen += cl;
}
}
}
exports.CustomPath2D = CustomPath2D;
const temp = [ "l", 0, 0, 0, 0, 0, 0, 0 ];
function scale(current, sX, sY) {
const c = temp[0] = current[0];
if ("a" === c || "A" === c) temp[1] = sX * current[1], temp[2] = sY * current[2],
temp[3] = current[3], temp[4] = current[4], temp[5] = current[5], temp[6] = sX * current[6],
temp[7] = sY * current[7]; else if ("h" === c || "H" === c) temp[1] = sX * current[1]; else if ("v" === c || "V" === c) temp[1] = sY * current[1]; else for (let i = 1, n = current.length; i < n; ++i) temp[i] = (i % 2 == 1 ? sX : sY) * current[i];
return temp;
}
//# sourceMappingURL=custom-path2d.js.map