@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,203 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Vec3 = void 0;
4
+ /**
5
+ * A vector with three components, $\begin{pmatrix} x \\ y \\ z \end{pmatrix}$.
6
+ * Can also be used to represent a two-component vector.
7
+ *
8
+ * A `Vec3` is immutable.
9
+ *
10
+ * @example
11
+ *
12
+ * ```ts,runnable,console
13
+ * import { Vec3 } from '@js-draw/math';
14
+ *
15
+ * console.log('Vector addition:', Vec3.of(1, 2, 3).plus(Vec3.of(0, 1, 0)));
16
+ * console.log('Scalar multiplication:', Vec3.of(1, 2, 3).times(2));
17
+ * console.log('Cross products:', Vec3.unitX.cross(Vec3.unitY));
18
+ * console.log('Magnitude:', Vec3.of(1, 2, 3).length(), 'or', Vec3.of(1, 2, 3).magnitude());
19
+ * console.log('Square Magnitude:', Vec3.of(1, 2, 3).magnitudeSquared());
20
+ * console.log('As an array:', Vec3.unitZ.asArray());
21
+ * ```
22
+ */
23
+ class Vec3 {
24
+ constructor(x, y, z) {
25
+ this.x = x;
26
+ this.y = y;
27
+ this.z = z;
28
+ }
29
+ /** Returns the x, y components of this. */
30
+ get xy() {
31
+ // Useful for APIs that behave differently if .z is present.
32
+ return {
33
+ x: this.x,
34
+ y: this.y,
35
+ };
36
+ }
37
+ /** Construct a vector from three components. */
38
+ static of(x, y, z) {
39
+ return new Vec3(x, y, z);
40
+ }
41
+ /** Returns this' `idx`th component. For example, `Vec3.of(1, 2, 3).at(1) → 2`. */
42
+ at(idx) {
43
+ if (idx === 0)
44
+ return this.x;
45
+ if (idx === 1)
46
+ return this.y;
47
+ if (idx === 2)
48
+ return this.z;
49
+ throw new Error(`${idx} out of bounds!`);
50
+ }
51
+ /** Alias for this.magnitude. */
52
+ length() {
53
+ return this.magnitude();
54
+ }
55
+ magnitude() {
56
+ return Math.sqrt(this.dot(this));
57
+ }
58
+ magnitudeSquared() {
59
+ return this.dot(this);
60
+ }
61
+ /**
62
+ * Return this' angle in the XY plane (treats this as a Vec2).
63
+ *
64
+ * This is equivalent to `Math.atan2(vec.y, vec.x)`.
65
+ */
66
+ angle() {
67
+ return Math.atan2(this.y, this.x);
68
+ }
69
+ /**
70
+ * Returns a unit vector in the same direction as this.
71
+ *
72
+ * If `this` has zero length, the resultant vector has `NaN` components.
73
+ */
74
+ normalized() {
75
+ const norm = this.magnitude();
76
+ return Vec3.of(this.x / norm, this.y / norm, this.z / norm);
77
+ }
78
+ /**
79
+ * Like {@link normalized}, except returns zero if this has zero magnitude.
80
+ */
81
+ normalizedOrZero() {
82
+ if (this.eq(Vec3.zero)) {
83
+ return Vec3.zero;
84
+ }
85
+ return this.normalized();
86
+ }
87
+ /** @returns A copy of `this` multiplied by a scalar. */
88
+ times(c) {
89
+ return Vec3.of(this.x * c, this.y * c, this.z * c);
90
+ }
91
+ plus(v) {
92
+ return Vec3.of(this.x + v.x, this.y + v.y, this.z + v.z);
93
+ }
94
+ minus(v) {
95
+ return Vec3.of(this.x - v.x, this.y - v.y, this.z - v.z);
96
+ }
97
+ dot(other) {
98
+ return this.x * other.x + this.y * other.y + this.z * other.z;
99
+ }
100
+ cross(other) {
101
+ // | i j k |
102
+ // | x1 y1 z1| = (i)(y1z2 - y2z1) - (j)(x1z2 - x2z1) + (k)(x1y2 - x2y1)
103
+ // | x2 y2 z2|
104
+ return Vec3.of(this.y * other.z - other.y * this.z, other.x * this.z - this.x * other.z, this.x * other.y - other.x * this.y);
105
+ }
106
+ /**
107
+ * If `other` is a `Vec3`, multiplies `this` component-wise by `other`. Otherwise,
108
+ * if `other is a `number`, returns the result of scalar multiplication.
109
+ *
110
+ * @example
111
+ * ```
112
+ * Vec3.of(1, 2, 3).scale(Vec3.of(2, 4, 6)); // → Vec3(2, 8, 18)
113
+ * ```
114
+ */
115
+ scale(other) {
116
+ if (typeof other === 'number') {
117
+ return this.times(other);
118
+ }
119
+ return Vec3.of(this.x * other.x, this.y * other.y, this.z * other.z);
120
+ }
121
+ /**
122
+ * Returns a vector orthogonal to this. If this is a Vec2, returns `this` rotated
123
+ * 90 degrees counter-clockwise.
124
+ */
125
+ orthog() {
126
+ // If parallel to the z-axis
127
+ if (this.dot(Vec3.unitX) === 0 && this.dot(Vec3.unitY) === 0) {
128
+ return this.dot(Vec3.unitX) === 0 ? Vec3.unitX : this.cross(Vec3.unitX).normalized();
129
+ }
130
+ return this.cross(Vec3.unitZ.times(-1)).normalized();
131
+ }
132
+ /** Returns this plus a vector of length `distance` in `direction`. */
133
+ extend(distance, direction) {
134
+ return this.plus(direction.normalized().times(distance));
135
+ }
136
+ /** Returns a vector `fractionTo` of the way to target from this. */
137
+ lerp(target, fractionTo) {
138
+ return this.times(1 - fractionTo).plus(target.times(fractionTo));
139
+ }
140
+ /**
141
+ * `zip` Maps a component of this and a corresponding component of
142
+ * `other` to a component of the output vector.
143
+ *
144
+ * @example
145
+ * ```
146
+ * const a = Vec3.of(1, 2, 3);
147
+ * const b = Vec3.of(0.5, 2.1, 2.9);
148
+ *
149
+ * const zipped = a.zip(b, (aComponent, bComponent) => {
150
+ * return Math.min(aComponent, bComponent);
151
+ * });
152
+ *
153
+ * console.log(zipped.toString()); // → Vec(0.5, 2, 2.9)
154
+ * ```
155
+ */
156
+ zip(other, zip) {
157
+ return Vec3.of(zip(other.x, this.x), zip(other.y, this.y), zip(other.z, this.z));
158
+ }
159
+ /**
160
+ * Returns a vector with each component acted on by `fn`.
161
+ *
162
+ * @example
163
+ * ```
164
+ * console.log(Vec3.of(1, 2, 3).map(val => val + 1)); // → Vec(2, 3, 4)
165
+ * ```
166
+ */
167
+ map(fn) {
168
+ return Vec3.of(fn(this.x, 0), fn(this.y, 1), fn(this.z, 2));
169
+ }
170
+ asArray() {
171
+ return [this.x, this.y, this.z];
172
+ }
173
+ /**
174
+ * [fuzz] The maximum difference between two components for this and [other]
175
+ * to be considered equal.
176
+ *
177
+ * @example
178
+ * ```
179
+ * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 100); // → true
180
+ * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 0.1); // → false
181
+ * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 3); // → true
182
+ * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 3.01); // → true
183
+ * Vec3.of(1, 2, 3).eq(Vec3.of(4, 5, 6), 2.99); // → false
184
+ * ```
185
+ */
186
+ eq(other, fuzz = 1e-10) {
187
+ for (let i = 0; i < 3; i++) {
188
+ if (Math.abs(other.at(i) - this.at(i)) > fuzz) {
189
+ return false;
190
+ }
191
+ }
192
+ return true;
193
+ }
194
+ toString() {
195
+ return `Vec(${this.x}, ${this.y}, ${this.z})`;
196
+ }
197
+ }
198
+ exports.Vec3 = Vec3;
199
+ Vec3.unitX = Vec3.of(1, 0, 0);
200
+ Vec3.unitY = Vec3.of(0, 1, 0);
201
+ Vec3.unitZ = Vec3.of(0, 0, 1);
202
+ Vec3.zero = Vec3.of(0, 0, 0);
203
+ exports.default = Vec3;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * ```ts,runnable,console
3
+ * import { Vec2, Mat33, Rect2 } from '@js-draw/math';
4
+ *
5
+ * // Example: Rotate a vector 90 degrees about the z-axis
6
+ * const rotate90Degrees = Mat33.zRotation(Math.PI/2); // π/2 radians = 90 deg
7
+ * const moveUp = Mat33.translation(Vec2.of(1, 0));
8
+ * const moveUpThenRotate = rotate90Degrees.rightMul(moveUp);
9
+ * console.log(moveUpThenRotate.transformVec2(Vec2.of(1, 2)));
10
+ *
11
+ * // Example: Bounding box of some points
12
+ * console.log(Rect2.bboxOf([
13
+ * Vec2.of(1, 2), Vec2.of(3, 4), Vec2.of(-100, 1000),
14
+ * ]));
15
+ * ```
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+ export { LineSegment2 } from './shapes/LineSegment2';
20
+ export { Path, PathCommandType, PathCommand, LinePathCommand, MoveToPathCommand, QuadraticBezierPathCommand, CubicBezierPathCommand, } from './shapes/Path';
21
+ export { Rect2 } from './shapes/Rect2';
22
+ export { QuadraticBezier } from './shapes/QuadraticBezier';
23
+ export { Mat33, Mat33Array } from './Mat33';
24
+ export { Point2, Vec2 } from './Vec2';
25
+ export { Vec3 } from './Vec3';
26
+ export { Color4 } from './Color4';
27
+ export { toRoundedString } from './rounding';
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /**
3
+ * ```ts,runnable,console
4
+ * import { Vec2, Mat33, Rect2 } from '@js-draw/math';
5
+ *
6
+ * // Example: Rotate a vector 90 degrees about the z-axis
7
+ * const rotate90Degrees = Mat33.zRotation(Math.PI/2); // π/2 radians = 90 deg
8
+ * const moveUp = Mat33.translation(Vec2.of(1, 0));
9
+ * const moveUpThenRotate = rotate90Degrees.rightMul(moveUp);
10
+ * console.log(moveUpThenRotate.transformVec2(Vec2.of(1, 2)));
11
+ *
12
+ * // Example: Bounding box of some points
13
+ * console.log(Rect2.bboxOf([
14
+ * Vec2.of(1, 2), Vec2.of(3, 4), Vec2.of(-100, 1000),
15
+ * ]));
16
+ * ```
17
+ *
18
+ * @packageDocumentation
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.toRoundedString = exports.Color4 = exports.Vec3 = exports.Vec2 = exports.Mat33 = exports.QuadraticBezier = exports.Rect2 = exports.PathCommandType = exports.Path = exports.LineSegment2 = void 0;
22
+ var LineSegment2_1 = require("./shapes/LineSegment2");
23
+ Object.defineProperty(exports, "LineSegment2", { enumerable: true, get: function () { return LineSegment2_1.LineSegment2; } });
24
+ var Path_1 = require("./shapes/Path");
25
+ Object.defineProperty(exports, "Path", { enumerable: true, get: function () { return Path_1.Path; } });
26
+ Object.defineProperty(exports, "PathCommandType", { enumerable: true, get: function () { return Path_1.PathCommandType; } });
27
+ var Rect2_1 = require("./shapes/Rect2");
28
+ Object.defineProperty(exports, "Rect2", { enumerable: true, get: function () { return Rect2_1.Rect2; } });
29
+ var QuadraticBezier_1 = require("./shapes/QuadraticBezier");
30
+ Object.defineProperty(exports, "QuadraticBezier", { enumerable: true, get: function () { return QuadraticBezier_1.QuadraticBezier; } });
31
+ var Mat33_1 = require("./Mat33");
32
+ Object.defineProperty(exports, "Mat33", { enumerable: true, get: function () { return Mat33_1.Mat33; } });
33
+ var Vec2_1 = require("./Vec2");
34
+ Object.defineProperty(exports, "Vec2", { enumerable: true, get: function () { return Vec2_1.Vec2; } });
35
+ var Vec3_1 = require("./Vec3");
36
+ Object.defineProperty(exports, "Vec3", { enumerable: true, get: function () { return Vec3_1.Vec3; } });
37
+ var Color4_1 = require("./Color4");
38
+ Object.defineProperty(exports, "Color4", { enumerable: true, get: function () { return Color4_1.Color4; } });
39
+ var rounding_1 = require("./rounding");
40
+ Object.defineProperty(exports, "toRoundedString", { enumerable: true, get: function () { return rounding_1.toRoundedString; } });
41
+ // Note: All above exports cannot use `export { default as ... } from "..."` because this
42
+ // breaks TypeDoc -- TypeDoc otherwise labels any imports of these classes as `default`.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Solves an equation of the form ax² + bx + c = 0.
3
+ * The larger solution is returned first.
4
+ *
5
+ * If there are no solutions, returns `[NaN, NaN]`. If there is one solution,
6
+ * repeats the solution twice in the result.
7
+ */
8
+ declare const solveQuadratic: (a: number, b: number, c: number) => [number, number];
9
+ export default solveQuadratic;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Solves an equation of the form ax² + bx + c = 0.
5
+ * The larger solution is returned first.
6
+ *
7
+ * If there are no solutions, returns `[NaN, NaN]`. If there is one solution,
8
+ * repeats the solution twice in the result.
9
+ */
10
+ const solveQuadratic = (a, b, c) => {
11
+ // See also https://en.wikipedia.org/wiki/Quadratic_formula
12
+ if (a === 0) {
13
+ let solution;
14
+ if (b === 0) {
15
+ solution = c === 0 ? 0 : NaN;
16
+ }
17
+ else {
18
+ // Then we have bx + c = 0
19
+ // which implies bx = -c.
20
+ // Thus, x = -c/b
21
+ solution = -c / b;
22
+ }
23
+ return [solution, solution];
24
+ }
25
+ const discriminant = b * b - 4 * a * c;
26
+ if (discriminant < 0) {
27
+ return [NaN, NaN];
28
+ }
29
+ const rootDiscriminant = Math.sqrt(discriminant);
30
+ const solution1 = (-b + rootDiscriminant) / (2 * a);
31
+ const solution2 = (-b - rootDiscriminant) / (2 * a);
32
+ if (solution1 > solution2) {
33
+ return [solution1, solution2];
34
+ }
35
+ else {
36
+ return [solution2, solution1];
37
+ }
38
+ };
39
+ exports.default = solveQuadratic;
@@ -0,0 +1,15 @@
1
+ export declare const cleanUpNumber: (text: string) => string;
2
+ /**
3
+ * Converts `num` to a string, removing trailing digits that were likely caused by
4
+ * precision errors.
5
+ *
6
+ * @example
7
+ * ```ts,runnable,console
8
+ * import { toRoundedString } from '@js-draw/math';
9
+ *
10
+ * console.log('Rounded: ', toRoundedString(1.000000011));
11
+ * ```
12
+ */
13
+ export declare const toRoundedString: (num: number) => string;
14
+ export declare const getLenAfterDecimal: (numberAsString: string) => number;
15
+ export declare const toStringOfSamePrecision: (num: number, ...references: string[]) => string;
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ // @packageDocumentation @internal
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.toStringOfSamePrecision = exports.getLenAfterDecimal = exports.toRoundedString = exports.cleanUpNumber = void 0;
5
+ // Clean up stringified numbers
6
+ const cleanUpNumber = (text) => {
7
+ // Regular expression substitions can be somewhat expensive. Only do them
8
+ // if necessary.
9
+ if (text.indexOf('e') > 0) {
10
+ // Round to zero.
11
+ if (text.match(/[eE][-]\d{2,}$/)) {
12
+ return '0';
13
+ }
14
+ }
15
+ const lastChar = text.charAt(text.length - 1);
16
+ if (lastChar === '0' || lastChar === '.') {
17
+ // Remove trailing zeroes
18
+ text = text.replace(/([.]\d*[^0]+)0+$/, '$1');
19
+ text = text.replace(/[.]0+$/, '.');
20
+ // Remove trailing period
21
+ text = text.replace(/[.]$/, '');
22
+ }
23
+ const firstChar = text.charAt(0);
24
+ if (firstChar === '0' || firstChar === '-') {
25
+ // Remove unnecessary leading zeroes.
26
+ text = text.replace(/^(0+)[.]/, '.');
27
+ text = text.replace(/^-(0+)[.]/, '-.');
28
+ text = text.replace(/^(-?)0+$/, '$10');
29
+ }
30
+ if (text === '-0') {
31
+ return '0';
32
+ }
33
+ return text;
34
+ };
35
+ exports.cleanUpNumber = cleanUpNumber;
36
+ /**
37
+ * Converts `num` to a string, removing trailing digits that were likely caused by
38
+ * precision errors.
39
+ *
40
+ * @example
41
+ * ```ts,runnable,console
42
+ * import { toRoundedString } from '@js-draw/math';
43
+ *
44
+ * console.log('Rounded: ', toRoundedString(1.000000011));
45
+ * ```
46
+ */
47
+ const toRoundedString = (num) => {
48
+ // Try to remove rounding errors. If the number ends in at least three/four zeroes
49
+ // (or nines) just one or two digits, it's probably a rounding error.
50
+ const fixRoundingUpExp = /^([-]?\d*\.\d{3,})0{4,}\d{1,4}$/;
51
+ const hasRoundingDownExp = /^([-]?)(\d*)\.(\d{3,}9{4,})\d{1,4}$/;
52
+ let text = num.toString(10);
53
+ if (text.indexOf('.') === -1) {
54
+ return text;
55
+ }
56
+ const roundingDownMatch = hasRoundingDownExp.exec(text);
57
+ if (roundingDownMatch) {
58
+ const negativeSign = roundingDownMatch[1];
59
+ const postDecimalString = roundingDownMatch[3];
60
+ const lastDigit = parseInt(postDecimalString.charAt(postDecimalString.length - 1), 10);
61
+ const postDecimal = parseInt(postDecimalString, 10);
62
+ const preDecimal = parseInt(roundingDownMatch[2], 10);
63
+ const origPostDecimalString = roundingDownMatch[3];
64
+ let newPostDecimal = (postDecimal + 10 - lastDigit).toString();
65
+ let carry = 0;
66
+ if (newPostDecimal.length > postDecimal.toString().length) {
67
+ // Left-shift
68
+ newPostDecimal = newPostDecimal.substring(1);
69
+ carry = 1;
70
+ }
71
+ // parseInt(...).toString() removes leading zeroes. Add them back.
72
+ while (newPostDecimal.length < origPostDecimalString.length) {
73
+ newPostDecimal = carry.toString(10) + newPostDecimal;
74
+ carry = 0;
75
+ }
76
+ text = `${negativeSign + (preDecimal + carry).toString()}.${newPostDecimal}`;
77
+ }
78
+ text = text.replace(fixRoundingUpExp, '$1');
79
+ return (0, exports.cleanUpNumber)(text);
80
+ };
81
+ exports.toRoundedString = toRoundedString;
82
+ const numberExp = /^([-]?)(\d*)[.](\d+)$/;
83
+ const getLenAfterDecimal = (numberAsString) => {
84
+ const numberMatch = numberExp.exec(numberAsString);
85
+ if (!numberMatch) {
86
+ // If not a match, either the number is exponential notation (or is something
87
+ // like NaN or Infinity)
88
+ if (numberAsString.search(/[eE]/) !== -1 || /^[a-zA-Z]+$/.exec(numberAsString)) {
89
+ return -1;
90
+ // Or it has no decimal point
91
+ }
92
+ else {
93
+ return 0;
94
+ }
95
+ }
96
+ const afterDecimalLen = numberMatch[3].length;
97
+ return afterDecimalLen;
98
+ };
99
+ exports.getLenAfterDecimal = getLenAfterDecimal;
100
+ // [reference] should be a string representation of a base-10 number (no exponential (e.g. 10e10))
101
+ const toStringOfSamePrecision = (num, ...references) => {
102
+ const text = num.toString(10);
103
+ const textMatch = numberExp.exec(text);
104
+ if (!textMatch) {
105
+ return text;
106
+ }
107
+ let decimalPlaces = -1;
108
+ for (const reference of references) {
109
+ decimalPlaces = Math.max((0, exports.getLenAfterDecimal)(reference), decimalPlaces);
110
+ }
111
+ if (decimalPlaces === -1) {
112
+ return (0, exports.toRoundedString)(num);
113
+ }
114
+ // Make text's after decimal length match [afterDecimalLen].
115
+ let postDecimal = textMatch[3].substring(0, decimalPlaces);
116
+ let preDecimal = textMatch[2];
117
+ const nextDigit = textMatch[3].charAt(decimalPlaces);
118
+ if (nextDigit !== '') {
119
+ const asNumber = parseInt(nextDigit, 10);
120
+ if (asNumber >= 5) {
121
+ // Don't attempt to parseInt() an empty string.
122
+ if (postDecimal.length > 0) {
123
+ const leadingZeroMatch = /^(0+)(\d*)$/.exec(postDecimal);
124
+ let leadingZeroes = '';
125
+ let postLeading = postDecimal;
126
+ if (leadingZeroMatch) {
127
+ leadingZeroes = leadingZeroMatch[1];
128
+ postLeading = leadingZeroMatch[2];
129
+ }
130
+ postDecimal = (parseInt(postDecimal) + 1).toString();
131
+ // If postDecimal got longer, remove leading zeroes if possible
132
+ if (postDecimal.length > postLeading.length && leadingZeroes.length > 0) {
133
+ leadingZeroes = leadingZeroes.substring(1);
134
+ }
135
+ postDecimal = leadingZeroes + postDecimal;
136
+ }
137
+ if (postDecimal.length === 0 || postDecimal.length > decimalPlaces) {
138
+ preDecimal = (parseInt(preDecimal) + 1).toString();
139
+ postDecimal = postDecimal.substring(1);
140
+ }
141
+ }
142
+ }
143
+ const negativeSign = textMatch[1];
144
+ return (0, exports.cleanUpNumber)(`${negativeSign}${preDecimal}.${postDecimal}`);
145
+ };
146
+ exports.toStringOfSamePrecision = toStringOfSamePrecision;
@@ -0,0 +1,49 @@
1
+ import LineSegment2 from './LineSegment2';
2
+ import { Point2 } from '../Vec2';
3
+ import Rect2 from './Rect2';
4
+ declare abstract class Abstract2DShape {
5
+ protected static readonly smallValue = 1e-12;
6
+ /**
7
+ * @returns the distance from `point` to this shape. If `point` is within this shape,
8
+ * this returns the distance from `point` to the edge of this shape.
9
+ *
10
+ * @see {@link signedDistance}
11
+ */
12
+ distance(point: Point2): number;
13
+ /**
14
+ * Computes the [signed distance function](https://en.wikipedia.org/wiki/Signed_distance_function)
15
+ * for this shape.
16
+ */
17
+ abstract signedDistance(point: Point2): number;
18
+ /**
19
+ * @returns points at which this shape intersects the given `lineSegment`.
20
+ *
21
+ * If this is a closed shape, returns points where the given `lineSegment` intersects
22
+ * the **boundary** of this.
23
+ */
24
+ abstract intersectsLineSegment(lineSegment: LineSegment2): Point2[];
25
+ /**
26
+ * Returns `true` if and only if the given `point` is contained within this shape.
27
+ *
28
+ * `epsilon` is a small number used to counteract floating point error. Thus, if
29
+ * `point` is within `epsilon` of the inside of this shape, `containsPoint` may also
30
+ * return `true`.
31
+ *
32
+ * The default implementation relies on `signedDistance`.
33
+ * Subclasses may override this method to provide a more efficient implementation.
34
+ */
35
+ containsPoint(point: Point2, epsilon?: number): boolean;
36
+ /**
37
+ * Returns a bounding box that precisely fits the content of this shape.
38
+ */
39
+ abstract getTightBoundingBox(): Rect2;
40
+ /**
41
+ * Returns a bounding box that **loosely** fits the content of this shape.
42
+ *
43
+ * The result of this call can be larger than the result of {@link getTightBoundingBox},
44
+ * **but should not be smaller**. Thus, a call to `getLooseBoundingBox` can be significantly
45
+ * faster than a call to {@link getTightBoundingBox} for some shapes.
46
+ */
47
+ getLooseBoundingBox(): Rect2;
48
+ }
49
+ export default Abstract2DShape;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class Abstract2DShape {
4
+ /**
5
+ * @returns the distance from `point` to this shape. If `point` is within this shape,
6
+ * this returns the distance from `point` to the edge of this shape.
7
+ *
8
+ * @see {@link signedDistance}
9
+ */
10
+ distance(point) {
11
+ return Math.abs(this.signedDistance(point));
12
+ }
13
+ /**
14
+ * Returns `true` if and only if the given `point` is contained within this shape.
15
+ *
16
+ * `epsilon` is a small number used to counteract floating point error. Thus, if
17
+ * `point` is within `epsilon` of the inside of this shape, `containsPoint` may also
18
+ * return `true`.
19
+ *
20
+ * The default implementation relies on `signedDistance`.
21
+ * Subclasses may override this method to provide a more efficient implementation.
22
+ */
23
+ containsPoint(point, epsilon = Abstract2DShape.smallValue) {
24
+ return this.signedDistance(point) < epsilon;
25
+ }
26
+ /**
27
+ * Returns a bounding box that **loosely** fits the content of this shape.
28
+ *
29
+ * The result of this call can be larger than the result of {@link getTightBoundingBox},
30
+ * **but should not be smaller**. Thus, a call to `getLooseBoundingBox` can be significantly
31
+ * faster than a call to {@link getTightBoundingBox} for some shapes.
32
+ */
33
+ getLooseBoundingBox() {
34
+ return this.getTightBoundingBox();
35
+ }
36
+ }
37
+ Abstract2DShape.smallValue = 1e-12;
38
+ exports.default = Abstract2DShape;
@@ -0,0 +1,36 @@
1
+ import { Bezier } from 'bezier-js';
2
+ import { Point2, Vec2 } from '../Vec2';
3
+ import Abstract2DShape from './Abstract2DShape';
4
+ import LineSegment2 from './LineSegment2';
5
+ import Rect2 from './Rect2';
6
+ /**
7
+ * A lazy-initializing wrapper around Bezier-js.
8
+ *
9
+ * Subclasses may override `at`, `derivativeAt`, and `normal` with functions
10
+ * that do not initialize a `bezier-js` `Bezier`.
11
+ *
12
+ * Do not use this class directly. It may be removed/replaced in a future release.
13
+ * @internal
14
+ */
15
+ declare abstract class BezierJSWrapper extends Abstract2DShape {
16
+ #private;
17
+ /** Returns the start, control points, and end point of this Bézier. */
18
+ abstract getPoints(): Point2[];
19
+ protected getBezier(): Bezier;
20
+ signedDistance(point: Point2): number;
21
+ /**
22
+ * @returns the (more) exact distance from `point` to this.
23
+ *
24
+ * @see {@link approximateDistance}
25
+ */
26
+ distance(point: Point2): number;
27
+ /**
28
+ * @returns the curve evaluated at `t`.
29
+ */
30
+ at(t: number): Point2;
31
+ derivativeAt(t: number): Point2;
32
+ normal(t: number): Vec2;
33
+ getTightBoundingBox(): Rect2;
34
+ intersectsLineSegment(line: LineSegment2): Point2[];
35
+ }
36
+ export default BezierJSWrapper;