@js-draw/math 1.21.3 → 1.22.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|