@tldraw/editor
Version:
A tiny little drawing app (editor).
152 lines (151 loc) • 4.29 kB
JavaScript
import { Box } from "../Box.mjs";
import { Vec } from "../Vec.mjs";
import { pointInPolygon } from "../utils.mjs";
class Geometry2d {
isFilled = false;
isClosed = true;
isLabel = false;
debugColor;
ignore;
constructor(opts) {
this.isFilled = opts.isFilled;
this.isClosed = opts.isClosed;
this.isLabel = opts.isLabel ?? false;
this.debugColor = opts.debugColor;
this.ignore = opts.ignore;
}
// hitTestPoint(point: Vec, margin = 0, hitInside = false) {
// // We've removed the broad phase here; that should be done outside of the call
// return this.distanceToPoint(point, hitInside) <= margin
// }
hitTestPoint(point, margin = 0, hitInside = false) {
if (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)) {
return true;
}
return Vec.Dist2(point, this.nearestPoint(point)) <= margin * margin;
}
distanceToPoint(point, hitInside = false) {
return point.dist(this.nearestPoint(point)) * (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices) ? -1 : 1);
}
distanceToLineSegment(A, B) {
if (A.equals(B)) return this.distanceToPoint(A);
const { vertices } = this;
let nearest;
let dist = Infinity;
let d, p, q;
for (let i = 0; i < vertices.length; i++) {
p = vertices[i];
q = Vec.NearestPointOnLineSegment(A, B, p, true);
d = Vec.Dist2(p, q);
if (d < dist) {
dist = d;
nearest = q;
}
}
if (!nearest) throw Error("nearest point not found");
return this.isClosed && this.isFilled && pointInPolygon(nearest, this.vertices) ? -dist : dist;
}
hitTestLineSegment(A, B, distance = 0) {
return this.distanceToLineSegment(A, B) <= distance;
}
nearestPointOnLineSegment(A, B) {
const { vertices } = this;
let nearest;
let dist = Infinity;
let d, p, q;
for (let i = 0; i < vertices.length; i++) {
p = vertices[i];
q = Vec.NearestPointOnLineSegment(A, B, p, true);
d = Vec.Dist2(p, q);
if (d < dist) {
dist = d;
nearest = q;
}
}
if (!nearest) throw Error("nearest point not found");
return nearest;
}
isPointInBounds(point, margin = 0) {
const { bounds } = this;
return !(point.x < bounds.minX - margin || point.y < bounds.minY - margin || point.x > bounds.maxX + margin || point.y > bounds.maxY + margin);
}
_vertices;
// eslint-disable-next-line no-restricted-syntax
get vertices() {
if (!this._vertices) {
this._vertices = this.getVertices();
}
return this._vertices;
}
getBounds() {
return Box.FromPoints(this.vertices);
}
_bounds;
// eslint-disable-next-line no-restricted-syntax
get bounds() {
if (!this._bounds) {
this._bounds = this.getBounds();
}
return this._bounds;
}
// eslint-disable-next-line no-restricted-syntax
get center() {
return this.bounds.center;
}
_area;
// eslint-disable-next-line no-restricted-syntax
get area() {
if (!this._area) {
this._area = this.getArea();
}
return this._area;
}
getArea() {
if (!this.isClosed) {
return 0;
}
const { vertices } = this;
let area = 0;
for (let i = 0, n = vertices.length; i < n; i++) {
const curr = vertices[i];
const next = vertices[(i + 1) % n];
area += curr.x * next.y - next.x * curr.y;
}
return area / 2;
}
toSimpleSvgPath() {
let path = "";
const { vertices } = this;
const n = vertices.length;
if (n === 0) return path;
path += `M${vertices[0].x},${vertices[0].y}`;
for (let i = 1; i < n; i++) {
path += `L${vertices[i].x},${vertices[i].y}`;
}
if (this.isClosed) {
path += "Z";
}
return path;
}
_length;
// eslint-disable-next-line no-restricted-syntax
get length() {
if (this._length) return this._length;
this._length = this.getLength();
return this._length;
}
getLength() {
const { vertices } = this;
let n1, p1 = vertices[0], length = 0;
for (let i = 1; i < vertices.length; i++) {
n1 = vertices[i];
length += Vec.Dist2(p1, n1);
p1 = n1;
}
return Math.sqrt(length);
}
}
export {
Geometry2d
};
//# sourceMappingURL=Geometry2d.mjs.map