@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,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;