@js-draw/math 1.21.3 → 1.22.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.
- package/build-config.json +1 -1
- package/dist/cjs/Color4.js +2 -2
- package/dist/cjs/Mat33.d.ts +1 -11
- package/dist/cjs/Mat33.js +8 -24
- package/dist/cjs/Vec3.js +9 -7
- package/dist/cjs/shapes/BezierJSWrapper.js +20 -13
- package/dist/cjs/shapes/LineSegment2.js +13 -17
- package/dist/cjs/shapes/Parameterized2DShape.js +1 -1
- package/dist/cjs/shapes/Path.js +49 -47
- package/dist/cjs/shapes/Rect2.js +13 -15
- package/dist/cjs/shapes/Triangle.js +4 -5
- package/dist/cjs/utils/convexHull2Of.js +3 -3
- package/dist/mjs/Color4.mjs +2 -2
- package/dist/mjs/Mat33.d.ts +1 -11
- package/dist/mjs/Mat33.mjs +8 -24
- package/dist/mjs/Vec3.mjs +9 -7
- package/dist/mjs/shapes/BezierJSWrapper.mjs +20 -13
- package/dist/mjs/shapes/LineSegment2.mjs +13 -17
- package/dist/mjs/shapes/Parameterized2DShape.mjs +1 -1
- package/dist/mjs/shapes/Path.mjs +49 -47
- package/dist/mjs/shapes/Rect2.mjs +13 -15
- package/dist/mjs/shapes/Triangle.mjs +4 -5
- package/dist/mjs/utils/convexHull2Of.mjs +3 -3
- package/dist-test/test_imports/test-require.cjs +1 -1
- package/package.json +3 -3
- package/src/Color4.test.ts +16 -21
- package/src/Color4.ts +22 -17
- package/src/Mat33.fromCSSMatrix.test.ts +31 -45
- package/src/Mat33.test.ts +58 -96
- package/src/Mat33.ts +61 -104
- package/src/Vec2.test.ts +3 -3
- package/src/Vec3.test.ts +2 -3
- package/src/Vec3.ts +34 -58
- package/src/lib.ts +0 -2
- package/src/polynomial/solveQuadratic.test.ts +39 -13
- package/src/polynomial/solveQuadratic.ts +5 -6
- package/src/rounding/cleanUpNumber.test.ts +1 -1
- package/src/rounding/constants.ts +1 -3
- package/src/rounding/getLenAfterDecimal.ts +1 -2
- package/src/rounding/lib.ts +1 -2
- package/src/rounding/toRoundedString.test.ts +1 -1
- package/src/rounding/toStringOfSamePrecision.test.ts +1 -2
- package/src/rounding/toStringOfSamePrecision.ts +1 -1
- package/src/shapes/BezierJSWrapper.ts +54 -37
- package/src/shapes/CubicBezier.ts +3 -3
- package/src/shapes/LineSegment2.test.ts +24 -17
- package/src/shapes/LineSegment2.ts +26 -29
- package/src/shapes/Parameterized2DShape.ts +5 -4
- package/src/shapes/Path.fromString.test.ts +5 -5
- package/src/shapes/Path.test.ts +122 -120
- package/src/shapes/Path.toString.test.ts +7 -7
- package/src/shapes/Path.ts +378 -352
- package/src/shapes/PointShape2D.ts +3 -3
- package/src/shapes/QuadraticBezier.test.ts +27 -21
- package/src/shapes/QuadraticBezier.ts +4 -9
- package/src/shapes/Rect2.test.ts +44 -75
- package/src/shapes/Rect2.ts +30 -35
- package/src/shapes/Triangle.test.ts +31 -29
- package/src/shapes/Triangle.ts +17 -18
- package/src/utils/convexHull2Of.test.ts +54 -15
- package/src/utils/convexHull2Of.ts +9 -7
- package/tsconfig.json +1 -3
- package/typedoc.json +2 -2
package/build-config.json
CHANGED
package/dist/cjs/Color4.js
CHANGED
@@ -61,7 +61,7 @@ class Color4 {
|
|
61
61
|
// Each character is a component
|
62
62
|
const components = hexString.split('');
|
63
63
|
// Convert to RRGGBBAA or RRGGBB format
|
64
|
-
hexString = components.map(component => `${component}0`).join('');
|
64
|
+
hexString = components.map((component) => `${component}0`).join('');
|
65
65
|
}
|
66
66
|
if (hexString.length === 6) {
|
67
67
|
// Alpha component
|
@@ -171,7 +171,7 @@ class Color4 {
|
|
171
171
|
// - https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
172
172
|
// - https://stackoverflow.com/a/9733420
|
173
173
|
// Normalize the components, as per above
|
174
|
-
const components = [this.r, this.g, this.b].map(component => {
|
174
|
+
const components = [this.r, this.g, this.b].map((component) => {
|
175
175
|
if (component < 0.03928) {
|
176
176
|
return component / 12.92;
|
177
177
|
}
|
package/dist/cjs/Mat33.d.ts
CHANGED
@@ -3,17 +3,7 @@ import Vec3 from './Vec3';
|
|
3
3
|
/**
|
4
4
|
* See {@link Mat33.toArray}.
|
5
5
|
*/
|
6
|
-
export type Mat33Array = [
|
7
|
-
number,
|
8
|
-
number,
|
9
|
-
number,
|
10
|
-
number,
|
11
|
-
number,
|
12
|
-
number,
|
13
|
-
number,
|
14
|
-
number,
|
15
|
-
number
|
16
|
-
];
|
6
|
+
export type Mat33Array = [number, number, number, number, number, number, number, number, number];
|
17
7
|
/**
|
18
8
|
* Represents a three dimensional linear transformation or
|
19
9
|
* a two-dimensional affine transformation. (An affine transformation scales/rotates/shears
|
package/dist/cjs/Mat33.js
CHANGED
@@ -76,11 +76,7 @@ class Mat33 {
|
|
76
76
|
this.c2 = c2;
|
77
77
|
this.c3 = c3;
|
78
78
|
this.cachedInverse = undefined;
|
79
|
-
this.rows = [
|
80
|
-
Vec3_1.default.of(a1, a2, a3),
|
81
|
-
Vec3_1.default.of(b1, b2, b3),
|
82
|
-
Vec3_1.default.of(c1, c2, c3),
|
83
|
-
];
|
79
|
+
this.rows = [Vec3_1.default.of(a1, a2, a3), Vec3_1.default.of(b1, b2, b3), Vec3_1.default.of(c1, c2, c3)];
|
84
80
|
}
|
85
81
|
/**
|
86
82
|
* Creates a matrix from the given rows:
|
@@ -112,16 +108,8 @@ class Mat33 {
|
|
112
108
|
if (this.cachedInverse !== undefined) {
|
113
109
|
return this.cachedInverse;
|
114
110
|
}
|
115
|
-
const toIdentity = [
|
116
|
-
|
117
|
-
this.rows[1],
|
118
|
-
this.rows[2],
|
119
|
-
];
|
120
|
-
const toResult = [
|
121
|
-
Vec3_1.default.unitX,
|
122
|
-
Vec3_1.default.unitY,
|
123
|
-
Vec3_1.default.unitZ,
|
124
|
-
];
|
111
|
+
const toIdentity = [this.rows[0], this.rows[1], this.rows[2]];
|
112
|
+
const toResult = [Vec3_1.default.unitX, Vec3_1.default.unitY, Vec3_1.default.unitZ];
|
125
113
|
// Convert toIdentity to the identity matrix and
|
126
114
|
// toResult to the inverse through elementary row operations
|
127
115
|
for (let cursor = 0; cursor < 3; cursor++) {
|
@@ -317,11 +305,7 @@ class Mat33 {
|
|
317
305
|
* ```
|
318
306
|
*/
|
319
307
|
toArray() {
|
320
|
-
return [
|
321
|
-
this.a1, this.a2, this.a3,
|
322
|
-
this.b1, this.b2, this.b3,
|
323
|
-
this.c1, this.c2, this.c3,
|
324
|
-
];
|
308
|
+
return [this.a1, this.a2, this.a3, this.b1, this.b2, this.b3, this.c1, this.c2, this.c3];
|
325
309
|
}
|
326
310
|
/**
|
327
311
|
* Returns a new `Mat33` where each entry is the output of the function
|
@@ -427,7 +411,7 @@ class Mat33 {
|
|
427
411
|
return Mat33.identity;
|
428
412
|
}
|
429
413
|
const parseArguments = (argumentString) => {
|
430
|
-
const parsed = argumentString.split(/[, \t\n]+/g).map(argString => {
|
414
|
+
const parsed = argumentString.split(/[, \t\n]+/g).map((argString) => {
|
431
415
|
// Handle trailing spaces/commands
|
432
416
|
if (argString.trim() === '') {
|
433
417
|
return null;
|
@@ -438,7 +422,7 @@ class Mat33 {
|
|
438
422
|
argString = argString.substring(0, argString.length - 1);
|
439
423
|
}
|
440
424
|
// Remove trailing px units.
|
441
|
-
argString = argString.replace(/px$/
|
425
|
+
argString = argString.replace(/px$/gi, '');
|
442
426
|
const numberExp = /^[-]?\d*(?:\.\d*)?(?:[eE][-+]?\d+)?$/i;
|
443
427
|
if (!numberExp.exec(argString)) {
|
444
428
|
throw new Error(`All arguments to transform functions must be numeric (state: ${JSON.stringify({
|
@@ -452,7 +436,7 @@ class Mat33 {
|
|
452
436
|
}
|
453
437
|
return argNumber;
|
454
438
|
});
|
455
|
-
return parsed.filter(n => n !== null);
|
439
|
+
return parsed.filter((n) => n !== null);
|
456
440
|
};
|
457
441
|
const keywordToAction = {
|
458
442
|
matrix: (matrixData) => {
|
@@ -502,7 +486,7 @@ class Mat33 {
|
|
502
486
|
};
|
503
487
|
// A command (\w+)
|
504
488
|
// followed by a set of arguments ([ \t\n0-9eE.,\-%]+)
|
505
|
-
const partRegex = /\s*(\w+)\s*\(([^)]*)\)/
|
489
|
+
const partRegex = /\s*(\w+)\s*\(([^)]*)\)/gi;
|
506
490
|
let match;
|
507
491
|
let matrix = null;
|
508
492
|
while ((match = partRegex.exec(cssString)) !== null) {
|
package/dist/cjs/Vec3.js
CHANGED
@@ -106,9 +106,9 @@ class Vec3Impl {
|
|
106
106
|
return [this.x, this.y, this.z];
|
107
107
|
}
|
108
108
|
eq(other, fuzz = defaultEqlTolerance) {
|
109
|
-
return (Math.abs(other.x - this.x) <= fuzz
|
110
|
-
|
111
|
-
|
109
|
+
return (Math.abs(other.x - this.x) <= fuzz &&
|
110
|
+
Math.abs(other.y - this.y) <= fuzz &&
|
111
|
+
Math.abs(other.z - this.z) <= fuzz);
|
112
112
|
}
|
113
113
|
toString() {
|
114
114
|
return `Vec(${this.x}, ${this.y}, ${this.z})`;
|
@@ -119,7 +119,9 @@ class Vec2Impl {
|
|
119
119
|
this.x = x;
|
120
120
|
this.y = y;
|
121
121
|
}
|
122
|
-
get z() {
|
122
|
+
get z() {
|
123
|
+
return 0;
|
124
|
+
}
|
123
125
|
get xy() {
|
124
126
|
// Useful for APIs that behave differently if .z is present.
|
125
127
|
return {
|
@@ -216,9 +218,9 @@ class Vec2Impl {
|
|
216
218
|
return [this.x, this.y, 0];
|
217
219
|
}
|
218
220
|
eq(other, fuzz = defaultEqlTolerance) {
|
219
|
-
return (Math.abs(other.x - this.x) <= fuzz
|
220
|
-
|
221
|
-
|
221
|
+
return (Math.abs(other.x - this.x) <= fuzz &&
|
222
|
+
Math.abs(other.y - this.y) <= fuzz &&
|
223
|
+
Math.abs(other.z) <= fuzz);
|
222
224
|
}
|
223
225
|
toString() {
|
224
226
|
return `Vec(${this.x}, ${this.y})`;
|
@@ -40,7 +40,7 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
40
40
|
}
|
41
41
|
getBezier() {
|
42
42
|
if (!__classPrivateFieldGet(this, _BezierJSWrapper_bezierJs, "f")) {
|
43
|
-
__classPrivateFieldSet(this, _BezierJSWrapper_bezierJs, new bezier_js_1.Bezier(this.getPoints().map(p => p.xy)), "f");
|
43
|
+
__classPrivateFieldSet(this, _BezierJSWrapper_bezierJs, new bezier_js_1.Bezier(this.getPoints().map((p) => p.xy)), "f");
|
44
44
|
}
|
45
45
|
return __classPrivateFieldGet(this, _BezierJSWrapper_bezierJs, "f");
|
46
46
|
}
|
@@ -94,10 +94,12 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
94
94
|
const asLine = LineSegment2_1.default.ofSmallestContainingPoints(this.getPoints());
|
95
95
|
if (asLine) {
|
96
96
|
const intersection = asLine.intersectsLineSegment(line);
|
97
|
-
return intersection.map(p => this.nearestPointTo(p).parameterValue);
|
97
|
+
return intersection.map((p) => this.nearestPointTo(p).parameterValue);
|
98
98
|
}
|
99
99
|
const bezier = this.getBezier();
|
100
|
-
return bezier
|
100
|
+
return bezier
|
101
|
+
.intersects(line)
|
102
|
+
.map((t) => {
|
101
103
|
// We're using the .intersects(line) function, which is documented
|
102
104
|
// to always return numbers. However, to satisfy the type checker (and
|
103
105
|
// possibly improperly-defined types),
|
@@ -106,12 +108,12 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
106
108
|
}
|
107
109
|
const point = Vec2_1.Vec2.ofXY(this.at(t));
|
108
110
|
// Ensure that the intersection is on the line segment
|
109
|
-
if (point.distanceTo(line.p1) > line.length
|
110
|
-
|| point.distanceTo(line.p2) > line.length) {
|
111
|
+
if (point.distanceTo(line.p1) > line.length || point.distanceTo(line.p2) > line.length) {
|
111
112
|
return null;
|
112
113
|
}
|
113
114
|
return t;
|
114
|
-
})
|
115
|
+
})
|
116
|
+
.filter((entry) => entry !== null);
|
115
117
|
}
|
116
118
|
splitAt(t) {
|
117
119
|
if (t <= 0 || t >= 1) {
|
@@ -120,8 +122,8 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
120
122
|
const bezier = this.getBezier();
|
121
123
|
const split = bezier.split(t);
|
122
124
|
return [
|
123
|
-
new BezierJSWrapperImpl(split.left.points.map(point => Vec2_1.Vec2.ofXY(point)), split.left),
|
124
|
-
new BezierJSWrapperImpl(split.right.points.map(point => Vec2_1.Vec2.ofXY(point)), split.right),
|
125
|
+
new BezierJSWrapperImpl(split.left.points.map((point) => Vec2_1.Vec2.ofXY(point)), split.left),
|
126
|
+
new BezierJSWrapperImpl(split.right.points.map((point) => Vec2_1.Vec2.ofXY(point)), split.right),
|
125
127
|
];
|
126
128
|
}
|
127
129
|
nearestPointTo(point) {
|
@@ -165,16 +167,19 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
165
167
|
const b = this.at(t);
|
166
168
|
const bPrime = this.derivativeAt(t);
|
167
169
|
const bPrimePrime = this.secondDerivativeAt(t);
|
168
|
-
return (2 * bPrime.x * bPrime.x +
|
169
|
-
|
170
|
+
return (2 * bPrime.x * bPrime.x +
|
171
|
+
2 * b.x * bPrimePrime.x -
|
172
|
+
2 * point.x * bPrimePrime.x +
|
173
|
+
2 * bPrime.y * bPrime.y +
|
174
|
+
2 * b.y * bPrimePrime.y -
|
175
|
+
2 * point.y * bPrimePrime.y);
|
170
176
|
};
|
171
177
|
// Because we're zeroing f'(t), we also need to be able to compute it:
|
172
178
|
const derivativeAt = (t) => {
|
173
179
|
// f'(t) = 2Bₓ(t)Bₓ'(t) - 2pₓBₓ'(t) + 2Bᵧ(t)Bᵧ'(t) - 2pᵧBᵧ'(t)
|
174
180
|
const b = this.at(t);
|
175
181
|
const bPrime = this.derivativeAt(t);
|
176
|
-
return (2 * b.x * bPrime.x - 2 * point.x * bPrime.x
|
177
|
-
+ 2 * b.y * bPrime.y - 2 * point.y * bPrime.y);
|
182
|
+
return (2 * b.x * bPrime.x - 2 * point.x * bPrime.x + 2 * b.y * bPrime.y - 2 * point.y * bPrime.y);
|
178
183
|
};
|
179
184
|
const iterate = () => {
|
180
185
|
const slope = secondDerivativeAt(t);
|
@@ -225,7 +230,9 @@ class BezierJSWrapper extends Parameterized2DShape_1.default {
|
|
225
230
|
return result;
|
226
231
|
}
|
227
232
|
toString() {
|
228
|
-
return `Bézier(${this.getPoints()
|
233
|
+
return `Bézier(${this.getPoints()
|
234
|
+
.map((point) => point.toString())
|
235
|
+
.join(', ')})`;
|
229
236
|
}
|
230
237
|
}
|
231
238
|
exports.BezierJSWrapper = BezierJSWrapper;
|
@@ -46,7 +46,7 @@ class LineSegment2 extends Parameterized2DShape_1.default {
|
|
46
46
|
static ofSmallestContainingPoints(points) {
|
47
47
|
if (points.length <= 1)
|
48
48
|
return null;
|
49
|
-
const sorted = [...points].sort((a, b) => a.x !== b.x ? a.x - b.x : a.y - b.y);
|
49
|
+
const sorted = [...points].sort((a, b) => (a.x !== b.x ? a.x - b.x : a.y - b.y));
|
50
50
|
const line = new LineSegment2(sorted[0], sorted[sorted.length - 1]);
|
51
51
|
for (const point of sorted) {
|
52
52
|
if (!line.containsPoint(point)) {
|
@@ -96,10 +96,7 @@ class LineSegment2 extends Parameterized2DShape_1.default {
|
|
96
96
|
if (t <= 0 || t >= 1) {
|
97
97
|
return [this];
|
98
98
|
}
|
99
|
-
return [
|
100
|
-
new LineSegment2(this.point1, this.at(t)),
|
101
|
-
new LineSegment2(this.at(t), this.point2),
|
102
|
-
];
|
99
|
+
return [new LineSegment2(this.point1, this.at(t)), new LineSegment2(this.at(t), this.point2)];
|
103
100
|
}
|
104
101
|
/**
|
105
102
|
* Returns the intersection of this with another line segment.
|
@@ -149,18 +146,17 @@ class LineSegment2 extends Parameterized2DShape_1.default {
|
|
149
146
|
return null;
|
150
147
|
}
|
151
148
|
const xIntersect = this.point1.x;
|
152
|
-
const yIntersect = (this.point1.x - other.point1.x) * other.direction.y / other.direction.x + other.point1.y;
|
149
|
+
const yIntersect = ((this.point1.x - other.point1.x) * other.direction.y) / other.direction.x + other.point1.y;
|
153
150
|
resultPoint = Vec2_1.Vec2.of(xIntersect, yIntersect);
|
154
151
|
resultT = (yIntersect - this.point1.y) / this.direction.y;
|
155
152
|
}
|
156
153
|
else {
|
157
154
|
// From above,
|
158
155
|
// x = ((o₁ᵧ - o₂ᵧ)(d₁ₓd₂ₓ) + (d₂ᵧd₁ₓ)(o₂ₓ) - (d₁ᵧd₂ₓ)(o₁ₓ))/(d₂ᵧd₁ₓ - d₁ᵧd₂ₓ)
|
159
|
-
const numerator = (
|
160
|
-
|
161
|
-
|
162
|
-
const denominator =
|
163
|
-
- this.direction.y * other.direction.x);
|
156
|
+
const numerator = (this.point1.y - other.point1.y) * this.direction.x * other.direction.x +
|
157
|
+
this.direction.x * other.direction.y * other.point1.x -
|
158
|
+
this.direction.y * other.direction.x * this.point1.x;
|
159
|
+
const denominator = other.direction.y * this.direction.x - this.direction.y * other.direction.x;
|
164
160
|
// Avoid dividing by zero. It means there is no intersection
|
165
161
|
if (denominator === 0) {
|
166
162
|
return null;
|
@@ -176,10 +172,10 @@ class LineSegment2 extends Parameterized2DShape_1.default {
|
|
176
172
|
const resultToP2 = resultPoint.distanceTo(this.point2);
|
177
173
|
const resultToP3 = resultPoint.distanceTo(other.point1);
|
178
174
|
const resultToP4 = resultPoint.distanceTo(other.point2);
|
179
|
-
if (resultToP1 > this.length
|
180
|
-
|
181
|
-
|
182
|
-
|
175
|
+
if (resultToP1 > this.length ||
|
176
|
+
resultToP2 > this.length ||
|
177
|
+
resultToP3 > other.length ||
|
178
|
+
resultToP4 > other.length) {
|
183
179
|
return null;
|
184
180
|
}
|
185
181
|
return {
|
@@ -264,8 +260,8 @@ class LineSegment2 extends Parameterized2DShape_1.default {
|
|
264
260
|
}
|
265
261
|
const tolerance = options?.tolerance;
|
266
262
|
const ignoreDirection = options?.ignoreDirection ?? true;
|
267
|
-
return ((other.p1.eq(this.p1, tolerance) && other.p2.eq(this.p2, tolerance))
|
268
|
-
|
263
|
+
return ((other.p1.eq(this.p1, tolerance) && other.p2.eq(this.p2, tolerance)) ||
|
264
|
+
(ignoreDirection && other.p1.eq(this.p2, tolerance) && other.p2.eq(this.p1, tolerance)));
|
269
265
|
}
|
270
266
|
}
|
271
267
|
exports.LineSegment2 = LineSegment2;
|
@@ -13,7 +13,7 @@ const Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
|
|
13
13
|
*/
|
14
14
|
class Parameterized2DShape extends Abstract2DShape_1.default {
|
15
15
|
intersectsLineSegment(line) {
|
16
|
-
return this.argIntersectsLineSegment(line).map(t => this.at(t));
|
16
|
+
return this.argIntersectsLineSegment(line).map((t) => this.at(t));
|
17
17
|
}
|
18
18
|
}
|
19
19
|
exports.Parameterized2DShape = Parameterized2DShape;
|
package/dist/cjs/shapes/Path.js
CHANGED
@@ -327,9 +327,7 @@ class Path {
|
|
327
327
|
const maxRaymarchSteps = 8;
|
328
328
|
// Start raymarching from each of these points. This allows detection of multiple
|
329
329
|
// intersections.
|
330
|
-
const startPoints = [
|
331
|
-
line.p1, ...additionalRaymarchStartPoints, line.p2
|
332
|
-
];
|
330
|
+
const startPoints = [line.p1, ...additionalRaymarchStartPoints, line.p2];
|
333
331
|
// Converts a point ON THE LINE to a parameter
|
334
332
|
const pointToParameter = (point) => {
|
335
333
|
// Because line.direction is a unit vector, this computes the length
|
@@ -435,7 +433,9 @@ class Path {
|
|
435
433
|
return [];
|
436
434
|
}
|
437
435
|
if (this.parts.length === 0) {
|
438
|
-
return new Path(this.startPoint, [
|
436
|
+
return new Path(this.startPoint, [
|
437
|
+
{ kind: PathCommandType.MoveTo, point: this.startPoint },
|
438
|
+
]).intersection(line, strokeRadius);
|
439
439
|
}
|
440
440
|
let index = 0;
|
441
441
|
for (const part of this.geometry) {
|
@@ -456,7 +456,7 @@ class Path {
|
|
456
456
|
const doRaymarching = strokeRadius && strokeRadius > 1e-8;
|
457
457
|
if (doRaymarching) {
|
458
458
|
// Starting points for raymarching (in addition to the end points of the line).
|
459
|
-
const startPoints = result.map(intersection => intersection.point);
|
459
|
+
const startPoints = result.map((intersection) => intersection.point);
|
460
460
|
result = this.raymarchIntersectionWith(line, strokeRadius, startPoints);
|
461
461
|
}
|
462
462
|
return result;
|
@@ -509,7 +509,8 @@ class Path {
|
|
509
509
|
*/
|
510
510
|
spliced(deleteFrom, deleteTo, insert, options) {
|
511
511
|
const isBeforeOrEqual = (a, b) => {
|
512
|
-
return a.curveIndex < b.curveIndex ||
|
512
|
+
return (a.curveIndex < b.curveIndex ||
|
513
|
+
(a.curveIndex === b.curveIndex && a.parameterValue <= b.parameterValue));
|
513
514
|
};
|
514
515
|
if (isBeforeOrEqual(deleteFrom, deleteTo)) {
|
515
516
|
// deleteFrom deleteTo
|
@@ -547,15 +548,15 @@ class Path {
|
|
547
548
|
//
|
548
549
|
// Bounds checking & reversal.
|
549
550
|
//
|
550
|
-
while (splitAt.length > 0
|
551
|
-
|
552
|
-
|
551
|
+
while (splitAt.length > 0 &&
|
552
|
+
splitAt[splitAt.length - 1].curveIndex >= this.parts.length - 1 &&
|
553
|
+
splitAt[splitAt.length - 1].parameterValue >= 1) {
|
553
554
|
splitAt.pop();
|
554
555
|
}
|
555
556
|
splitAt.reverse(); // .reverse() <-- We're `.pop`ing from the end
|
556
|
-
while (splitAt.length > 0
|
557
|
-
|
558
|
-
|
557
|
+
while (splitAt.length > 0 &&
|
558
|
+
splitAt[splitAt.length - 1].curveIndex <= 0 &&
|
559
|
+
splitAt[splitAt.length - 1].parameterValue <= 0) {
|
559
560
|
splitAt.pop();
|
560
561
|
}
|
561
562
|
if (splitAt.length === 0 || this.parts.length === 0) {
|
@@ -746,7 +747,7 @@ class Path {
|
|
746
747
|
if (affineTransfm.isIdentity()) {
|
747
748
|
return this;
|
748
749
|
}
|
749
|
-
return this.mapPoints(point => affineTransfm.transformVec2(point));
|
750
|
+
return this.mapPoints((point) => affineTransfm.transformVec2(point));
|
750
751
|
}
|
751
752
|
/**
|
752
753
|
* @internal
|
@@ -835,11 +836,10 @@ class Path {
|
|
835
836
|
});
|
836
837
|
lastPoint = part.endPoint;
|
837
838
|
break;
|
838
|
-
default:
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
}
|
839
|
+
default: {
|
840
|
+
const exhaustivenessCheck = part;
|
841
|
+
return exhaustivenessCheck;
|
842
|
+
}
|
843
843
|
}
|
844
844
|
}
|
845
845
|
newParts.reverse();
|
@@ -851,7 +851,8 @@ class Path {
|
|
851
851
|
return this.startPoint;
|
852
852
|
}
|
853
853
|
const lastPart = this.parts[this.parts.length - 1];
|
854
|
-
if (lastPart.kind === PathCommandType.QuadraticBezierTo ||
|
854
|
+
if (lastPart.kind === PathCommandType.QuadraticBezierTo ||
|
855
|
+
lastPart.kind === PathCommandType.CubicBezierTo) {
|
855
856
|
return lastPart.endPoint;
|
856
857
|
}
|
857
858
|
else {
|
@@ -960,9 +961,9 @@ class Path {
|
|
960
961
|
if (part1.kind !== part2.kind) {
|
961
962
|
return false;
|
962
963
|
}
|
963
|
-
else if (!part1.controlPoint1.eq(part2.controlPoint1, tolerance)
|
964
|
-
|
965
|
-
|
964
|
+
else if (!part1.controlPoint1.eq(part2.controlPoint1, tolerance) ||
|
965
|
+
!part1.controlPoint2.eq(part2.controlPoint2, tolerance) ||
|
966
|
+
!part1.endPoint.eq(part2.endPoint, tolerance)) {
|
966
967
|
return false;
|
967
968
|
}
|
968
969
|
break;
|
@@ -970,16 +971,15 @@ class Path {
|
|
970
971
|
if (part1.kind !== part2.kind) {
|
971
972
|
return false;
|
972
973
|
}
|
973
|
-
else if (!part1.controlPoint.eq(part2.controlPoint, tolerance)
|
974
|
-
|
974
|
+
else if (!part1.controlPoint.eq(part2.controlPoint, tolerance) ||
|
975
|
+
!part1.endPoint.eq(part2.endPoint, tolerance)) {
|
975
976
|
return false;
|
976
977
|
}
|
977
978
|
break;
|
978
|
-
default:
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
}
|
979
|
+
default: {
|
980
|
+
const exhaustivenessCheck = part1;
|
981
|
+
return exhaustivenessCheck;
|
982
|
+
}
|
983
983
|
}
|
984
984
|
}
|
985
985
|
return true;
|
@@ -1001,11 +1001,7 @@ class Path {
|
|
1001
1001
|
const cornerToEdge = Vec2_1.Vec2.of(lineWidth, lineWidth).times(0.5);
|
1002
1002
|
const innerRect = Rect2_1.default.fromCorners(rect.topLeft.plus(cornerToEdge), rect.bottomRight.minus(cornerToEdge));
|
1003
1003
|
const outerRect = Rect2_1.default.fromCorners(rect.topLeft.minus(cornerToEdge), rect.bottomRight.plus(cornerToEdge));
|
1004
|
-
corners = [
|
1005
|
-
innerRect.corners[3],
|
1006
|
-
...innerRect.corners,
|
1007
|
-
...outerRect.corners.reverse(),
|
1008
|
-
];
|
1004
|
+
corners = [innerRect.corners[3], ...innerRect.corners, ...outerRect.corners.reverse()];
|
1009
1005
|
startPoint = outerRect.corners[3];
|
1010
1006
|
}
|
1011
1007
|
else {
|
@@ -1188,19 +1184,23 @@ class Path {
|
|
1188
1184
|
});
|
1189
1185
|
};
|
1190
1186
|
const commandArgCounts = {
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1187
|
+
m: 1,
|
1188
|
+
l: 1,
|
1189
|
+
c: 3,
|
1190
|
+
q: 2,
|
1191
|
+
z: 0,
|
1192
|
+
h: 1,
|
1193
|
+
v: 1,
|
1198
1194
|
};
|
1199
1195
|
// Each command: Command character followed by anything that isn't a command character
|
1200
|
-
const commandExp = /([MZLHVCSQTA])\s*([^MZLHVCSQTA]*)/
|
1196
|
+
const commandExp = /([MZLHVCSQTA])\s*([^MZLHVCSQTA]*)/gi;
|
1201
1197
|
let current;
|
1202
1198
|
while ((current = commandExp.exec(pathString)) !== null) {
|
1203
|
-
const argParts = current[2]
|
1199
|
+
const argParts = current[2]
|
1200
|
+
.trim()
|
1201
|
+
.split(/[^0-9Ee.-]/)
|
1202
|
+
.filter((part) => part.length > 0)
|
1203
|
+
.reduce((accumualtor, current) => {
|
1204
1204
|
// As of 09/2022, iOS Safari doesn't support support lookbehind in regular
|
1205
1205
|
// expressions. As such, we need an alternative.
|
1206
1206
|
// Because '-' can be used as a path separator, unless preceeded by an 'e' (as in 1e-5),
|
@@ -1210,10 +1210,10 @@ class Path {
|
|
1210
1210
|
if (parts[0] !== '') {
|
1211
1211
|
accumualtor.push(parts[0]);
|
1212
1212
|
}
|
1213
|
-
accumualtor.push(...parts.slice(1).map(part => `-${part}`));
|
1213
|
+
accumualtor.push(...parts.slice(1).map((part) => `-${part}`));
|
1214
1214
|
return accumualtor;
|
1215
1215
|
}, []);
|
1216
|
-
let numericArgs = argParts.map(arg => parseFloat(arg));
|
1216
|
+
let numericArgs = argParts.map((arg) => parseFloat(arg));
|
1217
1217
|
let commandChar = current[1].toLowerCase();
|
1218
1218
|
let uppercaseCommand = current[1] !== commandChar;
|
1219
1219
|
// Convert commands that don't take points into commands that do.
|
@@ -1241,7 +1241,8 @@ class Path {
|
|
1241
1241
|
commandChar = 'l';
|
1242
1242
|
}
|
1243
1243
|
const commandArgCount = commandArgCounts[commandChar] ?? 0;
|
1244
|
-
const allArgs = numericArgs
|
1244
|
+
const allArgs = numericArgs
|
1245
|
+
.reduce((accumulator, current, index, parts) => {
|
1245
1246
|
if (index % 2 !== 0) {
|
1246
1247
|
const currentAsFloat = current;
|
1247
1248
|
const prevAsFloat = parts[index - 1];
|
@@ -1250,7 +1251,8 @@ class Path {
|
|
1250
1251
|
else {
|
1251
1252
|
return accumulator;
|
1252
1253
|
}
|
1253
|
-
}, [])
|
1254
|
+
}, [])
|
1255
|
+
.map((coordinate, index) => {
|
1254
1256
|
// Lowercase commands are relative, uppercase commands use absolute
|
1255
1257
|
// positioning
|
1256
1258
|
let newPos;
|
package/dist/cjs/shapes/Rect2.js
CHANGED
@@ -40,13 +40,16 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
40
40
|
return new Rect2(this.x, this.y, size.x, size.y);
|
41
41
|
}
|
42
42
|
containsPoint(other) {
|
43
|
-
return this.x <= other.x &&
|
44
|
-
|
43
|
+
return (this.x <= other.x &&
|
44
|
+
this.y <= other.y &&
|
45
|
+
this.x + this.w >= other.x &&
|
46
|
+
this.y + this.h >= other.y);
|
45
47
|
}
|
46
48
|
containsRect(other) {
|
47
|
-
return this.x <= other.x &&
|
48
|
-
|
49
|
-
|
49
|
+
return (this.x <= other.x &&
|
50
|
+
this.y <= other.y &&
|
51
|
+
this.x + this.w >= other.x + other.w &&
|
52
|
+
this.y + this.h >= other.y + other.h);
|
50
53
|
}
|
51
54
|
/**
|
52
55
|
* @returns true iff this and `other` overlap
|
@@ -131,7 +134,7 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
131
134
|
return new Rect2(this.x - margin, this.y - margin, this.w + margin * 2, this.h + margin * 2);
|
132
135
|
}
|
133
136
|
getClosestPointOnBoundaryTo(target) {
|
134
|
-
const closestEdgePoints = this.getEdges().map(edge => {
|
137
|
+
const closestEdgePoints = this.getEdges().map((edge) => {
|
135
138
|
return edge.closestPointTo(target);
|
136
139
|
});
|
137
140
|
let closest = null;
|
@@ -158,15 +161,10 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
158
161
|
return false;
|
159
162
|
}
|
160
163
|
const squareRadius = radius * radius;
|
161
|
-
return this.corners.every(corner => corner.minus(point).magnitudeSquared() < squareRadius);
|
164
|
+
return this.corners.every((corner) => corner.minus(point).magnitudeSquared() < squareRadius);
|
162
165
|
}
|
163
166
|
get corners() {
|
164
|
-
return [
|
165
|
-
this.bottomRight,
|
166
|
-
this.topRight,
|
167
|
-
this.topLeft,
|
168
|
-
this.bottomLeft,
|
169
|
-
];
|
167
|
+
return [this.bottomRight, this.topRight, this.topLeft, this.bottomLeft];
|
170
168
|
}
|
171
169
|
get maxDimension() {
|
172
170
|
return Math.max(this.w, this.h);
|
@@ -207,7 +205,7 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
207
205
|
const result = [];
|
208
206
|
for (const edge of this.getEdges()) {
|
209
207
|
const intersection = edge.intersectsLineSegment(lineSegment);
|
210
|
-
intersection.forEach(point => result.push(point));
|
208
|
+
intersection.forEach((point) => result.push(point));
|
211
209
|
}
|
212
210
|
return result;
|
213
211
|
}
|
@@ -225,7 +223,7 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
225
223
|
// [affineTransform] is a transformation matrix that both scales and **translates**.
|
226
224
|
// the bounding box of this' four corners after transformed by the given affine transformation.
|
227
225
|
transformedBoundingBox(affineTransform) {
|
228
|
-
return Rect2.bboxOf(this.corners.map(corner => affineTransform.transformVec2(corner)));
|
226
|
+
return Rect2.bboxOf(this.corners.map((corner) => affineTransform.transformVec2(corner)));
|
229
227
|
}
|
230
228
|
/** @return true iff this is equal to `other ± tolerance` */
|
231
229
|
eq(other, tolerance = 0) {
|
@@ -44,12 +44,12 @@ class Triangle extends Abstract2DShape_1.default {
|
|
44
44
|
}
|
45
45
|
// Transform, treating this as composed of 2D points.
|
46
46
|
transformed2DBy(affineTransform) {
|
47
|
-
return this.map(vertex => affineTransform.transformVec2(vertex));
|
47
|
+
return this.map((vertex) => affineTransform.transformVec2(vertex));
|
48
48
|
}
|
49
49
|
// Transforms this by a linear transform --- verticies are treated as
|
50
50
|
// 3D points.
|
51
51
|
transformedBy(linearTransform) {
|
52
|
-
return this.map(vertex => linearTransform.transformVec3(vertex));
|
52
|
+
return this.map((vertex) => linearTransform.transformVec3(vertex));
|
53
53
|
}
|
54
54
|
/**
|
55
55
|
* Returns the sides of this triangle, as an array of `LineSegment2`s.
|
@@ -71,8 +71,7 @@ class Triangle extends Abstract2DShape_1.default {
|
|
71
71
|
intersectsLineSegment(lineSegment) {
|
72
72
|
const result = [];
|
73
73
|
for (const edge of this.getEdges()) {
|
74
|
-
edge.intersectsLineSegment(lineSegment)
|
75
|
-
.forEach(point => result.push(point));
|
74
|
+
edge.intersectsLineSegment(lineSegment).forEach((point) => result.push(point));
|
76
75
|
}
|
77
76
|
return result;
|
78
77
|
}
|
@@ -106,7 +105,7 @@ class Triangle extends Abstract2DShape_1.default {
|
|
106
105
|
*/
|
107
106
|
signedDistance(point) {
|
108
107
|
const sides = this.getEdges();
|
109
|
-
const distances = sides.map(side => side.distance(point));
|
108
|
+
const distances = sides.map((side) => side.distance(point));
|
110
109
|
const distance = Math.min(...distances);
|
111
110
|
// If the point is in this' interior, signedDistance must return a negative
|
112
111
|
// number.
|
@@ -12,9 +12,9 @@ const convexHull2Of = (points) => {
|
|
12
12
|
return [];
|
13
13
|
}
|
14
14
|
// 1. Start with a vertex on the hull
|
15
|
-
const lowestPoint = points.reduce((lowest, current) => current.y < lowest.y ? current : lowest, points[0]);
|
15
|
+
const lowestPoint = points.reduce((lowest, current) => (current.y < lowest.y ? current : lowest), points[0]);
|
16
16
|
const vertices = [lowestPoint];
|
17
|
-
let toProcess = [...points.filter(p => !p.eq(lowestPoint))];
|
17
|
+
let toProcess = [...points.filter((p) => !p.eq(lowestPoint))];
|
18
18
|
let lastBaseDirection = Vec2_1.Vec2.of(-1, 0);
|
19
19
|
// 2. Find the point with greatest angle from the vertex:
|
20
20
|
//
|
@@ -40,7 +40,7 @@ const convexHull2Of = (points) => {
|
|
40
40
|
smallestDotProductSoFar = currentDotProduct;
|
41
41
|
}
|
42
42
|
}
|
43
|
-
toProcess = toProcess.filter(p => !p.eq(furthestPointSoFar));
|
43
|
+
toProcess = toProcess.filter((p) => !p.eq(furthestPointSoFar));
|
44
44
|
const newBaseDirection = furthestPointSoFar.minus(lastVertex).normalized();
|
45
45
|
// If the last vertex is on the same edge as the current, there's no need to include
|
46
46
|
// the previous one.
|