@antv/x6
Version:
JavaScript diagramming library that uses SVG and HTML for rendering
948 lines • 35.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Path = void 0;
const tslib_1 = require("tslib");
const util_1 = require("../util");
const PathUtil = tslib_1.__importStar(require("./util"));
const line_1 = require("../line");
const point_1 = require("../point");
const curve_1 = require("../curve");
const polyline_1 = require("../polyline");
const rectangle_1 = require("../rectangle");
const geometry_1 = require("../geometry");
const close_1 = require("./close");
const lineto_1 = require("./lineto");
const moveto_1 = require("./moveto");
const curveto_1 = require("./curveto");
const normalize_1 = require("./normalize");
class Path extends geometry_1.Geometry {
static isPath(instance) {
return instance != null && instance instanceof Path;
}
static parse(pathData) {
if (!pathData) {
return new Path();
}
const path = new Path();
const commandRe = /(?:[a-zA-Z] *)(?:(?:-?\d+(?:\.\d+)?(?:e[-+]?\d+)? *,? *)|(?:-?\.\d+ *,? *))+|(?:[a-zA-Z] *)(?! |\d|-|\.)/g;
const commands = (0, normalize_1.normalizePathData)(pathData).match(commandRe);
if (commands != null) {
for (let i = 0, ii = commands.length; i < ii; i += 1) {
const command = commands[i];
const argRe = /(?:[a-zA-Z])|(?:(?:-?\d+(?:\.\d+)?(?:e[-+]?\d+)?))|(?:(?:-?\.\d+))/g;
const args = command.match(argRe); // [type, coordinate1, coordinate2...]
if (args != null) {
const type = args[0];
const coords = args.slice(1).map((a) => +a);
const segment = Path.createSegment.call(null, type, ...coords);
path.appendSegment(segment);
}
}
}
return path;
}
static createSegment(type, ...args) {
if (type === 'M') {
return moveto_1.MoveTo.create.call(null, ...args);
}
if (type === 'L') {
return lineto_1.LineTo.create.call(null, ...args);
}
if (type === 'C') {
return curveto_1.CurveTo.create.call(null, ...args);
}
if (type === 'z' || type === 'Z') {
return close_1.Close.create();
}
throw new Error(`Invalid path segment type "${type}"`);
}
constructor(args) {
super();
this.PRECISION = 3;
this.segments = [];
if (Array.isArray(args)) {
if (line_1.Line.isLine(args[0]) || curve_1.Curve.isCurve(args[0])) {
let previousObj = null;
const arr = args;
arr.forEach((o, i) => {
if (i === 0) {
this.appendSegment(Path.createSegment('M', o.start));
}
if (previousObj != null && !previousObj.end.equals(o.start)) {
this.appendSegment(Path.createSegment('M', o.start));
}
if (line_1.Line.isLine(o)) {
this.appendSegment(Path.createSegment('L', o.end));
}
else if (curve_1.Curve.isCurve(o)) {
this.appendSegment(Path.createSegment('C', o.controlPoint1, o.controlPoint2, o.end));
}
previousObj = o;
});
}
else {
const arr = args;
arr.forEach((s) => {
if (s.isSegment) {
this.appendSegment(s);
}
});
}
}
else if (args != null) {
if (line_1.Line.isLine(args)) {
this.appendSegment(Path.createSegment('M', args.start));
this.appendSegment(Path.createSegment('L', args.end));
}
else if (curve_1.Curve.isCurve(args)) {
this.appendSegment(Path.createSegment('M', args.start));
this.appendSegment(Path.createSegment('C', args.controlPoint1, args.controlPoint2, args.end));
}
else if (polyline_1.Polyline.isPolyline(args)) {
if (args.points && args.points.length) {
args.points.forEach((point, index) => {
const segment = index === 0
? Path.createSegment('M', point)
: Path.createSegment('L', point);
this.appendSegment(segment);
});
}
}
else if (args.isSegment) {
this.appendSegment(args);
}
}
}
get start() {
const segments = this.segments;
const count = segments.length;
if (count === 0) {
return null;
}
for (let i = 0; i < count; i += 1) {
const segment = segments[i];
if (segment.isVisible) {
return segment.start;
}
}
// if no visible segment, return last segment end point
return segments[count - 1].end;
}
get end() {
const segments = this.segments;
const count = segments.length;
if (count === 0) {
return null;
}
for (let i = count - 1; i >= 0; i -= 1) {
const segment = segments[i];
if (segment.isVisible) {
return segment.end;
}
}
// if no visible segment, return last segment end point
return segments[count - 1].end;
}
moveTo(...args) {
return this.appendSegment(moveto_1.MoveTo.create.call(null, ...args));
}
lineTo(...args) {
return this.appendSegment(lineto_1.LineTo.create.call(null, ...args));
}
curveTo(...args) {
return this.appendSegment(curveto_1.CurveTo.create.call(null, ...args));
}
arcTo(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, endX, endY) {
const start = this.end || new point_1.Point();
const points = typeof endX === 'number'
? PathUtil.arcToCurves(start.x, start.y, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, endX, endY)
: PathUtil.arcToCurves(start.x, start.y, rx, ry, xAxisRotation, largeArcFlag, sweepFlag, endX.x, endX.y);
if (points != null) {
for (let i = 0, ii = points.length; i < ii; i += 6) {
this.curveTo(points[i], points[i + 1], points[i + 2], points[i + 3], points[i + 4], points[i + 5]);
}
}
return this;
}
quadTo(x1, y1, x, y) {
const start = this.end || new point_1.Point();
const data = ['M', start.x, start.y];
if (typeof x1 === 'number') {
data.push('Q', x1, y1, x, y);
}
else {
const p = y1;
data.push(`Q`, x1.x, x1.y, p.x, p.y);
}
const path = Path.parse(data.join(' '));
this.appendSegment(path.segments.slice(1));
return this;
}
close() {
return this.appendSegment(close_1.Close.create());
}
drawPoints(points, options = {}) {
const raw = PathUtil.drawPoints(points, options);
const sub = Path.parse(raw);
if (sub && sub.segments) {
this.appendSegment(sub.segments);
}
}
bbox() {
const segments = this.segments;
const count = segments.length;
if (count === 0) {
return null;
}
let bbox;
for (let i = 0; i < count; i += 1) {
const segment = segments[i];
if (segment.isVisible) {
const segmentBBox = segment.bbox();
if (segmentBBox != null) {
bbox = bbox ? bbox.union(segmentBBox) : segmentBBox;
}
}
}
if (bbox != null) {
return bbox;
}
// if the path has only invisible elements, return end point of last segment
const lastSegment = segments[count - 1];
return new rectangle_1.Rectangle(lastSegment.end.x, lastSegment.end.y, 0, 0);
}
appendSegment(seg) {
const count = this.segments.length;
let previousSegment = count !== 0 ? this.segments[count - 1] : null;
let currentSegment;
const nextSegment = null;
if (Array.isArray(seg)) {
for (let i = 0, ii = seg.length; i < ii; i += 1) {
const segment = seg[i];
currentSegment = this.prepareSegment(segment, previousSegment, nextSegment);
this.segments.push(currentSegment);
previousSegment = currentSegment;
}
}
else if (seg != null && seg.isSegment) {
currentSegment = this.prepareSegment(seg, previousSegment, nextSegment);
this.segments.push(currentSegment);
}
return this;
}
insertSegment(index, seg) {
const count = this.segments.length;
if (index < 0) {
index = count + index + 1; // eslint-disable-line
}
if (index > count || index < 0) {
throw new Error('Index out of range.');
}
let currentSegment;
let previousSegment = null;
let nextSegment = null;
if (count !== 0) {
if (index >= 1) {
previousSegment = this.segments[index - 1];
nextSegment = previousSegment.nextSegment;
}
else {
previousSegment = null;
nextSegment = this.segments[0];
}
}
if (!Array.isArray(seg)) {
currentSegment = this.prepareSegment(seg, previousSegment, nextSegment);
this.segments.splice(index, 0, currentSegment);
}
else {
for (let i = 0, ii = seg.length; i < ii; i += 1) {
const segment = seg[i];
currentSegment = this.prepareSegment(segment, previousSegment, nextSegment);
this.segments.splice(index + i, 0, currentSegment);
previousSegment = currentSegment;
}
}
return this;
}
removeSegment(index) {
const idx = this.fixIndex(index);
const removedSegment = this.segments.splice(idx, 1)[0];
const previousSegment = removedSegment.previousSegment;
const nextSegment = removedSegment.nextSegment;
// link the previous and next segments together (if present)
if (previousSegment) {
previousSegment.nextSegment = nextSegment;
}
if (nextSegment) {
nextSegment.previousSegment = previousSegment;
}
if (removedSegment.isSubpathStart && nextSegment) {
this.updateSubpathStartSegment(nextSegment);
}
return removedSegment;
}
replaceSegment(index, seg) {
const idx = this.fixIndex(index);
let currentSegment;
const replacedSegment = this.segments[idx];
let previousSegment = replacedSegment.previousSegment;
const nextSegment = replacedSegment.nextSegment;
let updateSubpathStart = replacedSegment.isSubpathStart;
if (!Array.isArray(seg)) {
currentSegment = this.prepareSegment(seg, previousSegment, nextSegment);
this.segments.splice(idx, 1, currentSegment);
if (updateSubpathStart && currentSegment.isSubpathStart) {
// already updated by `prepareSegment`
updateSubpathStart = false;
}
}
else {
this.segments.splice(index, 1);
for (let i = 0, ii = seg.length; i < ii; i += 1) {
const segment = seg[i];
currentSegment = this.prepareSegment(segment, previousSegment, nextSegment);
this.segments.splice(index + i, 0, currentSegment);
previousSegment = currentSegment;
if (updateSubpathStart && currentSegment.isSubpathStart) {
updateSubpathStart = false;
}
}
}
if (updateSubpathStart && nextSegment) {
this.updateSubpathStartSegment(nextSegment);
}
}
getSegment(index) {
const idx = this.fixIndex(index);
return this.segments[idx];
}
fixIndex(index) {
const length = this.segments.length;
if (length === 0) {
throw new Error('Path has no segments.');
}
let i = index;
while (i < 0) {
i = length + i;
}
if (i >= length || i < 0) {
throw new Error('Index out of range.');
}
return i;
}
segmentAt(ratio, options = {}) {
const index = this.segmentIndexAt(ratio, options);
if (!index) {
return null;
}
return this.getSegment(index);
}
segmentAtLength(length, options = {}) {
const index = this.segmentIndexAtLength(length, options);
if (!index)
return null;
return this.getSegment(index);
}
segmentIndexAt(ratio, options = {}) {
if (this.segments.length === 0) {
return null;
}
const rate = (0, util_1.clamp)(ratio, 0, 1);
const opt = this.getOptions(options);
const len = this.length(opt);
const length = len * rate;
return this.segmentIndexAtLength(length, opt);
}
segmentIndexAtLength(length, options = {}) {
const count = this.segments.length;
if (count === 0) {
return null;
}
let fromStart = true;
if (length < 0) {
fromStart = false;
length = -length; // eslint-disable-line
}
const precision = this.getPrecision(options);
const segmentSubdivisions = this.getSubdivisions(options);
let memo = 0;
let lastVisibleIndex = null;
for (let i = 0; i < count; i += 1) {
const index = fromStart ? i : count - 1 - i;
const segment = this.segments[index];
const subdivisions = segmentSubdivisions[index];
const len = segment.length({ precision, subdivisions });
if (segment.isVisible) {
if (length <= memo + len) {
return index;
}
lastVisibleIndex = index;
}
memo += len;
}
// If length requested is higher than the length of the path, return
// last visible segment index. If no visible segment, return null.
return lastVisibleIndex;
}
getSegmentSubdivisions(options = {}) {
const precision = this.getPrecision(options);
const segmentSubdivisions = [];
for (let i = 0, ii = this.segments.length; i < ii; i += 1) {
const segment = this.segments[i];
const subdivisions = segment.getSubdivisions({ precision });
segmentSubdivisions.push(subdivisions);
}
return segmentSubdivisions;
}
updateSubpathStartSegment(segment) {
let previous = segment.previousSegment;
let current = segment;
while (current && !current.isSubpathStart) {
// assign previous segment's subpath start segment to this segment
if (previous != null) {
current.subpathStartSegment = previous.subpathStartSegment;
}
else {
current.subpathStartSegment = null;
}
previous = current;
current = current.nextSegment;
}
}
prepareSegment(segment, previousSegment, nextSegment) {
segment.previousSegment = previousSegment;
segment.nextSegment = nextSegment;
if (previousSegment != null) {
previousSegment.nextSegment = segment;
}
if (nextSegment != null) {
nextSegment.previousSegment = segment;
}
let updateSubpathStart = segment;
if (segment.isSubpathStart) {
// move to
segment.subpathStartSegment = segment;
updateSubpathStart = nextSegment;
}
// assign previous segment's subpath start (or self if it is a subpath start) to subsequent segments
if (updateSubpathStart != null) {
this.updateSubpathStartSegment(updateSubpathStart);
}
return segment;
}
closestPoint(p, options = {}) {
const t = this.closestPointT(p, options);
if (!t) {
return null;
}
return this.pointAtT(t);
}
closestPointLength(p, options = {}) {
const opts = this.getOptions(options);
const t = this.closestPointT(p, opts);
if (!t) {
return 0;
}
return this.lengthAtT(t, opts);
}
closestPointNormalizedLength(p, options = {}) {
const opts = this.getOptions(options);
const cpLength = this.closestPointLength(p, opts);
if (cpLength === 0) {
return 0;
}
const length = this.length(opts);
if (length === 0) {
return 0;
}
return cpLength / length;
}
closestPointT(p, options = {}) {
if (this.segments.length === 0) {
return null;
}
const precision = this.getPrecision(options);
const segmentSubdivisions = this.getSubdivisions(options);
let closestPointT;
let minSquaredDistance = Infinity;
for (let i = 0, ii = this.segments.length; i < ii; i += 1) {
const segment = this.segments[i];
const subdivisions = segmentSubdivisions[i];
if (segment.isVisible) {
const segmentClosestPointT = segment.closestPointT(p, {
precision,
subdivisions,
});
const segmentClosestPoint = segment.pointAtT(segmentClosestPointT);
const squaredDistance = (0, util_1.squaredLength)(segmentClosestPoint, p);
if (squaredDistance < minSquaredDistance) {
closestPointT = { segmentIndex: i, value: segmentClosestPointT };
minSquaredDistance = squaredDistance;
}
}
}
if (closestPointT) {
return closestPointT;
}
return { segmentIndex: this.segments.length - 1, value: 1 };
}
closestPointTangent(p, options = {}) {
if (this.segments.length === 0) {
return null;
}
const precision = this.getPrecision(options);
const segmentSubdivisions = this.getSubdivisions(options);
let closestPointTangent;
let minSquaredDistance = Infinity;
for (let i = 0, ii = this.segments.length; i < ii; i += 1) {
const segment = this.segments[i];
const subdivisions = segmentSubdivisions[i];
if (segment.isDifferentiable()) {
const segmentClosestPointT = segment.closestPointT(p, {
precision,
subdivisions,
});
const segmentClosestPoint = segment.pointAtT(segmentClosestPointT);
const squaredDistance = (0, util_1.squaredLength)(segmentClosestPoint, p);
if (squaredDistance < minSquaredDistance) {
closestPointTangent = segment.tangentAtT(segmentClosestPointT);
minSquaredDistance = squaredDistance;
}
}
}
if (closestPointTangent) {
return closestPointTangent;
}
return null;
}
containsPoint(p, options = {}) {
const polylines = this.toPolylines(options);
if (!polylines) {
return false;
}
let numIntersections = 0;
for (let i = 0, ii = polylines.length; i < ii; i += 1) {
const polyline = polylines[i];
if (polyline.containsPoint(p)) {
numIntersections += 1;
}
}
// returns `true` for odd numbers of intersections (even-odd algorithm)
return numIntersections % 2 === 1;
}
pointAt(ratio, options = {}) {
if (this.segments.length === 0) {
return null;
}
if (ratio <= 0) {
return this.start.clone();
}
if (ratio >= 1) {
return this.end.clone();
}
const opts = this.getOptions(options);
const pathLength = this.length(opts);
const length = pathLength * ratio;
return this.pointAtLength(length, opts);
}
pointAtLength(length, options = {}) {
if (this.segments.length === 0) {
return null;
}
if (length === 0) {
return this.start.clone();
}
let fromStart = true;
if (length < 0) {
fromStart = false;
length = -length; // eslint-disable-line
}
const precision = this.getPrecision(options);
const segmentSubdivisions = this.getSubdivisions(options);
let lastVisibleSegment;
let memo = 0;
for (let i = 0, ii = this.segments.length; i < ii; i += 1) {
const index = fromStart ? i : ii - 1 - i;
const segment = this.segments[index];
const subdivisions = segmentSubdivisions[index];
const d = segment.length({
precision,
subdivisions,
});
if (segment.isVisible) {
if (length <= memo + d) {
return segment.pointAtLength((fromStart ? 1 : -1) * (length - memo), {
precision,
subdivisions,
});
}
lastVisibleSegment = segment;
}
memo += d;
}
// if length requested is higher than the length of the path,
// return last visible segment endpoint
if (lastVisibleSegment) {
return fromStart ? lastVisibleSegment.end : lastVisibleSegment.start;
}
// if no visible segment, return last segment end point
const lastSegment = this.segments[this.segments.length - 1];
return lastSegment.end.clone();
}
pointAtT(t) {
const segments = this.segments;
const numSegments = segments.length;
if (numSegments === 0)
return null; // if segments is an empty array
const segmentIndex = t.segmentIndex;
if (segmentIndex < 0)
return segments[0].pointAtT(0);
if (segmentIndex >= numSegments) {
return segments[numSegments - 1].pointAtT(1);
}
const tValue = (0, util_1.clamp)(t.value, 0, 1);
return segments[segmentIndex].pointAtT(tValue);
}
divideAt(ratio, options = {}) {
if (this.segments.length === 0) {
return null;
}
const rate = (0, util_1.clamp)(ratio, 0, 1);
const opts = this.getOptions(options);
const len = this.length(opts);
const length = len * rate;
return this.divideAtLength(length, opts);
}
divideAtLength(length, options = {}) {
if (this.segments.length === 0) {
return null;
}
let fromStart = true;
if (length < 0) {
fromStart = false;
length = -length; // eslint-disable-line
}
const precision = this.getPrecision(options);
const segmentSubdivisions = this.getSubdivisions(options);
let memo = 0;
let divided;
let dividedSegmentIndex;
let lastValidSegment;
let lastValidSegmentIndex;
let t;
for (let i = 0, ii = this.segments.length; i < ii; i += 1) {
const index = fromStart ? i : ii - 1 - i;
const segment = this.getSegment(index);
const subdivisions = segmentSubdivisions[index];
const opts = { precision, subdivisions };
const len = segment.length(opts);
if (segment.isDifferentiable()) {
lastValidSegment = segment;
lastValidSegmentIndex = index;
if (length <= memo + len) {
dividedSegmentIndex = index;
divided = segment.divideAtLength((fromStart ? 1 : -1) * (length - memo), opts);
break;
}
}
memo += len;
}
if (!lastValidSegment) {
return null;
}
if (!divided) {
dividedSegmentIndex = lastValidSegmentIndex;
t = fromStart ? 1 : 0;
divided = lastValidSegment.divideAtT(t);
}
// create a copy of this path and replace the identified segment with its two divided parts:
const pathCopy = this.clone();
const index = dividedSegmentIndex;
pathCopy.replaceSegment(index, divided);
const divisionStartIndex = index;
let divisionMidIndex = index + 1;
let divisionEndIndex = index + 2;
// do not insert the part if it looks like a point
if (!divided[0].isDifferentiable()) {
pathCopy.removeSegment(divisionStartIndex);
divisionMidIndex -= 1;
divisionEndIndex -= 1;
}
// insert a Moveto segment to ensure secondPath will be valid:
const movetoEnd = pathCopy.getSegment(divisionMidIndex).start;
pathCopy.insertSegment(divisionMidIndex, Path.createSegment('M', movetoEnd));
divisionEndIndex += 1;
// do not insert the part if it looks like a point
if (!divided[1].isDifferentiable()) {
pathCopy.removeSegment(divisionEndIndex - 1);
divisionEndIndex -= 1;
}
// ensure that Closepath segments in secondPath will be assigned correct subpathStartSegment:
const secondPathSegmentIndexConversion = divisionEndIndex - divisionStartIndex - 1;
for (let i = divisionEndIndex, ii = pathCopy.segments.length; i < ii; i += 1) {
const originalSegment = this.getSegment(i - secondPathSegmentIndexConversion);
const segment = pathCopy.getSegment(i);
if (segment.type === 'Z' &&
!originalSegment.subpathStartSegment.end.equals(segment.subpathStartSegment.end)) {
// pathCopy segment's subpathStartSegment is different from original segment's one
// convert this Closepath segment to a Lineto and replace it in pathCopy
const convertedSegment = Path.createSegment('L', originalSegment.end);
pathCopy.replaceSegment(i, convertedSegment);
}
}
// distribute pathCopy segments into two paths and return those:
const firstPath = new Path(pathCopy.segments.slice(0, divisionMidIndex));
const secondPath = new Path(pathCopy.segments.slice(divisionMidIndex));
return [firstPath, secondPath];
}
intersectsWithLine(line, options = {}) {
const polylines = this.toPolylines(options);
if (polylines == null) {
return null;
}
let intersections = null;
for (let i = 0, ii = polylines.length; i < ii; i += 1) {
const polyline = polylines[i];
const intersection = line.intersect(polyline);
if (intersection) {
if (intersections == null) {
intersections = [];
}
if (Array.isArray(intersection)) {
intersections.push(...intersection);
}
else {
intersections.push(intersection);
}
}
}
return intersections;
}
isDifferentiable() {
for (let i = 0, ii = this.segments.length; i < ii; i += 1) {
const segment = this.segments[i];
if (segment.isDifferentiable()) {
return true;
}
}
return false;
}
isValid() {
const segments = this.segments;
const isValid = segments.length === 0 || segments[0].type === 'M';
return isValid;
}
length(options = {}) {
if (this.segments.length === 0) {
return 0;
}
const segmentSubdivisions = this.getSubdivisions(options);
let length = 0;
for (let i = 0, ii = this.segments.length; i < ii; i += 1) {
const segment = this.segments[i];
const subdivisions = segmentSubdivisions[i];
length += segment.length({ subdivisions });
}
return length;
}
lengthAtT(t, options = {}) {
const count = this.segments.length;
if (count === 0) {
return 0;
}
let segmentIndex = t.segmentIndex;
if (segmentIndex < 0) {
return 0;
}
let tValue = (0, util_1.clamp)(t.value, 0, 1);
if (segmentIndex >= count) {
segmentIndex = count - 1;
tValue = 1;
}
const precision = this.getPrecision(options);
const segmentSubdivisions = this.getSubdivisions(options);
let length = 0;
for (let i = 0; i < segmentIndex; i += 1) {
const segment = this.segments[i];
const subdivisions = segmentSubdivisions[i];
length += segment.length({ precision, subdivisions });
}
const segment = this.segments[segmentIndex];
const subdivisions = segmentSubdivisions[segmentIndex];
length += segment.lengthAtT(tValue, { precision, subdivisions });
return length;
}
tangentAt(ratio, options = {}) {
if (this.segments.length === 0) {
return null;
}
const rate = (0, util_1.clamp)(ratio, 0, 1);
const opts = this.getOptions(options);
const len = this.length(opts);
const length = len * rate;
return this.tangentAtLength(length, opts);
}
tangentAtLength(length, options = {}) {
if (this.segments.length === 0) {
return null;
}
let fromStart = true;
if (length < 0) {
fromStart = false;
length = -length; // eslint-disable-line
}
const precision = this.getPrecision(options);
const segmentSubdivisions = this.getSubdivisions(options);
let lastValidSegment;
let memo = 0;
for (let i = 0, ii = this.segments.length; i < ii; i += 1) {
const index = fromStart ? i : ii - 1 - i;
const segment = this.segments[index];
const subdivisions = segmentSubdivisions[index];
const len = segment.length({ precision, subdivisions });
if (segment.isDifferentiable()) {
if (length <= memo + len) {
return segment.tangentAtLength((fromStart ? 1 : -1) * (length - memo), {
precision,
subdivisions,
});
}
lastValidSegment = segment;
}
memo += len;
}
// if length requested is higher than the length of the path, return tangent of endpoint of last valid segment
if (lastValidSegment) {
const t = fromStart ? 1 : 0;
return lastValidSegment.tangentAtT(t);
}
// if no valid segment, return null
return null;
}
tangentAtT(t) {
const count = this.segments.length;
if (count === 0) {
return null;
}
const segmentIndex = t.segmentIndex;
if (segmentIndex < 0) {
return this.segments[0].tangentAtT(0);
}
if (segmentIndex >= count) {
return this.segments[count - 1].tangentAtT(1);
}
const tValue = (0, util_1.clamp)(t.value, 0, 1);
return this.segments[segmentIndex].tangentAtT(tValue);
}
getPrecision(options = {}) {
return options.precision == null ? this.PRECISION : options.precision;
}
getSubdivisions(options = {}) {
if (options.segmentSubdivisions == null) {
const precision = this.getPrecision(options);
return this.getSegmentSubdivisions({ precision });
}
return options.segmentSubdivisions;
}
getOptions(options = {}) {
const precision = this.getPrecision(options);
const segmentSubdivisions = this.getSubdivisions(options);
return { precision, segmentSubdivisions };
}
toPoints(options = {}) {
const segments = this.segments;
const count = segments.length;
if (count === 0) {
return null;
}
const segmentSubdivisions = this.getSubdivisions(options);
const points = [];
let partialPoints = [];
for (let i = 0; i < count; i += 1) {
const segment = segments[i];
if (segment.isVisible) {
const divisions = segmentSubdivisions[i];
if (divisions.length > 0) {
// eslint-disable-next-line no-loop-func
divisions.forEach((c) => partialPoints.push(c.start));
}
else {
partialPoints.push(segment.start);
}
}
else if (partialPoints.length > 0) {
partialPoints.push(segments[i - 1].end);
points.push(partialPoints);
partialPoints = [];
}
}
if (partialPoints.length > 0) {
partialPoints.push(this.end);
points.push(partialPoints);
}
return points;
}
toPolylines(options = {}) {
const points = this.toPoints(options);
if (!points) {
return null;
}
return points.map((arr) => new polyline_1.Polyline(arr));
}
scale(sx, sy, origin) {
this.segments.forEach((s) => s.scale(sx, sy, origin));
return this;
}
rotate(angle, origin) {
this.segments.forEach((segment) => segment.rotate(angle, origin));
return this;
}
translate(tx, ty) {
if (typeof tx === 'number') {
this.segments.forEach((s) => s.translate(tx, ty));
}
else {
this.segments.forEach((s) => s.translate(tx));
}
return this;
}
clone() {
const path = new Path();
this.segments.forEach((s) => path.appendSegment(s.clone()));
return path;
}
equals(p) {
if (p == null) {
return false;
}
const segments = this.segments;
const otherSegments = p.segments;
const count = segments.length;
if (otherSegments.length !== count) {
return false;
}
for (let i = 0; i < count; i += 1) {
const a = segments[i];
const b = otherSegments[i];
if (a.type !== b.type || !a.equals(b)) {
return false;
}
}
return true;
}
toJSON() {
return this.segments.map((s) => s.toJSON());
}
serialize() {
if (!this.isValid()) {
throw new Error('Invalid path segments.');
}
return this.segments.map((s) => s.serialize()).join(' ');
}
toString() {
return this.serialize();
}
}
exports.Path = Path;
//# sourceMappingURL=path.js.map