@js-draw/math 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. package/README.md +3 -0
  2. package/build-config.json +4 -0
  3. package/dist/cjs/Color4.d.ts +83 -0
  4. package/dist/cjs/Color4.js +277 -0
  5. package/dist/cjs/Mat33.d.ts +131 -0
  6. package/dist/cjs/Mat33.js +345 -0
  7. package/dist/cjs/Vec2.d.ts +42 -0
  8. package/dist/cjs/Vec2.js +48 -0
  9. package/dist/cjs/Vec3.d.ts +126 -0
  10. package/dist/cjs/Vec3.js +203 -0
  11. package/dist/cjs/lib.d.ts +27 -0
  12. package/dist/cjs/lib.js +42 -0
  13. package/dist/cjs/polynomial/solveQuadratic.d.ts +9 -0
  14. package/dist/cjs/polynomial/solveQuadratic.js +39 -0
  15. package/dist/cjs/rounding.d.ts +15 -0
  16. package/dist/cjs/rounding.js +146 -0
  17. package/dist/cjs/shapes/Abstract2DShape.d.ts +49 -0
  18. package/dist/cjs/shapes/Abstract2DShape.js +38 -0
  19. package/dist/cjs/shapes/BezierJSWrapper.d.ts +36 -0
  20. package/dist/cjs/shapes/BezierJSWrapper.js +94 -0
  21. package/dist/cjs/shapes/CubicBezier.d.ts +17 -0
  22. package/dist/cjs/shapes/CubicBezier.js +35 -0
  23. package/dist/cjs/shapes/LineSegment2.d.ts +70 -0
  24. package/dist/cjs/shapes/LineSegment2.js +183 -0
  25. package/dist/cjs/shapes/Path.d.ts +96 -0
  26. package/dist/cjs/shapes/Path.js +766 -0
  27. package/dist/cjs/shapes/PointShape2D.d.ts +18 -0
  28. package/dist/cjs/shapes/PointShape2D.js +31 -0
  29. package/dist/cjs/shapes/QuadraticBezier.d.ts +35 -0
  30. package/dist/cjs/shapes/QuadraticBezier.js +120 -0
  31. package/dist/cjs/shapes/Rect2.d.ts +58 -0
  32. package/dist/cjs/shapes/Rect2.js +259 -0
  33. package/dist/cjs/shapes/Triangle.d.ts +46 -0
  34. package/dist/cjs/shapes/Triangle.js +126 -0
  35. package/dist/mjs/Color4.d.ts +83 -0
  36. package/dist/mjs/Color4.mjs +271 -0
  37. package/dist/mjs/Mat33.d.ts +131 -0
  38. package/dist/mjs/Mat33.mjs +338 -0
  39. package/dist/mjs/Vec2.d.ts +42 -0
  40. package/dist/mjs/Vec2.mjs +42 -0
  41. package/dist/mjs/Vec3.d.ts +126 -0
  42. package/dist/mjs/Vec3.mjs +199 -0
  43. package/dist/mjs/lib.d.ts +27 -0
  44. package/dist/mjs/lib.mjs +29 -0
  45. package/dist/mjs/polynomial/solveQuadratic.d.ts +9 -0
  46. package/dist/mjs/polynomial/solveQuadratic.mjs +37 -0
  47. package/dist/mjs/rounding.d.ts +15 -0
  48. package/dist/mjs/rounding.mjs +139 -0
  49. package/dist/mjs/shapes/Abstract2DShape.d.ts +49 -0
  50. package/dist/mjs/shapes/Abstract2DShape.mjs +36 -0
  51. package/dist/mjs/shapes/BezierJSWrapper.d.ts +36 -0
  52. package/dist/mjs/shapes/BezierJSWrapper.mjs +89 -0
  53. package/dist/mjs/shapes/CubicBezier.d.ts +17 -0
  54. package/dist/mjs/shapes/CubicBezier.mjs +30 -0
  55. package/dist/mjs/shapes/LineSegment2.d.ts +70 -0
  56. package/dist/mjs/shapes/LineSegment2.mjs +176 -0
  57. package/dist/mjs/shapes/Path.d.ts +96 -0
  58. package/dist/mjs/shapes/Path.mjs +759 -0
  59. package/dist/mjs/shapes/PointShape2D.d.ts +18 -0
  60. package/dist/mjs/shapes/PointShape2D.mjs +26 -0
  61. package/dist/mjs/shapes/QuadraticBezier.d.ts +35 -0
  62. package/dist/mjs/shapes/QuadraticBezier.mjs +113 -0
  63. package/dist/mjs/shapes/Rect2.d.ts +58 -0
  64. package/dist/mjs/shapes/Rect2.mjs +252 -0
  65. package/dist/mjs/shapes/Triangle.d.ts +46 -0
  66. package/dist/mjs/shapes/Triangle.mjs +121 -0
  67. package/package.json +48 -0
  68. package/tsconfig.json +7 -0
  69. package/typedoc.json +5 -0
@@ -0,0 +1,18 @@
1
+ import { Point2 } from '../Vec2';
2
+ import Vec3 from '../Vec3';
3
+ import Abstract2DShape from './Abstract2DShape';
4
+ import LineSegment2 from './LineSegment2';
5
+ import Rect2 from './Rect2';
6
+ /**
7
+ * Like a {@link Point2}, but with additional functionality (e.g. SDF).
8
+ *
9
+ * Access the internal `Point2` using the `p` property.
10
+ */
11
+ declare class PointShape2D extends Abstract2DShape {
12
+ readonly p: Point2;
13
+ constructor(p: Point2);
14
+ signedDistance(point: Vec3): number;
15
+ intersectsLineSegment(lineSegment: LineSegment2, epsilon?: number): Vec3[];
16
+ getTightBoundingBox(): Rect2;
17
+ }
18
+ export default PointShape2D;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
7
+ const Rect2_1 = __importDefault(require("./Rect2"));
8
+ /**
9
+ * Like a {@link Point2}, but with additional functionality (e.g. SDF).
10
+ *
11
+ * Access the internal `Point2` using the `p` property.
12
+ */
13
+ class PointShape2D extends Abstract2DShape_1.default {
14
+ constructor(p) {
15
+ super();
16
+ this.p = p;
17
+ }
18
+ signedDistance(point) {
19
+ return this.p.minus(point).magnitude();
20
+ }
21
+ intersectsLineSegment(lineSegment, epsilon) {
22
+ if (lineSegment.containsPoint(this.p, epsilon)) {
23
+ return [this.p];
24
+ }
25
+ return [];
26
+ }
27
+ getTightBoundingBox() {
28
+ return new Rect2_1.default(this.p.x, this.p.y, 0, 0);
29
+ }
30
+ }
31
+ exports.default = PointShape2D;
@@ -0,0 +1,35 @@
1
+ import { Point2, Vec2 } from '../Vec2';
2
+ import BezierJSWrapper from './BezierJSWrapper';
3
+ import Rect2 from './Rect2';
4
+ /**
5
+ * A wrapper around `bezier-js`'s quadratic Bézier.
6
+ *
7
+ * This wrappper lazy-loads `bezier-js`'s Bézier and can perform some operations
8
+ * without loading it at all (e.g. `normal`, `at`, and `approximateDistance`).
9
+ */
10
+ export declare class QuadraticBezier extends BezierJSWrapper {
11
+ readonly p0: Point2;
12
+ readonly p1: Point2;
13
+ readonly p2: Point2;
14
+ constructor(p0: Point2, p1: Point2, p2: Point2);
15
+ /**
16
+ * Returns a component of a quadratic Bézier curve at t, where p0,p1,p2 are either all x or
17
+ * all y components of the target curve.
18
+ */
19
+ private static componentAt;
20
+ private static derivativeComponentAt;
21
+ /**
22
+ * @returns the curve evaluated at `t`.
23
+ */
24
+ at(t: number): Point2;
25
+ derivativeAt(t: number): Point2;
26
+ normal(t: number): Vec2;
27
+ /** @returns an overestimate of this shape's bounding box. */
28
+ getLooseBoundingBox(): Rect2;
29
+ /**
30
+ * @returns the *approximate* distance from `point` to this curve.
31
+ */
32
+ approximateDistance(point: Point2): number;
33
+ getPoints(): import("../Vec3").Vec3[];
34
+ }
35
+ export default QuadraticBezier;
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.QuadraticBezier = void 0;
7
+ const Vec2_1 = require("../Vec2");
8
+ const solveQuadratic_1 = __importDefault(require("../polynomial/solveQuadratic"));
9
+ const BezierJSWrapper_1 = __importDefault(require("./BezierJSWrapper"));
10
+ const Rect2_1 = __importDefault(require("./Rect2"));
11
+ /**
12
+ * A wrapper around `bezier-js`'s quadratic Bézier.
13
+ *
14
+ * This wrappper lazy-loads `bezier-js`'s Bézier and can perform some operations
15
+ * without loading it at all (e.g. `normal`, `at`, and `approximateDistance`).
16
+ */
17
+ class QuadraticBezier extends BezierJSWrapper_1.default {
18
+ constructor(p0, p1, p2) {
19
+ super();
20
+ this.p0 = p0;
21
+ this.p1 = p1;
22
+ this.p2 = p2;
23
+ }
24
+ /**
25
+ * Returns a component of a quadratic Bézier curve at t, where p0,p1,p2 are either all x or
26
+ * all y components of the target curve.
27
+ */
28
+ static componentAt(t, p0, p1, p2) {
29
+ return p0 + t * (-2 * p0 + 2 * p1) + t * t * (p0 - 2 * p1 + p2);
30
+ }
31
+ static derivativeComponentAt(t, p0, p1, p2) {
32
+ return -2 * p0 + 2 * p1 + 2 * t * (p0 - 2 * p1 + p2);
33
+ }
34
+ /**
35
+ * @returns the curve evaluated at `t`.
36
+ */
37
+ at(t) {
38
+ const p0 = this.p0;
39
+ const p1 = this.p1;
40
+ const p2 = this.p2;
41
+ return Vec2_1.Vec2.of(QuadraticBezier.componentAt(t, p0.x, p1.x, p2.x), QuadraticBezier.componentAt(t, p0.y, p1.y, p2.y));
42
+ }
43
+ derivativeAt(t) {
44
+ const p0 = this.p0;
45
+ const p1 = this.p1;
46
+ const p2 = this.p2;
47
+ return Vec2_1.Vec2.of(QuadraticBezier.derivativeComponentAt(t, p0.x, p1.x, p2.x), QuadraticBezier.derivativeComponentAt(t, p0.y, p1.y, p2.y));
48
+ }
49
+ normal(t) {
50
+ const tangent = this.derivativeAt(t);
51
+ return tangent.orthog().normalized();
52
+ }
53
+ /** @returns an overestimate of this shape's bounding box. */
54
+ getLooseBoundingBox() {
55
+ return Rect2_1.default.bboxOf([this.p0, this.p1, this.p2]);
56
+ }
57
+ /**
58
+ * @returns the *approximate* distance from `point` to this curve.
59
+ */
60
+ approximateDistance(point) {
61
+ // We want to minimize f(t) = |B(t) - p|².
62
+ // Expanding,
63
+ // f(t) = (Bₓ(t) - pₓ)² + (Bᵧ(t) - pᵧ)²
64
+ // ⇒ f'(t) = Dₜ(Bₓ(t) - pₓ)² + Dₜ(Bᵧ(t) - pᵧ)²
65
+ //
66
+ // Considering just one component,
67
+ // Dₜ(Bₓ(t) - pₓ)² = 2(Bₓ(t) - pₓ)(DₜBₓ(t))
68
+ // = 2(Bₓ(t)DₜBₓ(t) - pₓBₓ(t))
69
+ // = 2(p0ₓ + (t)(-2p0ₓ + 2p1ₓ) + (t²)(p0ₓ - 2p1ₓ + p2ₓ) - pₓ)((-2p0ₓ + 2p1ₓ) + 2(t)(p0ₓ - 2p1ₓ + p2ₓ))
70
+ // - (pₓ)((-2p0ₓ + 2p1ₓ) + (t)(p0ₓ - 2p1ₓ + p2ₓ))
71
+ const A = this.p0.x - point.x;
72
+ const B = -2 * this.p0.x + 2 * this.p1.x;
73
+ const C = this.p0.x - 2 * this.p1.x + this.p2.x;
74
+ // Let A = p0ₓ - pₓ, B = -2p0ₓ + 2p1ₓ, C = p0ₓ - 2p1ₓ + p2ₓ. We then have,
75
+ // Dₜ(Bₓ(t) - pₓ)²
76
+ // = 2(A + tB + t²C)(B + 2tC) - (pₓ)(B + 2tC)
77
+ // = 2(AB + tB² + t²BC + 2tCA + 2tCtB + 2tCt²C) - pₓB - pₓ2tC
78
+ // = 2(AB + tB² + 2tCA + t²BC + 2t²CB + 2C²t³) - pₓB - pₓ2tC
79
+ // = 2AB + 2t(B² + 2CA) + 2t²(BC + 2CB) + 4C²t³ - pₓB - pₓ2tC
80
+ // = 2AB + 2t(B² + 2CA - pₓC) + 2t²(BC + 2CB) + 4C²t³ - pₓB
81
+ //
82
+ const D = this.p0.y - point.y;
83
+ const E = -2 * this.p0.y + 2 * this.p1.y;
84
+ const F = this.p0.y - 2 * this.p1.y + this.p2.y;
85
+ // Using D = p0ᵧ - pᵧ, E = -2p0ᵧ + 2p1ᵧ, F = p0ᵧ - 2p1ᵧ + p2ᵧ, we thus have,
86
+ // f'(t) = 2AB + 2t(B² + 2CA - pₓC) + 2t²(BC + 2CB) + 4C²t³ - pₓB
87
+ // + 2DE + 2t(E² + 2FD - pᵧF) + 2t²(EF + 2FE) + 4F²t³ - pᵧE
88
+ const a = 2 * A * B + 2 * D * E - point.x * B - point.y * E;
89
+ const b = 2 * B * B + 2 * E * E + 2 * C * A + 2 * F * D - point.x * C - point.y * F;
90
+ const c = 2 * E * F + 2 * B * C + 2 * C * B + 2 * F * E;
91
+ //const d = 4 * C * C + 4 * F * F;
92
+ // Thus,
93
+ // f'(t) = a + bt + ct² + dt³
94
+ const fDerivAtZero = a;
95
+ const f2ndDerivAtZero = b;
96
+ const f3rdDerivAtZero = 2 * c;
97
+ // Using the first few terms of a Maclaurin series to approximate f'(t),
98
+ // f'(t) ≈ f'(0) + t f''(0) + t² f'''(0) / 2
99
+ let [min1, min2] = (0, solveQuadratic_1.default)(f3rdDerivAtZero / 2, f2ndDerivAtZero, fDerivAtZero);
100
+ // If the quadratic has no solutions, approximate.
101
+ if (isNaN(min1)) {
102
+ min1 = 0.25;
103
+ }
104
+ if (isNaN(min2)) {
105
+ min2 = 0.75;
106
+ }
107
+ const at1 = this.at(min1);
108
+ const at2 = this.at(min2);
109
+ const sqrDist1 = at1.minus(point).magnitudeSquared();
110
+ const sqrDist2 = at2.minus(point).magnitudeSquared();
111
+ const sqrDist3 = this.at(0).minus(point).magnitudeSquared();
112
+ const sqrDist4 = this.at(1).minus(point).magnitudeSquared();
113
+ return Math.sqrt(Math.min(sqrDist1, sqrDist2, sqrDist3, sqrDist4));
114
+ }
115
+ getPoints() {
116
+ return [this.p0, this.p1, this.p2];
117
+ }
118
+ }
119
+ exports.QuadraticBezier = QuadraticBezier;
120
+ exports.default = QuadraticBezier;
@@ -0,0 +1,58 @@
1
+ import LineSegment2 from './LineSegment2';
2
+ import Mat33 from '../Mat33';
3
+ import { Point2, Vec2 } from '../Vec2';
4
+ import Abstract2DShape from './Abstract2DShape';
5
+ import Vec3 from '../Vec3';
6
+ /** An object that can be converted to a Rect2. */
7
+ export interface RectTemplate {
8
+ x: number;
9
+ y: number;
10
+ w?: number;
11
+ h?: number;
12
+ width?: number;
13
+ height?: number;
14
+ }
15
+ export declare class Rect2 extends Abstract2DShape {
16
+ readonly x: number;
17
+ readonly y: number;
18
+ readonly w: number;
19
+ readonly h: number;
20
+ readonly topLeft: Point2;
21
+ readonly size: Vec2;
22
+ readonly bottomRight: Point2;
23
+ readonly area: number;
24
+ constructor(x: number, y: number, w: number, h: number);
25
+ translatedBy(vec: Vec2): Rect2;
26
+ resizedTo(size: Vec2): Rect2;
27
+ containsPoint(other: Point2): boolean;
28
+ containsRect(other: Rect2): boolean;
29
+ intersects(other: Rect2): boolean;
30
+ intersection(other: Rect2): Rect2 | null;
31
+ union(other: Rect2): Rect2;
32
+ divideIntoGrid(columns: number, rows: number): Rect2[];
33
+ grownToPoint(point: Point2, margin?: number): Rect2;
34
+ grownBy(margin: number): Rect2;
35
+ getClosestPointOnBoundaryTo(target: Point2): Vec3;
36
+ get corners(): Point2[];
37
+ get maxDimension(): number;
38
+ get topRight(): Vec3;
39
+ get bottomLeft(): Vec3;
40
+ get width(): number;
41
+ get height(): number;
42
+ get center(): Vec3;
43
+ getEdges(): LineSegment2[];
44
+ intersectsLineSegment(lineSegment: LineSegment2): Point2[];
45
+ signedDistance(point: Vec3): number;
46
+ getTightBoundingBox(): Rect2;
47
+ transformedBoundingBox(affineTransform: Mat33): Rect2;
48
+ /** @return true iff this is equal to [other] ± fuzz */
49
+ eq(other: Rect2, fuzz?: number): boolean;
50
+ toString(): string;
51
+ static fromCorners(corner1: Point2, corner2: Point2): Rect2;
52
+ static bboxOf(points: Point2[], margin?: number): Rect2;
53
+ static union(...rects: Rect2[]): Rect2;
54
+ static of(template: RectTemplate): Rect2;
55
+ static empty: Rect2;
56
+ static unitSquare: Rect2;
57
+ }
58
+ export default Rect2;
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Rect2 = void 0;
7
+ const LineSegment2_1 = __importDefault(require("./LineSegment2"));
8
+ const Vec2_1 = require("../Vec2");
9
+ const Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
10
+ // invariant: w ≥ 0, h ≥ 0, immutable
11
+ class Rect2 extends Abstract2DShape_1.default {
12
+ constructor(x, y, w, h) {
13
+ super();
14
+ this.x = x;
15
+ this.y = y;
16
+ this.w = w;
17
+ this.h = h;
18
+ if (w < 0) {
19
+ this.x += w;
20
+ this.w = Math.abs(w);
21
+ }
22
+ if (h < 0) {
23
+ this.y += h;
24
+ this.h = Math.abs(h);
25
+ }
26
+ // Precompute/store vector forms.
27
+ this.topLeft = Vec2_1.Vec2.of(this.x, this.y);
28
+ this.size = Vec2_1.Vec2.of(this.w, this.h);
29
+ this.bottomRight = this.topLeft.plus(this.size);
30
+ this.area = this.w * this.h;
31
+ }
32
+ translatedBy(vec) {
33
+ return new Rect2(vec.x + this.x, vec.y + this.y, this.w, this.h);
34
+ }
35
+ // Returns a copy of this with the given size (but same top-left).
36
+ resizedTo(size) {
37
+ return new Rect2(this.x, this.y, size.x, size.y);
38
+ }
39
+ containsPoint(other) {
40
+ return this.x <= other.x && this.y <= other.y
41
+ && this.x + this.w >= other.x && this.y + this.h >= other.y;
42
+ }
43
+ containsRect(other) {
44
+ return this.x <= other.x && this.y <= other.y
45
+ && this.bottomRight.x >= other.bottomRight.x
46
+ && this.bottomRight.y >= other.bottomRight.y;
47
+ }
48
+ intersects(other) {
49
+ // Project along x/y axes.
50
+ const thisMinX = this.x;
51
+ const thisMaxX = thisMinX + this.w;
52
+ const otherMinX = other.x;
53
+ const otherMaxX = other.x + other.w;
54
+ if (thisMaxX < otherMinX || thisMinX > otherMaxX) {
55
+ return false;
56
+ }
57
+ const thisMinY = this.y;
58
+ const thisMaxY = thisMinY + this.h;
59
+ const otherMinY = other.y;
60
+ const otherMaxY = other.y + other.h;
61
+ if (thisMaxY < otherMinY || thisMinY > otherMaxY) {
62
+ return false;
63
+ }
64
+ return true;
65
+ }
66
+ // Returns the overlap of this and [other], or null, if no such
67
+ // overlap exists
68
+ intersection(other) {
69
+ if (!this.intersects(other)) {
70
+ return null;
71
+ }
72
+ const topLeft = this.topLeft.zip(other.topLeft, Math.max);
73
+ const bottomRight = this.bottomRight.zip(other.bottomRight, Math.min);
74
+ return Rect2.fromCorners(topLeft, bottomRight);
75
+ }
76
+ // Returns a new rectangle containing both [this] and [other].
77
+ union(other) {
78
+ return Rect2.union(this, other);
79
+ }
80
+ // Returns a the subdivision of this into [columns] columns
81
+ // and [rows] rows. For example,
82
+ // Rect2.unitSquare.divideIntoGrid(2, 2)
83
+ // -> [ Rect2(0, 0, 0.5, 0.5), Rect2(0.5, 0, 0.5, 0.5), Rect2(0, 0.5, 0.5, 0.5), Rect2(0.5, 0.5, 0.5, 0.5) ]
84
+ // The rectangles are ordered in row-major order.
85
+ divideIntoGrid(columns, rows) {
86
+ const result = [];
87
+ if (columns <= 0 || rows <= 0) {
88
+ return result;
89
+ }
90
+ const eachRectWidth = this.w / columns;
91
+ const eachRectHeight = this.h / rows;
92
+ if (eachRectWidth === 0) {
93
+ columns = 1;
94
+ }
95
+ if (eachRectHeight === 0) {
96
+ rows = 1;
97
+ }
98
+ for (let j = 0; j < rows; j++) {
99
+ for (let i = 0; i < columns; i++) {
100
+ const x = eachRectWidth * i + this.x;
101
+ const y = eachRectHeight * j + this.y;
102
+ result.push(new Rect2(x, y, eachRectWidth, eachRectHeight));
103
+ }
104
+ }
105
+ return result;
106
+ }
107
+ // Returns a rectangle containing this and [point].
108
+ // [margin] is the minimum distance between the new point and the edge
109
+ // of the resultant rectangle.
110
+ grownToPoint(point, margin = 0) {
111
+ const otherRect = new Rect2(point.x - margin, point.y - margin, margin * 2, margin * 2);
112
+ return this.union(otherRect);
113
+ }
114
+ // Returns this grown by [margin] in both the x and y directions.
115
+ grownBy(margin) {
116
+ if (margin === 0) {
117
+ return this;
118
+ }
119
+ return new Rect2(this.x - margin, this.y - margin, this.w + margin * 2, this.h + margin * 2);
120
+ }
121
+ getClosestPointOnBoundaryTo(target) {
122
+ const closestEdgePoints = this.getEdges().map(edge => {
123
+ return edge.closestPointTo(target);
124
+ });
125
+ let closest = null;
126
+ let closestDist = null;
127
+ for (const point of closestEdgePoints) {
128
+ const dist = point.minus(target).length();
129
+ if (closestDist === null || dist < closestDist) {
130
+ closest = point;
131
+ closestDist = dist;
132
+ }
133
+ }
134
+ return closest;
135
+ }
136
+ get corners() {
137
+ return [
138
+ this.bottomRight,
139
+ this.topRight,
140
+ this.topLeft,
141
+ this.bottomLeft,
142
+ ];
143
+ }
144
+ get maxDimension() {
145
+ return Math.max(this.w, this.h);
146
+ }
147
+ get topRight() {
148
+ return this.bottomRight.plus(Vec2_1.Vec2.of(0, -this.h));
149
+ }
150
+ get bottomLeft() {
151
+ return this.topLeft.plus(Vec2_1.Vec2.of(0, this.h));
152
+ }
153
+ get width() {
154
+ return this.w;
155
+ }
156
+ get height() {
157
+ return this.h;
158
+ }
159
+ get center() {
160
+ return this.topLeft.plus(this.size.times(0.5));
161
+ }
162
+ // Returns edges in the order
163
+ // [ rightEdge, topEdge, leftEdge, bottomEdge ]
164
+ getEdges() {
165
+ const corners = this.corners;
166
+ return [
167
+ new LineSegment2_1.default(corners[0], corners[1]),
168
+ new LineSegment2_1.default(corners[1], corners[2]),
169
+ new LineSegment2_1.default(corners[2], corners[3]),
170
+ new LineSegment2_1.default(corners[3], corners[0]),
171
+ ];
172
+ }
173
+ intersectsLineSegment(lineSegment) {
174
+ const result = [];
175
+ for (const edge of this.getEdges()) {
176
+ const intersection = edge.intersectsLineSegment(lineSegment);
177
+ intersection.forEach(point => result.push(point));
178
+ }
179
+ return result;
180
+ }
181
+ signedDistance(point) {
182
+ const closestBoundaryPoint = this.getClosestPointOnBoundaryTo(point);
183
+ const dist = point.minus(closestBoundaryPoint).magnitude();
184
+ if (this.containsPoint(point)) {
185
+ return -dist;
186
+ }
187
+ return dist;
188
+ }
189
+ getTightBoundingBox() {
190
+ return this;
191
+ }
192
+ // [affineTransform] is a transformation matrix that both scales and **translates**.
193
+ // the bounding box of this' four corners after transformed by the given affine transformation.
194
+ transformedBoundingBox(affineTransform) {
195
+ return Rect2.bboxOf(this.corners.map(corner => affineTransform.transformVec2(corner)));
196
+ }
197
+ /** @return true iff this is equal to [other] ± fuzz */
198
+ eq(other, fuzz = 0) {
199
+ return this.topLeft.eq(other.topLeft, fuzz) && this.size.eq(other.size, fuzz);
200
+ }
201
+ toString() {
202
+ return `Rect(point(${this.x}, ${this.y}), size(${this.w}, ${this.h}))`;
203
+ }
204
+ static fromCorners(corner1, corner2) {
205
+ return new Rect2(Math.min(corner1.x, corner2.x), Math.min(corner1.y, corner2.y), Math.abs(corner1.x - corner2.x), Math.abs(corner1.y - corner2.y));
206
+ }
207
+ // Returns a box that contains all points in [points] with at least [margin]
208
+ // between each point and the edge of the box.
209
+ static bboxOf(points, margin = 0) {
210
+ let minX = 0;
211
+ let minY = 0;
212
+ let maxX = 0;
213
+ let maxY = 0;
214
+ let isFirst = true;
215
+ for (const point of points) {
216
+ if (isFirst) {
217
+ minX = point.x;
218
+ minY = point.y;
219
+ maxX = point.x;
220
+ maxY = point.y;
221
+ isFirst = false;
222
+ }
223
+ minX = Math.min(minX, point.x);
224
+ minY = Math.min(minY, point.y);
225
+ maxX = Math.max(maxX, point.x);
226
+ maxY = Math.max(maxY, point.y);
227
+ }
228
+ return Rect2.fromCorners(Vec2_1.Vec2.of(minX - margin, minY - margin), Vec2_1.Vec2.of(maxX + margin, maxY + margin));
229
+ }
230
+ // @returns a rectangle that contains all of the given rectangles, the bounding box
231
+ // of the given rectangles.
232
+ static union(...rects) {
233
+ if (rects.length === 0) {
234
+ return Rect2.empty;
235
+ }
236
+ const firstRect = rects[0];
237
+ let minX = firstRect.topLeft.x;
238
+ let minY = firstRect.topLeft.y;
239
+ let maxX = firstRect.bottomRight.x;
240
+ let maxY = firstRect.bottomRight.y;
241
+ for (let i = 1; i < rects.length; i++) {
242
+ const rect = rects[i];
243
+ minX = Math.min(minX, rect.topLeft.x);
244
+ minY = Math.min(minY, rect.topLeft.y);
245
+ maxX = Math.max(maxX, rect.bottomRight.x);
246
+ maxY = Math.max(maxY, rect.bottomRight.y);
247
+ }
248
+ return new Rect2(minX, minY, maxX - minX, maxY - minY);
249
+ }
250
+ static of(template) {
251
+ const width = template.width ?? template.w ?? 0;
252
+ const height = template.height ?? template.h ?? 0;
253
+ return new Rect2(template.x, template.y, width, height);
254
+ }
255
+ }
256
+ exports.Rect2 = Rect2;
257
+ Rect2.empty = new Rect2(0, 0, 0, 0);
258
+ Rect2.unitSquare = new Rect2(0, 0, 1, 1);
259
+ exports.default = Rect2;
@@ -0,0 +1,46 @@
1
+ import Mat33 from '../Mat33';
2
+ import { Point2 } from '../Vec2';
3
+ import Vec3 from '../Vec3';
4
+ import Abstract2DShape from './Abstract2DShape';
5
+ import LineSegment2 from './LineSegment2';
6
+ import Rect2 from './Rect2';
7
+ type TriangleBoundary = [LineSegment2, LineSegment2, LineSegment2];
8
+ export default class Triangle extends Abstract2DShape {
9
+ #private;
10
+ readonly vertex1: Vec3;
11
+ readonly vertex2: Vec3;
12
+ readonly vertex3: Vec3;
13
+ /**
14
+ * @see {@link fromVertices}
15
+ */
16
+ protected constructor(vertex1: Vec3, vertex2: Vec3, vertex3: Vec3);
17
+ /**
18
+ * Creates a triangle from its three corners. Corners may be stored in a different
19
+ * order than given.
20
+ */
21
+ static fromVertices(vertex1: Vec3, vertex2: Vec3, vertex3: Vec3): Triangle;
22
+ get vertices(): [Point2, Point2, Point2];
23
+ map(mapping: (vertex: Vec3) => Vec3): Triangle;
24
+ transformed2DBy(affineTransform: Mat33): Triangle;
25
+ transformedBy(linearTransform: Mat33): Triangle;
26
+ /**
27
+ * Returns the sides of this triangle, as an array of `LineSegment2`s.
28
+ *
29
+ * The first side is from `vertex1` to `vertex2`, the next from `vertex2` to `vertex3`,
30
+ * and the last from `vertex3` to `vertex1`.
31
+ */
32
+ getEdges(): TriangleBoundary;
33
+ intersectsLineSegment(lineSegment: LineSegment2): Vec3[];
34
+ /** @inheritdoc */
35
+ containsPoint(point: Vec3, epsilon?: number): boolean;
36
+ /**
37
+ * @returns the signed distance from `point` to the closest edge of this triangle.
38
+ *
39
+ * If `point` is inside `this`, the result is negative, otherwise, the result is
40
+ * positive.
41
+ */
42
+ signedDistance(point: Vec3): number;
43
+ /** @inheritdoc */
44
+ getTightBoundingBox(): Rect2;
45
+ }
46
+ export {};