UNPKG

@js-draw/math

Version:
121 lines (120 loc) 5.31 kB
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var _Triangle_sides; import Abstract2DShape from './Abstract2DShape.mjs'; import LineSegment2 from './LineSegment2.mjs'; import Rect2 from './Rect2.mjs'; class Triangle extends Abstract2DShape { /** * @see {@link fromVertices} */ constructor(vertex1, vertex2, vertex3) { super(); this.vertex1 = vertex1; this.vertex2 = vertex2; this.vertex3 = vertex3; _Triangle_sides.set(this, undefined); } /** * Creates a triangle from its three corners. Corners may be stored in a different * order than given. */ static fromVertices(vertex1, vertex2, vertex3) { return new Triangle(vertex1, vertex2, vertex3); } get vertices() { return [this.vertex1, this.vertex2, this.vertex3]; } map(mapping) { return new Triangle(mapping(this.vertex1), mapping(this.vertex2), mapping(this.vertex3)); } // Transform, treating this as composed of 2D points. transformed2DBy(affineTransform) { return this.map((vertex) => affineTransform.transformVec2(vertex)); } // Transforms this by a linear transform --- verticies are treated as // 3D points. transformedBy(linearTransform) { return this.map((vertex) => linearTransform.transformVec3(vertex)); } /** * Returns the sides of this triangle, as an array of `LineSegment2`s. * * The first side is from `vertex1` to `vertex2`, the next from `vertex2` to `vertex3`, * and the last from `vertex3` to `vertex1`. */ getEdges() { if (__classPrivateFieldGet(this, _Triangle_sides, "f")) { return __classPrivateFieldGet(this, _Triangle_sides, "f"); } const side1 = new LineSegment2(this.vertex1, this.vertex2); const side2 = new LineSegment2(this.vertex2, this.vertex3); const side3 = new LineSegment2(this.vertex3, this.vertex1); const sides = [side1, side2, side3]; __classPrivateFieldSet(this, _Triangle_sides, sides, "f"); return sides; } intersectsLineSegment(lineSegment) { const result = []; for (const edge of this.getEdges()) { edge.intersectsLineSegment(lineSegment).forEach((point) => result.push(point)); } return result; } /** @inheritdoc */ containsPoint(point, epsilon = Abstract2DShape.smallValue) { // Project `point` onto normals to each of this' sides. // Uses the Separating Axis Theorem (https://en.wikipedia.org/wiki/Hyperplane_separation_theorem#Use_in_collision_detection) const sides = this.getEdges(); for (const side of sides) { const orthog = side.direction.orthog(); // Project all three vertices // TODO: Performance can be improved here (two vertices will always have the same projection) const projv1 = orthog.dot(this.vertex1); const projv2 = orthog.dot(this.vertex2); const projv3 = orthog.dot(this.vertex3); const minProjVertex = Math.min(projv1, projv2, projv3); const maxProjVertex = Math.max(projv1, projv2, projv3); const projPoint = orthog.dot(point); const inProjection = projPoint >= minProjVertex - epsilon && projPoint <= maxProjVertex + epsilon; if (!inProjection) { return false; } } return true; } /** * @returns the signed distance from `point` to the closest edge of this triangle. * * If `point` is inside `this`, the result is negative, otherwise, the result is * positive. */ signedDistance(point) { const sides = this.getEdges(); const distances = sides.map((side) => side.distance(point)); const distance = Math.min(...distances); // If the point is in this' interior, signedDistance must return a negative // number. if (this.containsPoint(point, 0)) { return -distance; } else { return distance; } } /** @inheritdoc */ getTightBoundingBox() { return Rect2.bboxOf(this.vertices); } } _Triangle_sides = new WeakMap(); export default Triangle;