@js-draw/math 1.21.3 → 1.23.1
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.d.ts +24 -1
- package/dist/cjs/Color4.js +35 -3
- package/dist/cjs/Mat33.d.ts +21 -11
- package/dist/cjs/Mat33.js +28 -24
- package/dist/cjs/Vec3.d.ts +12 -3
- package/dist/cjs/Vec3.js +20 -9
- package/dist/cjs/lib.d.ts +3 -0
- package/dist/cjs/lib.js +3 -0
- package/dist/cjs/shapes/BezierJSWrapper.d.ts +2 -0
- package/dist/cjs/shapes/BezierJSWrapper.js +22 -13
- package/dist/cjs/shapes/LineSegment2.js +13 -17
- package/dist/cjs/shapes/Parameterized2DShape.js +1 -1
- package/dist/cjs/shapes/Path.d.ts +1 -0
- package/dist/cjs/shapes/Path.js +50 -47
- package/dist/cjs/shapes/QuadraticBezier.d.ts +19 -2
- package/dist/cjs/shapes/QuadraticBezier.js +26 -3
- package/dist/cjs/shapes/Rect2.d.ts +13 -0
- package/dist/cjs/shapes/Rect2.js +35 -16
- package/dist/cjs/shapes/Triangle.js +4 -5
- package/dist/cjs/utils/convexHull2Of.js +3 -3
- package/dist/mjs/Color4.d.ts +24 -1
- package/dist/mjs/Color4.mjs +35 -3
- package/dist/mjs/Mat33.d.ts +21 -11
- package/dist/mjs/Mat33.mjs +28 -24
- package/dist/mjs/Vec3.d.ts +12 -3
- package/dist/mjs/Vec3.mjs +20 -9
- package/dist/mjs/lib.d.ts +3 -0
- package/dist/mjs/lib.mjs +3 -0
- package/dist/mjs/shapes/BezierJSWrapper.d.ts +2 -0
- package/dist/mjs/shapes/BezierJSWrapper.mjs +22 -13
- package/dist/mjs/shapes/LineSegment2.mjs +13 -17
- package/dist/mjs/shapes/Parameterized2DShape.mjs +1 -1
- package/dist/mjs/shapes/Path.d.ts +1 -0
- package/dist/mjs/shapes/Path.mjs +50 -47
- package/dist/mjs/shapes/QuadraticBezier.d.ts +19 -2
- package/dist/mjs/shapes/QuadraticBezier.mjs +26 -3
- package/dist/mjs/shapes/Rect2.d.ts +13 -0
- package/dist/mjs/shapes/Rect2.mjs +35 -16
- 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 +21 -21
- package/src/Color4.ts +61 -18
- package/src/Mat33.fromCSSMatrix.test.ts +32 -46
- package/src/Mat33.test.ts +64 -102
- package/src/Mat33.ts +81 -104
- package/src/Vec2.test.ts +3 -3
- package/src/Vec3.test.ts +2 -3
- package/src/Vec3.ts +46 -61
- package/src/lib.ts +3 -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 +56 -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 +379 -352
- package/src/shapes/PointShape2D.ts +3 -3
- package/src/shapes/QuadraticBezier.test.ts +27 -21
- package/src/shapes/QuadraticBezier.ts +26 -11
- package/src/shapes/Rect2.test.ts +44 -75
- package/src/shapes/Rect2.ts +47 -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/src/Mat33.ts
CHANGED
@@ -4,11 +4,7 @@ import Vec3 from './Vec3';
|
|
4
4
|
/**
|
5
5
|
* See {@link Mat33.toArray}.
|
6
6
|
*/
|
7
|
-
export type Mat33Array = [
|
8
|
-
number, number, number,
|
9
|
-
number, number, number,
|
10
|
-
number, number, number,
|
11
|
-
];
|
7
|
+
export type Mat33Array = [number, number, number, number, number, number, number, number, number];
|
12
8
|
|
13
9
|
/**
|
14
10
|
* Represents a three dimensional linear transformation or
|
@@ -82,13 +78,9 @@ export class Mat33 {
|
|
82
78
|
|
83
79
|
public readonly c1: number,
|
84
80
|
public readonly c2: number,
|
85
|
-
public readonly c3: number
|
81
|
+
public readonly c3: number,
|
86
82
|
) {
|
87
|
-
this.rows = [
|
88
|
-
Vec3.of(a1, a2, a3),
|
89
|
-
Vec3.of(b1, b2, b3),
|
90
|
-
Vec3.of(c1, c2, c3),
|
91
|
-
];
|
83
|
+
this.rows = [Vec3.of(a1, a2, a3), Vec3.of(b1, b2, b3), Vec3.of(c1, c2, c3)];
|
92
84
|
}
|
93
85
|
|
94
86
|
/**
|
@@ -102,19 +94,11 @@ export class Mat33 {
|
|
102
94
|
* $$
|
103
95
|
*/
|
104
96
|
public static ofRows(r1: Vec3, r2: Vec3, r3: Vec3): Mat33 {
|
105
|
-
return new Mat33(
|
106
|
-
r1.x, r1.y, r1.z,
|
107
|
-
r2.x, r2.y, r2.z,
|
108
|
-
r3.x, r3.y, r3.z
|
109
|
-
);
|
97
|
+
return new Mat33(r1.x, r1.y, r1.z, r2.x, r2.y, r2.z, r3.x, r3.y, r3.z);
|
110
98
|
}
|
111
99
|
|
112
100
|
/** The 3x3 [identity matrix](https://en.wikipedia.org/wiki/Identity_matrix). */
|
113
|
-
public static identity = new Mat33(
|
114
|
-
1, 0, 0,
|
115
|
-
0, 1, 0,
|
116
|
-
0, 0, 1
|
117
|
-
);
|
101
|
+
public static identity = new Mat33(1, 0, 0, 0, 1, 0, 0, 0, 1);
|
118
102
|
|
119
103
|
/**
|
120
104
|
* Either returns the inverse of this, or, if this matrix is singular/uninvertable,
|
@@ -131,23 +115,15 @@ export class Mat33 {
|
|
131
115
|
return this.computeInverse() !== null;
|
132
116
|
}
|
133
117
|
|
134
|
-
private cachedInverse: Mat33|undefined|null = undefined;
|
135
|
-
private computeInverse(): Mat33|null {
|
118
|
+
private cachedInverse: Mat33 | undefined | null = undefined;
|
119
|
+
private computeInverse(): Mat33 | null {
|
136
120
|
if (this.cachedInverse !== undefined) {
|
137
121
|
return this.cachedInverse;
|
138
122
|
}
|
139
123
|
|
140
|
-
const toIdentity = [
|
141
|
-
this.rows[0],
|
142
|
-
this.rows[1],
|
143
|
-
this.rows[2],
|
144
|
-
];
|
124
|
+
const toIdentity = [this.rows[0], this.rows[1], this.rows[2]];
|
145
125
|
|
146
|
-
const toResult = [
|
147
|
-
Vec3.unitX,
|
148
|
-
Vec3.unitY,
|
149
|
-
Vec3.unitZ,
|
150
|
-
];
|
126
|
+
const toResult = [Vec3.unitX, Vec3.unitY, Vec3.unitZ];
|
151
127
|
|
152
128
|
// Convert toIdentity to the identity matrix and
|
153
129
|
// toResult to the inverse through elementary row operations
|
@@ -199,29 +175,27 @@ export class Mat33 {
|
|
199
175
|
for (let i = 1; i <= 2; i++) {
|
200
176
|
const otherRowIdx = (cursor + i) % 3;
|
201
177
|
scale = -toIdentity[otherRowIdx].at(cursor);
|
202
|
-
toIdentity[otherRowIdx] = toIdentity[otherRowIdx].plus(
|
203
|
-
|
204
|
-
);
|
205
|
-
toResult[otherRowIdx] = toResult[otherRowIdx].plus(
|
206
|
-
cursorToResultRow.times(scale)
|
207
|
-
);
|
178
|
+
toIdentity[otherRowIdx] = toIdentity[otherRowIdx].plus(cursorToIdentityRow.times(scale));
|
179
|
+
toResult[otherRowIdx] = toResult[otherRowIdx].plus(cursorToResultRow.times(scale));
|
208
180
|
}
|
209
181
|
}
|
210
182
|
|
211
|
-
const inverse = Mat33.ofRows(
|
212
|
-
toResult[0],
|
213
|
-
toResult[1],
|
214
|
-
toResult[2]
|
215
|
-
);
|
183
|
+
const inverse = Mat33.ofRows(toResult[0], toResult[1], toResult[2]);
|
216
184
|
this.cachedInverse = inverse;
|
217
185
|
return inverse;
|
218
186
|
}
|
219
187
|
|
220
188
|
public transposed(): Mat33 {
|
221
189
|
return new Mat33(
|
222
|
-
this.a1,
|
223
|
-
this.
|
224
|
-
this.
|
190
|
+
this.a1,
|
191
|
+
this.b1,
|
192
|
+
this.c1,
|
193
|
+
this.a2,
|
194
|
+
this.b2,
|
195
|
+
this.c2,
|
196
|
+
this.a3,
|
197
|
+
this.b3,
|
198
|
+
this.c3,
|
225
199
|
);
|
226
200
|
}
|
227
201
|
|
@@ -256,9 +230,15 @@ export class Mat33 {
|
|
256
230
|
};
|
257
231
|
|
258
232
|
return new Mat33(
|
259
|
-
at(0, 0),
|
260
|
-
at(
|
261
|
-
at(
|
233
|
+
at(0, 0),
|
234
|
+
at(0, 1),
|
235
|
+
at(0, 2),
|
236
|
+
at(1, 0),
|
237
|
+
at(1, 1),
|
238
|
+
at(1, 2),
|
239
|
+
at(2, 0),
|
240
|
+
at(2, 1),
|
241
|
+
at(2, 2),
|
262
242
|
);
|
263
243
|
}
|
264
244
|
|
@@ -288,11 +268,7 @@ export class Mat33 {
|
|
288
268
|
* This is the standard way of transforming vectors in ℝ³.
|
289
269
|
*/
|
290
270
|
public transformVec3(other: Vec3): Vec3 {
|
291
|
-
return Vec3.of(
|
292
|
-
this.rows[0].dot(other),
|
293
|
-
this.rows[1].dot(other),
|
294
|
-
this.rows[2].dot(other)
|
295
|
-
);
|
271
|
+
return Vec3.of(this.rows[0].dot(other), this.rows[1].dot(other), this.rows[2].dot(other));
|
296
272
|
}
|
297
273
|
|
298
274
|
/** @returns true iff this is the identity matrix. */
|
@@ -326,7 +302,7 @@ export class Mat33 {
|
|
326
302
|
*/
|
327
303
|
public toString(): string {
|
328
304
|
let result = '';
|
329
|
-
const maxColumnLens = [
|
305
|
+
const maxColumnLens = [0, 0, 0];
|
330
306
|
|
331
307
|
// Determine the longest item in each column so we can pad the others to that
|
332
308
|
// length.
|
@@ -390,11 +366,7 @@ export class Mat33 {
|
|
390
366
|
* ```
|
391
367
|
*/
|
392
368
|
public toArray(): Mat33Array {
|
393
|
-
return [
|
394
|
-
this.a1, this.a2, this.a3,
|
395
|
-
this.b1, this.b2, this.b3,
|
396
|
-
this.c1, this.c2, this.c3,
|
397
|
-
];
|
369
|
+
return [this.a1, this.a2, this.a3, this.b1, this.b2, this.b3, this.c1, this.c2, this.c3];
|
398
370
|
}
|
399
371
|
|
400
372
|
/**
|
@@ -413,11 +385,17 @@ export class Mat33 {
|
|
413
385
|
* // ⎣ 6, 7, 8 ⎦
|
414
386
|
* ```
|
415
387
|
*/
|
416
|
-
public mapEntries(mapping: (component: number, rowcol: [number, number])=>number): Mat33 {
|
388
|
+
public mapEntries(mapping: (component: number, rowcol: [number, number]) => number): Mat33 {
|
417
389
|
return new Mat33(
|
418
|
-
mapping(this.a1, [0, 0]),
|
419
|
-
mapping(this.
|
420
|
-
mapping(this.
|
390
|
+
mapping(this.a1, [0, 0]),
|
391
|
+
mapping(this.a2, [0, 1]),
|
392
|
+
mapping(this.a3, [0, 2]),
|
393
|
+
mapping(this.b1, [1, 0]),
|
394
|
+
mapping(this.b2, [1, 1]),
|
395
|
+
mapping(this.b3, [1, 2]),
|
396
|
+
mapping(this.c1, [2, 0]),
|
397
|
+
mapping(this.c2, [2, 1]),
|
398
|
+
mapping(this.c3, [2, 2]),
|
421
399
|
);
|
422
400
|
}
|
423
401
|
|
@@ -428,11 +406,7 @@ export class Mat33 {
|
|
428
406
|
|
429
407
|
/** Returns the `idx`-th column (`idx` is 0-indexed). */
|
430
408
|
public getColumn(idx: number) {
|
431
|
-
return Vec3.of(
|
432
|
-
this.rows[0].at(idx),
|
433
|
-
this.rows[1].at(idx),
|
434
|
-
this.rows[2].at(idx),
|
435
|
-
);
|
409
|
+
return Vec3.of(this.rows[0].at(idx), this.rows[1].at(idx), this.rows[2].at(idx));
|
436
410
|
}
|
437
411
|
|
438
412
|
/** Returns the magnitude of the entry with the largest entry */
|
@@ -463,13 +437,29 @@ export class Mat33 {
|
|
463
437
|
// Vec2s z = 1. As such,
|
464
438
|
// outVec2.x = inVec2.x * 1 + inVec2.y * 0 + 1 * amount.x
|
465
439
|
// ...
|
466
|
-
return new Mat33(
|
467
|
-
1, 0, amount.x,
|
468
|
-
0, 1, amount.y,
|
469
|
-
0, 0, 1
|
470
|
-
);
|
440
|
+
return new Mat33(1, 0, amount.x, 0, 1, amount.y, 0, 0, 1);
|
471
441
|
}
|
472
442
|
|
443
|
+
/**
|
444
|
+
* Creates a matrix for rotating `Vec2`s about `center` by some number of `radians`.
|
445
|
+
*
|
446
|
+
* For this function, {@link Vec2}s are considered to be points in 2D space.
|
447
|
+
*
|
448
|
+
* For example,
|
449
|
+
* ```ts,runnable,console
|
450
|
+
* import { Mat33, Vec2 } from '@js-draw/math';
|
451
|
+
*
|
452
|
+
* const halfCircle = Math.PI; // PI radians = 180 degrees = 1/2 circle
|
453
|
+
* const center = Vec2.of(1, 1); // The point (1,1)
|
454
|
+
* const rotationMatrix = Mat33.zRotation(halfCircle, center);
|
455
|
+
*
|
456
|
+
* console.log(
|
457
|
+
* 'Rotating (0,0) 180deg about', center, 'results in',
|
458
|
+
* // Rotates (0,0)
|
459
|
+
* rotationMatrix.transformVec2(Vec2.zero),
|
460
|
+
* );
|
461
|
+
* ```
|
462
|
+
*/
|
473
463
|
public static zRotation(radians: number, center: Point2 = Vec2.zero): Mat33 {
|
474
464
|
if (radians === 0) {
|
475
465
|
return Mat33.identity;
|
@@ -481,15 +471,11 @@ export class Mat33 {
|
|
481
471
|
// Translate everything so that rotation is about the origin
|
482
472
|
let result = Mat33.translation(center);
|
483
473
|
|
484
|
-
result = result.rightMul(new Mat33(
|
485
|
-
cos, -sin, 0,
|
486
|
-
sin, cos, 0,
|
487
|
-
0, 0, 1
|
488
|
-
));
|
474
|
+
result = result.rightMul(new Mat33(cos, -sin, 0, sin, cos, 0, 0, 0, 1));
|
489
475
|
return result.rightMul(Mat33.translation(center.times(-1)));
|
490
476
|
}
|
491
477
|
|
492
|
-
public static scaling2D(amount: number|Vec2, center: Point2 = Vec2.zero): Mat33 {
|
478
|
+
public static scaling2D(amount: number | Vec2, center: Point2 = Vec2.zero): Mat33 {
|
493
479
|
let result = Mat33.translation(center);
|
494
480
|
let xAmount, yAmount;
|
495
481
|
|
@@ -501,11 +487,7 @@ export class Mat33 {
|
|
501
487
|
yAmount = amount.y;
|
502
488
|
}
|
503
489
|
|
504
|
-
result = result.rightMul(new Mat33(
|
505
|
-
xAmount, 0, 0,
|
506
|
-
0, yAmount, 0,
|
507
|
-
0, 0, 1
|
508
|
-
));
|
490
|
+
result = result.rightMul(new Mat33(xAmount, 0, 0, 0, yAmount, 0, 0, 0, 1));
|
509
491
|
|
510
492
|
// Translate such that [center] goes to (0, 0)
|
511
493
|
return result.rightMul(Mat33.translation(center.times(-1)));
|
@@ -536,7 +518,7 @@ export class Mat33 {
|
|
536
518
|
}
|
537
519
|
|
538
520
|
const parseArguments = (argumentString: string): number[] => {
|
539
|
-
const parsed = argumentString.split(/[, \t\n]+/g).map(argString => {
|
521
|
+
const parsed = argumentString.split(/[, \t\n]+/g).map((argString) => {
|
540
522
|
// Handle trailing spaces/commands
|
541
523
|
if (argString.trim() === '') {
|
542
524
|
return null;
|
@@ -549,18 +531,16 @@ export class Mat33 {
|
|
549
531
|
}
|
550
532
|
|
551
533
|
// Remove trailing px units.
|
552
|
-
argString = argString.replace(/px$/
|
534
|
+
argString = argString.replace(/px$/gi, '');
|
553
535
|
|
554
536
|
const numberExp = /^[-]?\d*(?:\.\d*)?(?:[eE][-+]?\d+)?$/i;
|
555
537
|
|
556
538
|
if (!numberExp.exec(argString)) {
|
557
539
|
throw new Error(
|
558
|
-
`All arguments to transform functions must be numeric (state: ${
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
})
|
563
|
-
})`
|
540
|
+
`All arguments to transform functions must be numeric (state: ${JSON.stringify({
|
541
|
+
currentArgument: argString,
|
542
|
+
allArguments: argumentString,
|
543
|
+
})})`,
|
564
544
|
);
|
565
545
|
}
|
566
546
|
|
@@ -572,10 +552,9 @@ export class Mat33 {
|
|
572
552
|
|
573
553
|
return argNumber;
|
574
554
|
});
|
575
|
-
return parsed.filter(n => n !== null);
|
555
|
+
return parsed.filter((n) => n !== null);
|
576
556
|
};
|
577
557
|
|
578
|
-
|
579
558
|
const keywordToAction = {
|
580
559
|
matrix: (matrixData: number[]) => {
|
581
560
|
if (matrixData.length !== 6) {
|
@@ -589,11 +568,7 @@ export class Mat33 {
|
|
589
568
|
const e = matrixData[4];
|
590
569
|
const f = matrixData[5];
|
591
570
|
|
592
|
-
const transform = new Mat33(
|
593
|
-
a, c, e,
|
594
|
-
b, d, f,
|
595
|
-
0, 0, 1
|
596
|
-
);
|
571
|
+
const transform = new Mat33(a, c, e, b, d, f, 0, 0, 1);
|
597
572
|
return transform;
|
598
573
|
},
|
599
574
|
|
@@ -623,7 +598,9 @@ export class Mat33 {
|
|
623
598
|
translateX = translateArgs[0];
|
624
599
|
translateY = translateArgs[1];
|
625
600
|
} else {
|
626
|
-
throw new Error(
|
601
|
+
throw new Error(
|
602
|
+
`The translate() function requires either 1 or 2 arguments. Given ${translateArgs}`,
|
603
|
+
);
|
627
604
|
}
|
628
605
|
|
629
606
|
return Mat33.translation(Vec2.of(translateX, translateY));
|
@@ -632,9 +609,9 @@ export class Mat33 {
|
|
632
609
|
|
633
610
|
// A command (\w+)
|
634
611
|
// followed by a set of arguments ([ \t\n0-9eE.,\-%]+)
|
635
|
-
const partRegex = /\s*(\w+)\s*\(([^)]*)\)/
|
612
|
+
const partRegex = /\s*(\w+)\s*\(([^)]*)\)/gi;
|
636
613
|
let match;
|
637
|
-
let matrix: Mat33|null = null;
|
614
|
+
let matrix: Mat33 | null = null;
|
638
615
|
|
639
616
|
while ((match = partRegex.exec(cssString)) !== null) {
|
640
617
|
const action = match[1].toLowerCase();
|
package/src/Vec2.test.ts
CHANGED
@@ -17,11 +17,11 @@ describe('Vec2', () => {
|
|
17
17
|
});
|
18
18
|
|
19
19
|
it('More complicated expressions', () => {
|
20
|
-
expect(
|
20
|
+
expect(Vec2.of(1, 2).plus(Vec2.of(3, 4)).times(2)).objEq(Vec2.of(8, 12));
|
21
21
|
});
|
22
22
|
|
23
23
|
it('Angle', () => {
|
24
|
-
expect(Vec2.of(-1, 1).angle()).toBeCloseTo(3 * Math.PI / 4);
|
24
|
+
expect(Vec2.of(-1, 1).angle()).toBeCloseTo((3 * Math.PI) / 4);
|
25
25
|
});
|
26
26
|
|
27
27
|
it('Perpindicular', () => {
|
@@ -29,4 +29,4 @@ describe('Vec2', () => {
|
|
29
29
|
expect(Vec2.unitX.cross(Vec3.unitZ)).objEq(Vec2.unitY.times(-1), tolerance);
|
30
30
|
expect(Vec2.unitX.orthog()).objEq(Vec2.unitY, tolerance);
|
31
31
|
});
|
32
|
-
});
|
32
|
+
});
|
package/src/Vec3.test.ts
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
import Vec3 from './Vec3';
|
3
2
|
|
4
3
|
describe('Vec3', () => {
|
@@ -60,7 +59,7 @@ describe('Vec3', () => {
|
|
60
59
|
{ from: Vec3.of(-1, -10, 0), to: Vec3.of(1, 2, 0), expected: 148 },
|
61
60
|
])(
|
62
61
|
'.squareDistanceTo and .distanceTo should return correct square and euclidean distances (%j)',
|
63
|
-
({ from
|
62
|
+
({ from, to, expected }) => {
|
64
63
|
expect(from.squareDistanceTo(to)).toBe(expected);
|
65
64
|
expect(to.squareDistanceTo(from)).toBe(expected);
|
66
65
|
expect(to.distanceTo(from)).toBeCloseTo(Math.sqrt(expected));
|
@@ -83,4 +82,4 @@ describe('Vec3', () => {
|
|
83
82
|
expect(a.eq(b, tolerance)).toBe(eq);
|
84
83
|
expect(b.eq(a, tolerance)).toBe(eq);
|
85
84
|
});
|
86
|
-
});
|
85
|
+
});
|
package/src/Vec3.ts
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
1
|
/**
|
4
2
|
* A vector with three components, $\begin{pmatrix} x \\ y \\ z \end{pmatrix}$.
|
5
3
|
* Can also be used to represent a two-component vector.
|
@@ -28,7 +26,7 @@ export interface Vec3 {
|
|
28
26
|
* Returns the x, y components of this.
|
29
27
|
* May be implemented as a getter method.
|
30
28
|
*/
|
31
|
-
readonly xy: { x: number
|
29
|
+
readonly xy: { x: number; y: number };
|
32
30
|
|
33
31
|
/** Returns the vector's `idx`th component. For example, `Vec3.of(1, 2, 3).at(1) → 2`. */
|
34
32
|
at(i: number): number;
|
@@ -124,7 +122,7 @@ export interface Vec3 {
|
|
124
122
|
* Vec3.of(1, 2, 3).scale(Vec3.of(2, 4, 6)); // → Vec3(2, 8, 18)
|
125
123
|
* ```
|
126
124
|
*/
|
127
|
-
scale(other: Vec3|number): Vec3;
|
125
|
+
scale(other: Vec3 | number): Vec3;
|
128
126
|
|
129
127
|
/**
|
130
128
|
* Returns a vector orthogonal to this. If this is a Vec2, returns `this` rotated
|
@@ -154,9 +152,7 @@ export interface Vec3 {
|
|
154
152
|
* console.log(zipped.toString()); // → Vec(0.5, 2, 2.9)
|
155
153
|
* ```
|
156
154
|
*/
|
157
|
-
zip(
|
158
|
-
other: Vec3, zip: (componentInThis: number, componentInOther: number)=> number
|
159
|
-
): Vec3;
|
155
|
+
zip(other: Vec3, zip: (componentInThis: number, componentInOther: number) => number): Vec3;
|
160
156
|
|
161
157
|
/**
|
162
158
|
* Returns a vector with each component acted on by `fn`.
|
@@ -167,13 +163,12 @@ export interface Vec3 {
|
|
167
163
|
* console.log(Vec3.of(1, 2, 3).map(val => val + 1)); // → Vec(2, 3, 4)
|
168
164
|
* ```
|
169
165
|
*/
|
170
|
-
map(fn: (component: number, index: number)=> number): Vec3;
|
171
|
-
|
172
|
-
asArray(): [ number, number, number ];
|
166
|
+
map(fn: (component: number, index: number) => number): Vec3;
|
173
167
|
|
168
|
+
asArray(): [number, number, number];
|
174
169
|
|
175
170
|
/**
|
176
|
-
*
|
171
|
+
* @param tolerance The maximum difference between two components for this and [other]
|
177
172
|
* to be considered equal.
|
178
173
|
*
|
179
174
|
* @example
|
@@ -196,9 +191,8 @@ class Vec3Impl implements Vec3 {
|
|
196
191
|
public constructor(
|
197
192
|
public readonly x: number,
|
198
193
|
public readonly y: number,
|
199
|
-
public readonly z: number
|
200
|
-
) {
|
201
|
-
}
|
194
|
+
public readonly z: number,
|
195
|
+
) {}
|
202
196
|
|
203
197
|
public get xy(): { x: number; y: number } {
|
204
198
|
// Useful for APIs that behave differently if .z is present.
|
@@ -288,16 +282,12 @@ class Vec3Impl implements Vec3 {
|
|
288
282
|
);
|
289
283
|
}
|
290
284
|
|
291
|
-
public scale(other: Vec3|number): Vec3 {
|
285
|
+
public scale(other: Vec3 | number): Vec3 {
|
292
286
|
if (typeof other === 'number') {
|
293
287
|
return this.times(other);
|
294
288
|
}
|
295
289
|
|
296
|
-
return Vec3.of(
|
297
|
-
this.x * other.x,
|
298
|
-
this.y * other.y,
|
299
|
-
this.z * other.z,
|
300
|
-
);
|
290
|
+
return Vec3.of(this.x * other.x, this.y * other.y, this.z * other.z);
|
301
291
|
}
|
302
292
|
|
303
293
|
public orthog(): Vec3 {
|
@@ -318,28 +308,25 @@ class Vec3Impl implements Vec3 {
|
|
318
308
|
}
|
319
309
|
|
320
310
|
public zip(
|
321
|
-
other: Vec3,
|
311
|
+
other: Vec3,
|
312
|
+
zip: (componentInThis: number, componentInOther: number) => number,
|
322
313
|
): Vec3 {
|
323
|
-
return Vec3.of(
|
324
|
-
zip(other.x, this.x),
|
325
|
-
zip(other.y, this.y),
|
326
|
-
zip(other.z, this.z)
|
327
|
-
);
|
314
|
+
return Vec3.of(zip(other.x, this.x), zip(other.y, this.y), zip(other.z, this.z));
|
328
315
|
}
|
329
316
|
|
330
|
-
public map(fn: (component: number, index: number)=> number): Vec3 {
|
317
|
+
public map(fn: (component: number, index: number) => number): Vec3 {
|
331
318
|
return Vec3.of(fn(this.x, 0), fn(this.y, 1), fn(this.z, 2));
|
332
319
|
}
|
333
320
|
|
334
|
-
public asArray(): [
|
321
|
+
public asArray(): [number, number, number] {
|
335
322
|
return [this.x, this.y, this.z];
|
336
323
|
}
|
337
324
|
|
338
325
|
public eq(other: Vec3, fuzz: number = defaultEqlTolerance): boolean {
|
339
326
|
return (
|
340
|
-
Math.abs(other.x - this.x) <= fuzz
|
341
|
-
|
342
|
-
|
327
|
+
Math.abs(other.x - this.x) <= fuzz &&
|
328
|
+
Math.abs(other.y - this.y) <= fuzz &&
|
329
|
+
Math.abs(other.z - this.z) <= fuzz
|
343
330
|
);
|
344
331
|
}
|
345
332
|
|
@@ -352,10 +339,11 @@ class Vec2Impl implements Vec3 {
|
|
352
339
|
public constructor(
|
353
340
|
public readonly x: number,
|
354
341
|
public readonly y: number,
|
355
|
-
) {
|
356
|
-
}
|
342
|
+
) {}
|
357
343
|
|
358
|
-
public get z() {
|
344
|
+
public get z() {
|
345
|
+
return 0;
|
346
|
+
}
|
359
347
|
|
360
348
|
public get xy(): { x: number; y: number } {
|
361
349
|
// Useful for APIs that behave differently if .z is present.
|
@@ -436,22 +424,15 @@ class Vec2Impl implements Vec3 {
|
|
436
424
|
// | i j k |
|
437
425
|
// | x1 y1 z1| = (i)(y1z2 - y2z1) - (j)(x1z2 - x2z1) + (k)(x1y2 - x2y1)
|
438
426
|
// | x2 y2 z2|
|
439
|
-
return Vec3.of(
|
440
|
-
this.y * other.z,
|
441
|
-
-this.x * other.z,
|
442
|
-
this.x * other.y - other.x * this.y,
|
443
|
-
);
|
427
|
+
return Vec3.of(this.y * other.z, -this.x * other.z, this.x * other.y - other.x * this.y);
|
444
428
|
}
|
445
429
|
|
446
|
-
public scale(other: Vec3|number): Vec3 {
|
430
|
+
public scale(other: Vec3 | number): Vec3 {
|
447
431
|
if (typeof other === 'number') {
|
448
432
|
return this.times(other);
|
449
433
|
}
|
450
434
|
|
451
|
-
return Vec2.of(
|
452
|
-
this.x * other.x,
|
453
|
-
this.y * other.y,
|
454
|
-
);
|
435
|
+
return Vec2.of(this.x * other.x, this.y * other.y);
|
455
436
|
}
|
456
437
|
|
457
438
|
public orthog(): Vec3 {
|
@@ -472,30 +453,25 @@ class Vec2Impl implements Vec3 {
|
|
472
453
|
}
|
473
454
|
|
474
455
|
public zip(
|
475
|
-
other: Vec3,
|
456
|
+
other: Vec3,
|
457
|
+
zip: (componentInThis: number, componentInOther: number) => number,
|
476
458
|
): Vec3 {
|
477
|
-
return Vec3.of(
|
478
|
-
zip(other.x, this.x),
|
479
|
-
zip(other.y, this.y),
|
480
|
-
zip(other.z, 0),
|
481
|
-
);
|
459
|
+
return Vec3.of(zip(other.x, this.x), zip(other.y, this.y), zip(other.z, 0));
|
482
460
|
}
|
483
461
|
|
484
|
-
public map(fn: (component: number, index: number)=> number): Vec3 {
|
485
|
-
return Vec3.of(
|
486
|
-
fn(this.x, 0), fn(this.y, 1), fn(0, 2)
|
487
|
-
);
|
462
|
+
public map(fn: (component: number, index: number) => number): Vec3 {
|
463
|
+
return Vec3.of(fn(this.x, 0), fn(this.y, 1), fn(0, 2));
|
488
464
|
}
|
489
465
|
|
490
|
-
public asArray(): [
|
466
|
+
public asArray(): [number, number, number] {
|
491
467
|
return [this.x, this.y, 0];
|
492
468
|
}
|
493
469
|
|
494
470
|
public eq(other: Vec3, fuzz: number = defaultEqlTolerance): boolean {
|
495
471
|
return (
|
496
|
-
Math.abs(other.x - this.x) <= fuzz
|
497
|
-
|
498
|
-
|
472
|
+
Math.abs(other.x - this.x) <= fuzz &&
|
473
|
+
Math.abs(other.y - this.y) <= fuzz &&
|
474
|
+
Math.abs(other.z) <= fuzz
|
499
475
|
);
|
500
476
|
}
|
501
477
|
|
@@ -505,12 +481,16 @@ class Vec2Impl implements Vec3 {
|
|
505
481
|
}
|
506
482
|
|
507
483
|
/**
|
508
|
-
* A `Vec2` is a
|
484
|
+
* A `Vec2` is a {@link Vec3} optimized for working in a plane. `Vec2`s have an
|
509
485
|
* always-zero `z` component.
|
510
486
|
*
|
511
487
|
* ```ts,runnable,console
|
512
488
|
* import { Vec2 } from '@js-draw/math';
|
513
|
-
*
|
489
|
+
*
|
490
|
+
* const v = Vec2.of(1, 2);
|
491
|
+
* console.log('a Vec2:', v);
|
492
|
+
* console.log('x component:', v.x);
|
493
|
+
* console.log('z component:', v.z);
|
514
494
|
* ```
|
515
495
|
*/
|
516
496
|
export namespace Vec2 {
|
@@ -537,7 +517,7 @@ export namespace Vec2 {
|
|
537
517
|
* const v2 = Vec2.ofXY({ x: -123.4, y: 1 });
|
538
518
|
* ```
|
539
519
|
*/
|
540
|
-
export const ofXY = ({x, y}: {x: number
|
520
|
+
export const ofXY = ({ x, y }: { x: number; y: number }) => {
|
541
521
|
return Vec2.of(x, y);
|
542
522
|
};
|
543
523
|
|
@@ -551,6 +531,7 @@ export namespace Vec2 {
|
|
551
531
|
export const zero = Vec2.of(0, 0);
|
552
532
|
}
|
553
533
|
|
534
|
+
/** Contains static methods for constructing a {@link Vec3}. */
|
554
535
|
export namespace Vec3 {
|
555
536
|
/**
|
556
537
|
* Construct a vector from three components.
|
@@ -559,6 +540,7 @@ export namespace Vec3 {
|
|
559
540
|
* ```ts,runnable,console
|
560
541
|
* import { Vec3 } from '@js-draw/math';
|
561
542
|
* const v1 = Vec3.of(1, 2, 3);
|
543
|
+
* console.log(v1.plus(Vec3.of(0, 100, 0)));
|
562
544
|
* ```
|
563
545
|
*/
|
564
546
|
export const of = (x: number, y: number, z: number): Vec3 => {
|
@@ -569,8 +551,11 @@ export namespace Vec3 {
|
|
569
551
|
}
|
570
552
|
};
|
571
553
|
|
554
|
+
/** A unit vector in the x direction (`[1, 0, 0]`). */
|
572
555
|
export const unitX = Vec2.unitX;
|
556
|
+
/** A unit vector in the y direction (`[0, 1, 0]`). */
|
573
557
|
export const unitY = Vec2.unitY;
|
558
|
+
/** The zero vector (`[0, 0, 0]`). */
|
574
559
|
export const zero = Vec2.zero;
|
575
560
|
|
576
561
|
/** A vector of length 1 in the z direction. */
|
package/src/lib.ts
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
/**
|
2
|
+
* This package contains general math utilities used by `js-draw`.
|
3
|
+
* These include 2D and 3D vectors, 2D paths, and 3x3 matrices.
|
4
|
+
*
|
2
5
|
* ```ts,runnable,console
|
3
6
|
* import { Vec2, Mat33, Rect2 } from '@js-draw/math';
|
4
7
|
*
|
@@ -20,7 +23,6 @@
|
|
20
23
|
export { LineSegment2 } from './shapes/LineSegment2';
|
21
24
|
export {
|
22
25
|
Path,
|
23
|
-
|
24
26
|
IntersectionResult as PathIntersectionResult,
|
25
27
|
CurveIndexRecord as PathCurveIndex,
|
26
28
|
stepCurveIndexBy as stepPathIndexBy,
|
@@ -43,6 +45,5 @@ export { Vec3 } from './Vec3';
|
|
43
45
|
export { Color4 } from './Color4';
|
44
46
|
export * from './rounding/lib';
|
45
47
|
|
46
|
-
|
47
48
|
// Note: All above exports cannot use `export { default as ... } from "..."` because this
|
48
49
|
// breaks TypeDoc -- TypeDoc otherwise labels any imports of these classes as `default`.
|