@tldraw/editor
Version:
A tiny little drawing app (editor).
80 lines (79 loc) • 2.74 kB
JavaScript
import { Vec } from "../Vec.mjs";
import { intersectLineSegmentCircle } from "../intersect.mjs";
import { getArcMeasure, getPointInArcT, getPointOnCircle } from "../utils.mjs";
import { Geometry2d } from "./Geometry2d.mjs";
import { getVerticesCountForLength } from "./geometry-constants.mjs";
class Arc2d extends Geometry2d {
_center;
radius;
start;
end;
largeArcFlag;
sweepFlag;
measure;
angleStart;
angleEnd;
constructor(config) {
super({ ...config, isFilled: false, isClosed: false });
const { center, sweepFlag, largeArcFlag, start, end } = config;
if (start.equals(end)) throw Error(`Arc must have different start and end points.`);
this.angleStart = Vec.Angle(center, start);
this.angleEnd = Vec.Angle(center, end);
this.radius = Vec.Dist(center, start);
this.measure = getArcMeasure(this.angleStart, this.angleEnd, sweepFlag, largeArcFlag);
this.start = start;
this.end = end;
this.sweepFlag = sweepFlag;
this.largeArcFlag = largeArcFlag;
this._center = center;
}
nearestPoint(point) {
const { _center, measure, radius, angleEnd, angleStart, start: A, end: B } = this;
const t = getPointInArcT(measure, angleStart, angleEnd, _center.angle(point));
if (t <= 0) return A;
if (t >= 1) return B;
const P = _center.clone().add(point.clone().sub(_center).uni().mul(radius));
let nearest;
let dist = Infinity;
let d;
for (const p of [A, B, P]) {
d = Vec.Dist2(point, p);
if (d < dist) {
nearest = p;
dist = d;
}
}
if (!nearest) throw Error("nearest point not found");
return nearest;
}
hitTestLineSegment(A, B) {
const { _center, radius, measure, angleStart, angleEnd } = this;
const intersection = intersectLineSegmentCircle(A, B, _center, radius);
if (intersection === null) return false;
return intersection.some((p) => {
const result = getPointInArcT(measure, angleStart, angleEnd, _center.angle(p));
return result >= 0 && result <= 1;
});
}
getVertices() {
const { _center, measure, length, radius, angleStart } = this;
const vertices = [];
for (let i = 0, n = getVerticesCountForLength(Math.abs(length)); i < n + 1; i++) {
const t = i / n * measure;
const angle = angleStart + t;
vertices.push(getPointOnCircle(_center, radius, angle));
}
return vertices;
}
getSvgPathData(first = true) {
const { start, end, radius, largeArcFlag, sweepFlag } = this;
return `${first ? `M${start.toFixed()}` : ``} A${radius} ${radius} 0 ${largeArcFlag} ${sweepFlag} ${end.toFixed()}`;
}
getLength() {
return this.measure * this.radius;
}
}
export {
Arc2d
};
//# sourceMappingURL=Arc2d.mjs.map