@js-draw/math 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,17 @@
1
+ import { Point2 } from '../Vec2';
2
+ import BezierJSWrapper from './BezierJSWrapper';
3
+ import Rect2 from './Rect2';
4
+ /**
5
+ * A wrapper around [`bezier-js`](https://github.com/Pomax/bezierjs)'s cubic Bezier.
6
+ */
7
+ declare class CubicBezier extends BezierJSWrapper {
8
+ readonly p0: Point2;
9
+ readonly p1: Point2;
10
+ readonly p2: Point2;
11
+ readonly p3: Point2;
12
+ constructor(p0: Point2, p1: Point2, p2: Point2, p3: Point2);
13
+ getPoints(): import("../Vec3").Vec3[];
14
+ /** Returns an overestimate of this shape's bounding box. */
15
+ getLooseBoundingBox(): Rect2;
16
+ }
17
+ export default CubicBezier;
@@ -0,0 +1,30 @@
1
+ import BezierJSWrapper from './BezierJSWrapper.mjs';
2
+ import Rect2 from './Rect2.mjs';
3
+ /**
4
+ * A wrapper around [`bezier-js`](https://github.com/Pomax/bezierjs)'s cubic Bezier.
5
+ */
6
+ class CubicBezier extends BezierJSWrapper {
7
+ constructor(
8
+ // Start point
9
+ p0,
10
+ // Control point 1
11
+ p1,
12
+ // Control point 2
13
+ p2,
14
+ // End point
15
+ p3) {
16
+ super();
17
+ this.p0 = p0;
18
+ this.p1 = p1;
19
+ this.p2 = p2;
20
+ this.p3 = p3;
21
+ }
22
+ getPoints() {
23
+ return [this.p0, this.p1, this.p2, this.p3];
24
+ }
25
+ /** Returns an overestimate of this shape's bounding box. */
26
+ getLooseBoundingBox() {
27
+ return Rect2.bboxOf([this.p0, this.p1, this.p2, this.p3]);
28
+ }
29
+ }
30
+ export default CubicBezier;
@@ -0,0 +1,70 @@
1
+ import Mat33 from '../Mat33';
2
+ import Rect2 from './Rect2';
3
+ import { Vec2, Point2 } from '../Vec2';
4
+ import Abstract2DShape from './Abstract2DShape';
5
+ interface IntersectionResult {
6
+ point: Point2;
7
+ t: number;
8
+ }
9
+ /** Represents a line segment. A `LineSegment2` is immutable. */
10
+ export declare class LineSegment2 extends Abstract2DShape {
11
+ private readonly point1;
12
+ private readonly point2;
13
+ /**
14
+ * The **unit** direction vector of this line segment, from
15
+ * `point1` to `point2`.
16
+ *
17
+ * In other words, `direction` is `point2.minus(point1).normalized()`
18
+ * (perhaps except when `point1` is equal to `point2`).
19
+ */
20
+ readonly direction: Vec2;
21
+ /** The distance between `point1` and `point2`. */
22
+ readonly length: number;
23
+ /** The bounding box of this line segment. */
24
+ readonly bbox: Rect2;
25
+ /** Creates a new `LineSegment2` from its endpoints. */
26
+ constructor(point1: Point2, point2: Point2);
27
+ /** Alias for `point1`. */
28
+ get p1(): Point2;
29
+ /** Alias for `point2`. */
30
+ get p2(): Point2;
31
+ /**
32
+ * Gets a point a distance `t` along this line.
33
+ *
34
+ * @deprecated
35
+ */
36
+ get(t: number): Point2;
37
+ /**
38
+ * Returns a point a fraction, `t`, along this line segment.
39
+ * Thus, `segment.at(0)` returns `segment.p1` and `segment.at(1)` returns
40
+ * `segment.p2`.
41
+ *
42
+ * `t` should be in `[0, 1]`.
43
+ */
44
+ at(t: number): Point2;
45
+ intersection(other: LineSegment2): IntersectionResult | null;
46
+ intersects(other: LineSegment2): boolean;
47
+ /**
48
+ * Returns the points at which this line segment intersects the
49
+ * given line segment.
50
+ *
51
+ * Note that {@link intersects} returns *whether* this line segment intersects another
52
+ * line segment. This method, by contrast, returns **the point** at which the intersection
53
+ * occurs, if such a point exists.
54
+ */
55
+ intersectsLineSegment(lineSegment: LineSegment2): import("../Vec3").Vec3[];
56
+ closestPointTo(target: Point2): import("../Vec3").Vec3;
57
+ /**
58
+ * Returns the distance from this line segment to `target`.
59
+ *
60
+ * Because a line segment has no interior, this signed distance is equivalent to
61
+ * the full distance between `target` and this line segment.
62
+ */
63
+ signedDistance(target: Point2): number;
64
+ /** Returns a copy of this line segment transformed by the given `affineTransfm`. */
65
+ transformedBy(affineTransfm: Mat33): LineSegment2;
66
+ /** @inheritdoc */
67
+ getTightBoundingBox(): Rect2;
68
+ toString(): string;
69
+ }
70
+ export default LineSegment2;
@@ -0,0 +1,176 @@
1
+ import Rect2 from './Rect2.mjs';
2
+ import { Vec2 } from '../Vec2.mjs';
3
+ import Abstract2DShape from './Abstract2DShape.mjs';
4
+ /** Represents a line segment. A `LineSegment2` is immutable. */
5
+ export class LineSegment2 extends Abstract2DShape {
6
+ /** Creates a new `LineSegment2` from its endpoints. */
7
+ constructor(point1, point2) {
8
+ super();
9
+ this.point1 = point1;
10
+ this.point2 = point2;
11
+ this.bbox = Rect2.bboxOf([point1, point2]);
12
+ this.direction = point2.minus(point1);
13
+ this.length = this.direction.magnitude();
14
+ // Normalize
15
+ if (this.length > 0) {
16
+ this.direction = this.direction.times(1 / this.length);
17
+ }
18
+ }
19
+ // Accessors to make LineSegment2 compatible with bezier-js's
20
+ // interface
21
+ /** Alias for `point1`. */
22
+ get p1() {
23
+ return this.point1;
24
+ }
25
+ /** Alias for `point2`. */
26
+ get p2() {
27
+ return this.point2;
28
+ }
29
+ /**
30
+ * Gets a point a distance `t` along this line.
31
+ *
32
+ * @deprecated
33
+ */
34
+ get(t) {
35
+ return this.point1.plus(this.direction.times(t));
36
+ }
37
+ /**
38
+ * Returns a point a fraction, `t`, along this line segment.
39
+ * Thus, `segment.at(0)` returns `segment.p1` and `segment.at(1)` returns
40
+ * `segment.p2`.
41
+ *
42
+ * `t` should be in `[0, 1]`.
43
+ */
44
+ at(t) {
45
+ return this.get(t * this.length);
46
+ }
47
+ intersection(other) {
48
+ // We want x₁(t) = x₂(t) and y₁(t) = y₂(t)
49
+ // Observe that
50
+ // x = this.point1.x + this.direction.x · t₁
51
+ // = other.point1.x + other.direction.x · t₂
52
+ // Thus,
53
+ // t₁ = (x - this.point1.x) / this.direction.x
54
+ // = (y - this.point1.y) / this.direction.y
55
+ // and
56
+ // t₂ = (x - other.point1.x) / other.direction.x
57
+ // (and similarly for y)
58
+ //
59
+ // Letting o₁ₓ = this.point1.x, o₂ₓ = other.point1.x,
60
+ // d₁ᵧ = this.direction.y, ...
61
+ //
62
+ // We can substitute these into the equations for y:
63
+ // y = o₁ᵧ + d₁ᵧ · (x - o₁ₓ) / d₁ₓ
64
+ // = o₂ᵧ + d₂ᵧ · (x - o₂ₓ) / d₂ₓ
65
+ // ⇒ o₁ᵧ - o₂ᵧ = d₂ᵧ · (x - o₂ₓ) / d₂ₓ - d₁ᵧ · (x - o₁ₓ) / d₁ₓ
66
+ // = (d₂ᵧ/d₂ₓ)(x) - (d₂ᵧ/d₂ₓ)(o₂ₓ) - (d₁ᵧ/d₁ₓ)(x) + (d₁ᵧ/d₁ₓ)(o₁ₓ)
67
+ // = (x)(d₂ᵧ/d₂ₓ - d₁ᵧ/d₁ₓ) - (d₂ᵧ/d₂ₓ)(o₂ₓ) + (d₁ᵧ/d₁ₓ)(o₁ₓ)
68
+ // ⇒ (x)(d₂ᵧ/d₂ₓ - d₁ᵧ/d₁ₓ) = o₁ᵧ - o₂ᵧ + (d₂ᵧ/d₂ₓ)(o₂ₓ) - (d₁ᵧ/d₁ₓ)(o₁ₓ)
69
+ // ⇒ x = (o₁ᵧ - o₂ᵧ + (d₂ᵧ/d₂ₓ)(o₂ₓ) - (d₁ᵧ/d₁ₓ)(o₁ₓ))/(d₂ᵧ/d₂ₓ - d₁ᵧ/d₁ₓ)
70
+ // = (d₁ₓd₂ₓ)(o₁ᵧ - o₂ᵧ + (d₂ᵧ/d₂ₓ)(o₂ₓ) - (d₁ᵧ/d₁ₓ)(o₁ₓ))/(d₂ᵧd₁ₓ - d₁ᵧd₂ₓ)
71
+ // = ((o₁ᵧ - o₂ᵧ)((d₁ₓd₂ₓ)) + (d₂ᵧd₁ₓ)(o₂ₓ) - (d₁ᵧd₂ₓ)(o₁ₓ))/(d₂ᵧd₁ₓ - d₁ᵧd₂ₓ)
72
+ // ⇒ y = o₁ᵧ + d₁ᵧ · (x - o₁ₓ) / d₁ₓ = ...
73
+ let resultPoint, resultT;
74
+ if (this.direction.x === 0) {
75
+ // Vertical line: Where does the other have x = this.point1.x?
76
+ // x = o₁ₓ = o₂ₓ + d₂ₓ · (y - o₂ᵧ) / d₂ᵧ
77
+ // ⇒ (o₁ₓ - o₂ₓ)(d₂ᵧ/d₂ₓ) + o₂ᵧ = y
78
+ // Avoid division by zero
79
+ if (other.direction.x === 0 || this.direction.y === 0) {
80
+ return null;
81
+ }
82
+ const xIntersect = this.point1.x;
83
+ const yIntersect = (this.point1.x - other.point1.x) * other.direction.y / other.direction.x + other.point1.y;
84
+ resultPoint = Vec2.of(xIntersect, yIntersect);
85
+ resultT = (yIntersect - this.point1.y) / this.direction.y;
86
+ }
87
+ else {
88
+ // From above,
89
+ // x = ((o₁ᵧ - o₂ᵧ)(d₁ₓd₂ₓ) + (d₂ᵧd₁ₓ)(o₂ₓ) - (d₁ᵧd₂ₓ)(o₁ₓ))/(d₂ᵧd₁ₓ - d₁ᵧd₂ₓ)
90
+ const numerator = ((this.point1.y - other.point1.y) * this.direction.x * other.direction.x
91
+ + this.direction.x * other.direction.y * other.point1.x
92
+ - this.direction.y * other.direction.x * this.point1.x);
93
+ const denominator = (other.direction.y * this.direction.x
94
+ - this.direction.y * other.direction.x);
95
+ // Avoid dividing by zero. It means there is no intersection
96
+ if (denominator === 0) {
97
+ return null;
98
+ }
99
+ const xIntersect = numerator / denominator;
100
+ const t1 = (xIntersect - this.point1.x) / this.direction.x;
101
+ const yIntersect = this.point1.y + this.direction.y * t1;
102
+ resultPoint = Vec2.of(xIntersect, yIntersect);
103
+ resultT = (xIntersect - this.point1.x) / this.direction.x;
104
+ }
105
+ // Ensure the result is in this/the other segment.
106
+ const resultToP1 = resultPoint.minus(this.point1).magnitude();
107
+ const resultToP2 = resultPoint.minus(this.point2).magnitude();
108
+ const resultToP3 = resultPoint.minus(other.point1).magnitude();
109
+ const resultToP4 = resultPoint.minus(other.point2).magnitude();
110
+ if (resultToP1 > this.length
111
+ || resultToP2 > this.length
112
+ || resultToP3 > other.length
113
+ || resultToP4 > other.length) {
114
+ return null;
115
+ }
116
+ return {
117
+ point: resultPoint,
118
+ t: resultT,
119
+ };
120
+ }
121
+ intersects(other) {
122
+ return this.intersection(other) !== null;
123
+ }
124
+ /**
125
+ * Returns the points at which this line segment intersects the
126
+ * given line segment.
127
+ *
128
+ * Note that {@link intersects} returns *whether* this line segment intersects another
129
+ * line segment. This method, by contrast, returns **the point** at which the intersection
130
+ * occurs, if such a point exists.
131
+ */
132
+ intersectsLineSegment(lineSegment) {
133
+ const intersection = this.intersection(lineSegment);
134
+ if (intersection) {
135
+ return [intersection.point];
136
+ }
137
+ return [];
138
+ }
139
+ // Returns the closest point on this to [target]
140
+ closestPointTo(target) {
141
+ // Distance from P1 along this' direction.
142
+ const projectedDistFromP1 = target.minus(this.p1).dot(this.direction);
143
+ const projectedDistFromP2 = this.length - projectedDistFromP1;
144
+ const projection = this.p1.plus(this.direction.times(projectedDistFromP1));
145
+ if (projectedDistFromP1 > 0 && projectedDistFromP1 < this.length) {
146
+ return projection;
147
+ }
148
+ if (Math.abs(projectedDistFromP2) < Math.abs(projectedDistFromP1)) {
149
+ return this.p2;
150
+ }
151
+ else {
152
+ return this.p1;
153
+ }
154
+ }
155
+ /**
156
+ * Returns the distance from this line segment to `target`.
157
+ *
158
+ * Because a line segment has no interior, this signed distance is equivalent to
159
+ * the full distance between `target` and this line segment.
160
+ */
161
+ signedDistance(target) {
162
+ return this.closestPointTo(target).minus(target).magnitude();
163
+ }
164
+ /** Returns a copy of this line segment transformed by the given `affineTransfm`. */
165
+ transformedBy(affineTransfm) {
166
+ return new LineSegment2(affineTransfm.transformVec2(this.p1), affineTransfm.transformVec2(this.p2));
167
+ }
168
+ /** @inheritdoc */
169
+ getTightBoundingBox() {
170
+ return this.bbox;
171
+ }
172
+ toString() {
173
+ return `LineSegment(${this.p1.toString()}, ${this.p2.toString()})`;
174
+ }
175
+ }
176
+ export default LineSegment2;
@@ -0,0 +1,96 @@
1
+ import LineSegment2 from './LineSegment2';
2
+ import Mat33 from '../Mat33';
3
+ import Rect2 from './Rect2';
4
+ import { Point2 } from '../Vec2';
5
+ import Abstract2DShape from './Abstract2DShape';
6
+ export declare enum PathCommandType {
7
+ LineTo = 0,
8
+ MoveTo = 1,
9
+ CubicBezierTo = 2,
10
+ QuadraticBezierTo = 3
11
+ }
12
+ export interface CubicBezierPathCommand {
13
+ kind: PathCommandType.CubicBezierTo;
14
+ controlPoint1: Point2;
15
+ controlPoint2: Point2;
16
+ endPoint: Point2;
17
+ }
18
+ export interface QuadraticBezierPathCommand {
19
+ kind: PathCommandType.QuadraticBezierTo;
20
+ controlPoint: Point2;
21
+ endPoint: Point2;
22
+ }
23
+ export interface LinePathCommand {
24
+ kind: PathCommandType.LineTo;
25
+ point: Point2;
26
+ }
27
+ export interface MoveToPathCommand {
28
+ kind: PathCommandType.MoveTo;
29
+ point: Point2;
30
+ }
31
+ export type PathCommand = CubicBezierPathCommand | QuadraticBezierPathCommand | MoveToPathCommand | LinePathCommand;
32
+ interface IntersectionResult {
33
+ curve: Abstract2DShape;
34
+ /** @internal @deprecated */
35
+ parameterValue?: number;
36
+ point: Point2;
37
+ }
38
+ type GeometryType = Abstract2DShape;
39
+ type GeometryArrayType = Array<GeometryType>;
40
+ /**
41
+ * Represents a union of lines and curves.
42
+ */
43
+ export declare class Path {
44
+ readonly startPoint: Point2;
45
+ readonly parts: PathCommand[];
46
+ /**
47
+ * A rough estimate of the bounding box of the path.
48
+ * A slight overestimate.
49
+ * See {@link getExactBBox}
50
+ */
51
+ readonly bbox: Rect2;
52
+ constructor(startPoint: Point2, parts: PathCommand[]);
53
+ getExactBBox(): Rect2;
54
+ private cachedGeometry;
55
+ get geometry(): GeometryArrayType;
56
+ private cachedPolylineApproximation;
57
+ polylineApproximation(): LineSegment2[];
58
+ static computeBBoxForSegment(startPoint: Point2, part: PathCommand): Rect2;
59
+ /**
60
+ * Let `S` be a closed path a distance `strokeRadius` from this path.
61
+ *
62
+ * @returns Approximate intersections of `line` with `S` using ray marching, starting from
63
+ * both end points of `line` and each point in `additionalRaymarchStartPoints`.
64
+ */
65
+ private raymarchIntersectionWith;
66
+ /**
67
+ * Returns a list of intersections with this path. If `strokeRadius` is given,
68
+ * intersections are approximated with the surface `strokeRadius` away from this.
69
+ *
70
+ * If `strokeRadius > 0`, the resultant `parameterValue` has no defined value.
71
+ */
72
+ intersection(line: LineSegment2, strokeRadius?: number): IntersectionResult[];
73
+ private static mapPathCommand;
74
+ mapPoints(mapping: (point: Point2) => Point2): Path;
75
+ transformedBy(affineTransfm: Mat33): Path;
76
+ union(other: Path | null): Path;
77
+ private getEndPoint;
78
+ roughlyIntersects(rect: Rect2, strokeWidth?: number): boolean;
79
+ closedRoughlyIntersects(rect: Rect2): boolean;
80
+ static fromRect(rect: Rect2, lineWidth?: number | null): Path;
81
+ private cachedStringVersion;
82
+ toString(useNonAbsCommands?: boolean): string;
83
+ serialize(): string;
84
+ static toString(startPoint: Point2, parts: PathCommand[], onlyAbsCommands?: boolean): string;
85
+ /**
86
+ * Create a Path from a SVG path specification.
87
+ *
88
+ * ## To-do
89
+ * - TODO: Support a larger subset of SVG paths
90
+ * - Elliptical arcs are currently unsupported.
91
+ * - TODO: Support `s`,`t` commands shorthands.
92
+ */
93
+ static fromString(pathString: string): Path;
94
+ static empty: Path;
95
+ }
96
+ export default Path;