@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/dist/cjs/shapes/Path.js
CHANGED
@@ -13,6 +13,7 @@ const PointShape2D_1 = __importDefault(require("./PointShape2D"));
|
|
13
13
|
const toRoundedString_1 = __importDefault(require("../rounding/toRoundedString"));
|
14
14
|
const toStringOfSamePrecision_1 = __importDefault(require("../rounding/toStringOfSamePrecision"));
|
15
15
|
const convexHull2Of_1 = __importDefault(require("../utils/convexHull2Of"));
|
16
|
+
/** Identifiers for different path commands. These commands can make up a {@link Path}. */
|
16
17
|
var PathCommandType;
|
17
18
|
(function (PathCommandType) {
|
18
19
|
PathCommandType[PathCommandType["LineTo"] = 0] = "LineTo";
|
@@ -327,9 +328,7 @@ class Path {
|
|
327
328
|
const maxRaymarchSteps = 8;
|
328
329
|
// Start raymarching from each of these points. This allows detection of multiple
|
329
330
|
// intersections.
|
330
|
-
const startPoints = [
|
331
|
-
line.p1, ...additionalRaymarchStartPoints, line.p2
|
332
|
-
];
|
331
|
+
const startPoints = [line.p1, ...additionalRaymarchStartPoints, line.p2];
|
333
332
|
// Converts a point ON THE LINE to a parameter
|
334
333
|
const pointToParameter = (point) => {
|
335
334
|
// Because line.direction is a unit vector, this computes the length
|
@@ -435,7 +434,9 @@ class Path {
|
|
435
434
|
return [];
|
436
435
|
}
|
437
436
|
if (this.parts.length === 0) {
|
438
|
-
return new Path(this.startPoint, [
|
437
|
+
return new Path(this.startPoint, [
|
438
|
+
{ kind: PathCommandType.MoveTo, point: this.startPoint },
|
439
|
+
]).intersection(line, strokeRadius);
|
439
440
|
}
|
440
441
|
let index = 0;
|
441
442
|
for (const part of this.geometry) {
|
@@ -456,7 +457,7 @@ class Path {
|
|
456
457
|
const doRaymarching = strokeRadius && strokeRadius > 1e-8;
|
457
458
|
if (doRaymarching) {
|
458
459
|
// Starting points for raymarching (in addition to the end points of the line).
|
459
|
-
const startPoints = result.map(intersection => intersection.point);
|
460
|
+
const startPoints = result.map((intersection) => intersection.point);
|
460
461
|
result = this.raymarchIntersectionWith(line, strokeRadius, startPoints);
|
461
462
|
}
|
462
463
|
return result;
|
@@ -509,7 +510,8 @@ class Path {
|
|
509
510
|
*/
|
510
511
|
spliced(deleteFrom, deleteTo, insert, options) {
|
511
512
|
const isBeforeOrEqual = (a, b) => {
|
512
|
-
return a.curveIndex < b.curveIndex ||
|
513
|
+
return (a.curveIndex < b.curveIndex ||
|
514
|
+
(a.curveIndex === b.curveIndex && a.parameterValue <= b.parameterValue));
|
513
515
|
};
|
514
516
|
if (isBeforeOrEqual(deleteFrom, deleteTo)) {
|
515
517
|
// deleteFrom deleteTo
|
@@ -547,15 +549,15 @@ class Path {
|
|
547
549
|
//
|
548
550
|
// Bounds checking & reversal.
|
549
551
|
//
|
550
|
-
while (splitAt.length > 0
|
551
|
-
|
552
|
-
|
552
|
+
while (splitAt.length > 0 &&
|
553
|
+
splitAt[splitAt.length - 1].curveIndex >= this.parts.length - 1 &&
|
554
|
+
splitAt[splitAt.length - 1].parameterValue >= 1) {
|
553
555
|
splitAt.pop();
|
554
556
|
}
|
555
557
|
splitAt.reverse(); // .reverse() <-- We're `.pop`ing from the end
|
556
|
-
while (splitAt.length > 0
|
557
|
-
|
558
|
-
|
558
|
+
while (splitAt.length > 0 &&
|
559
|
+
splitAt[splitAt.length - 1].curveIndex <= 0 &&
|
560
|
+
splitAt[splitAt.length - 1].parameterValue <= 0) {
|
559
561
|
splitAt.pop();
|
560
562
|
}
|
561
563
|
if (splitAt.length === 0 || this.parts.length === 0) {
|
@@ -746,7 +748,7 @@ class Path {
|
|
746
748
|
if (affineTransfm.isIdentity()) {
|
747
749
|
return this;
|
748
750
|
}
|
749
|
-
return this.mapPoints(point => affineTransfm.transformVec2(point));
|
751
|
+
return this.mapPoints((point) => affineTransfm.transformVec2(point));
|
750
752
|
}
|
751
753
|
/**
|
752
754
|
* @internal
|
@@ -835,11 +837,10 @@ class Path {
|
|
835
837
|
});
|
836
838
|
lastPoint = part.endPoint;
|
837
839
|
break;
|
838
|
-
default:
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
}
|
840
|
+
default: {
|
841
|
+
const exhaustivenessCheck = part;
|
842
|
+
return exhaustivenessCheck;
|
843
|
+
}
|
843
844
|
}
|
844
845
|
}
|
845
846
|
newParts.reverse();
|
@@ -851,7 +852,8 @@ class Path {
|
|
851
852
|
return this.startPoint;
|
852
853
|
}
|
853
854
|
const lastPart = this.parts[this.parts.length - 1];
|
854
|
-
if (lastPart.kind === PathCommandType.QuadraticBezierTo ||
|
855
|
+
if (lastPart.kind === PathCommandType.QuadraticBezierTo ||
|
856
|
+
lastPart.kind === PathCommandType.CubicBezierTo) {
|
855
857
|
return lastPart.endPoint;
|
856
858
|
}
|
857
859
|
else {
|
@@ -960,9 +962,9 @@ class Path {
|
|
960
962
|
if (part1.kind !== part2.kind) {
|
961
963
|
return false;
|
962
964
|
}
|
963
|
-
else if (!part1.controlPoint1.eq(part2.controlPoint1, tolerance)
|
964
|
-
|
965
|
-
|
965
|
+
else if (!part1.controlPoint1.eq(part2.controlPoint1, tolerance) ||
|
966
|
+
!part1.controlPoint2.eq(part2.controlPoint2, tolerance) ||
|
967
|
+
!part1.endPoint.eq(part2.endPoint, tolerance)) {
|
966
968
|
return false;
|
967
969
|
}
|
968
970
|
break;
|
@@ -970,16 +972,15 @@ class Path {
|
|
970
972
|
if (part1.kind !== part2.kind) {
|
971
973
|
return false;
|
972
974
|
}
|
973
|
-
else if (!part1.controlPoint.eq(part2.controlPoint, tolerance)
|
974
|
-
|
975
|
+
else if (!part1.controlPoint.eq(part2.controlPoint, tolerance) ||
|
976
|
+
!part1.endPoint.eq(part2.endPoint, tolerance)) {
|
975
977
|
return false;
|
976
978
|
}
|
977
979
|
break;
|
978
|
-
default:
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
}
|
980
|
+
default: {
|
981
|
+
const exhaustivenessCheck = part1;
|
982
|
+
return exhaustivenessCheck;
|
983
|
+
}
|
983
984
|
}
|
984
985
|
}
|
985
986
|
return true;
|
@@ -1001,11 +1002,7 @@ class Path {
|
|
1001
1002
|
const cornerToEdge = Vec2_1.Vec2.of(lineWidth, lineWidth).times(0.5);
|
1002
1003
|
const innerRect = Rect2_1.default.fromCorners(rect.topLeft.plus(cornerToEdge), rect.bottomRight.minus(cornerToEdge));
|
1003
1004
|
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
|
-
];
|
1005
|
+
corners = [innerRect.corners[3], ...innerRect.corners, ...outerRect.corners.reverse()];
|
1009
1006
|
startPoint = outerRect.corners[3];
|
1010
1007
|
}
|
1011
1008
|
else {
|
@@ -1188,19 +1185,23 @@ class Path {
|
|
1188
1185
|
});
|
1189
1186
|
};
|
1190
1187
|
const commandArgCounts = {
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1188
|
+
m: 1,
|
1189
|
+
l: 1,
|
1190
|
+
c: 3,
|
1191
|
+
q: 2,
|
1192
|
+
z: 0,
|
1193
|
+
h: 1,
|
1194
|
+
v: 1,
|
1198
1195
|
};
|
1199
1196
|
// Each command: Command character followed by anything that isn't a command character
|
1200
|
-
const commandExp = /([MZLHVCSQTA])\s*([^MZLHVCSQTA]*)/
|
1197
|
+
const commandExp = /([MZLHVCSQTA])\s*([^MZLHVCSQTA]*)/gi;
|
1201
1198
|
let current;
|
1202
1199
|
while ((current = commandExp.exec(pathString)) !== null) {
|
1203
|
-
const argParts = current[2]
|
1200
|
+
const argParts = current[2]
|
1201
|
+
.trim()
|
1202
|
+
.split(/[^0-9Ee.-]/)
|
1203
|
+
.filter((part) => part.length > 0)
|
1204
|
+
.reduce((accumualtor, current) => {
|
1204
1205
|
// As of 09/2022, iOS Safari doesn't support support lookbehind in regular
|
1205
1206
|
// expressions. As such, we need an alternative.
|
1206
1207
|
// Because '-' can be used as a path separator, unless preceeded by an 'e' (as in 1e-5),
|
@@ -1210,10 +1211,10 @@ class Path {
|
|
1210
1211
|
if (parts[0] !== '') {
|
1211
1212
|
accumualtor.push(parts[0]);
|
1212
1213
|
}
|
1213
|
-
accumualtor.push(...parts.slice(1).map(part => `-${part}`));
|
1214
|
+
accumualtor.push(...parts.slice(1).map((part) => `-${part}`));
|
1214
1215
|
return accumualtor;
|
1215
1216
|
}, []);
|
1216
|
-
let numericArgs = argParts.map(arg => parseFloat(arg));
|
1217
|
+
let numericArgs = argParts.map((arg) => parseFloat(arg));
|
1217
1218
|
let commandChar = current[1].toLowerCase();
|
1218
1219
|
let uppercaseCommand = current[1] !== commandChar;
|
1219
1220
|
// Convert commands that don't take points into commands that do.
|
@@ -1241,7 +1242,8 @@ class Path {
|
|
1241
1242
|
commandChar = 'l';
|
1242
1243
|
}
|
1243
1244
|
const commandArgCount = commandArgCounts[commandChar] ?? 0;
|
1244
|
-
const allArgs = numericArgs
|
1245
|
+
const allArgs = numericArgs
|
1246
|
+
.reduce((accumulator, current, index, parts) => {
|
1245
1247
|
if (index % 2 !== 0) {
|
1246
1248
|
const currentAsFloat = current;
|
1247
1249
|
const prevAsFloat = parts[index - 1];
|
@@ -1250,7 +1252,8 @@ class Path {
|
|
1250
1252
|
else {
|
1251
1253
|
return accumulator;
|
1252
1254
|
}
|
1253
|
-
}, [])
|
1255
|
+
}, [])
|
1256
|
+
.map((coordinate, index) => {
|
1254
1257
|
// Lowercase commands are relative, uppercase commands use absolute
|
1255
1258
|
// positioning
|
1256
1259
|
let newPos;
|
@@ -2,9 +2,26 @@ import { Point2, Vec2 } from '../Vec2';
|
|
2
2
|
import BezierJSWrapper from './BezierJSWrapper';
|
3
3
|
import Rect2 from './Rect2';
|
4
4
|
/**
|
5
|
-
* Represents a 2D Bézier curve.
|
5
|
+
* Represents a 2D [Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
|
6
6
|
*
|
7
|
-
*
|
7
|
+
* Example:
|
8
|
+
* ```ts,runnable,console
|
9
|
+
* import { QuadraticBezier, Vec2 } from '@js-draw/math';
|
10
|
+
*
|
11
|
+
* const startPoint = Vec2.of(4, 3);
|
12
|
+
* const controlPoint = Vec2.of(1, 1);
|
13
|
+
* const endPoint = Vec2.of(1, 3);
|
14
|
+
*
|
15
|
+
* const curve = new QuadraticBezier(
|
16
|
+
* startPoint,
|
17
|
+
* controlPoint,
|
18
|
+
* endPoint,
|
19
|
+
* );
|
20
|
+
*
|
21
|
+
* console.log('Curve:', curve);
|
22
|
+
* ```
|
23
|
+
*
|
24
|
+
* **Note**: Some Bézier operations internally use the `bezier-js` library.
|
8
25
|
*/
|
9
26
|
export declare class QuadraticBezier extends BezierJSWrapper {
|
10
27
|
readonly p0: Point2;
|
@@ -9,12 +9,35 @@ const solveQuadratic_1 = __importDefault(require("../polynomial/solveQuadratic")
|
|
9
9
|
const BezierJSWrapper_1 = __importDefault(require("./BezierJSWrapper"));
|
10
10
|
const Rect2_1 = __importDefault(require("./Rect2"));
|
11
11
|
/**
|
12
|
-
* Represents a 2D Bézier curve.
|
12
|
+
* Represents a 2D [Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
|
13
13
|
*
|
14
|
-
*
|
14
|
+
* Example:
|
15
|
+
* ```ts,runnable,console
|
16
|
+
* import { QuadraticBezier, Vec2 } from '@js-draw/math';
|
17
|
+
*
|
18
|
+
* const startPoint = Vec2.of(4, 3);
|
19
|
+
* const controlPoint = Vec2.of(1, 1);
|
20
|
+
* const endPoint = Vec2.of(1, 3);
|
21
|
+
*
|
22
|
+
* const curve = new QuadraticBezier(
|
23
|
+
* startPoint,
|
24
|
+
* controlPoint,
|
25
|
+
* endPoint,
|
26
|
+
* );
|
27
|
+
*
|
28
|
+
* console.log('Curve:', curve);
|
29
|
+
* ```
|
30
|
+
*
|
31
|
+
* **Note**: Some Bézier operations internally use the `bezier-js` library.
|
15
32
|
*/
|
16
33
|
class QuadraticBezier extends BezierJSWrapper_1.default {
|
17
|
-
constructor(
|
34
|
+
constructor(
|
35
|
+
// Start point
|
36
|
+
p0,
|
37
|
+
// Control point
|
38
|
+
p1,
|
39
|
+
// End point
|
40
|
+
p2) {
|
18
41
|
super();
|
19
42
|
this.p0 = p0;
|
20
43
|
this.p1 = p1;
|
@@ -15,6 +15,18 @@ export interface RectTemplate {
|
|
15
15
|
/**
|
16
16
|
* Represents a rectangle in 2D space, parallel to the XY axes.
|
17
17
|
*
|
18
|
+
* **Example**:
|
19
|
+
* ```ts,runnable,console
|
20
|
+
* import { Rect2, Vec2 } from '@js-draw/math';
|
21
|
+
*
|
22
|
+
* const rect = Rect2.fromCorners(
|
23
|
+
* Vec2.of(0, 0),
|
24
|
+
* Vec2.of(10, 10),
|
25
|
+
* );
|
26
|
+
* console.log('area', rect.area);
|
27
|
+
* console.log('topLeft', rect.topLeft);
|
28
|
+
* ```
|
29
|
+
*
|
18
30
|
* `invariant: w ≥ 0, h ≥ 0, immutable`
|
19
31
|
*/
|
20
32
|
export declare class Rect2 extends Abstract2DShape {
|
@@ -29,6 +41,7 @@ export declare class Rect2 extends Abstract2DShape {
|
|
29
41
|
translatedBy(vec: Vec2): Rect2;
|
30
42
|
resizedTo(size: Vec2): Rect2;
|
31
43
|
containsPoint(other: Point2): boolean;
|
44
|
+
/** @returns true iff `other` is completely within this `Rect2`. */
|
32
45
|
containsRect(other: Rect2): boolean;
|
33
46
|
/**
|
34
47
|
* @returns true iff this and `other` overlap
|
package/dist/cjs/shapes/Rect2.js
CHANGED
@@ -10,10 +10,30 @@ const Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
|
|
10
10
|
/**
|
11
11
|
* Represents a rectangle in 2D space, parallel to the XY axes.
|
12
12
|
*
|
13
|
+
* **Example**:
|
14
|
+
* ```ts,runnable,console
|
15
|
+
* import { Rect2, Vec2 } from '@js-draw/math';
|
16
|
+
*
|
17
|
+
* const rect = Rect2.fromCorners(
|
18
|
+
* Vec2.of(0, 0),
|
19
|
+
* Vec2.of(10, 10),
|
20
|
+
* );
|
21
|
+
* console.log('area', rect.area);
|
22
|
+
* console.log('topLeft', rect.topLeft);
|
23
|
+
* ```
|
24
|
+
*
|
13
25
|
* `invariant: w ≥ 0, h ≥ 0, immutable`
|
14
26
|
*/
|
15
27
|
class Rect2 extends Abstract2DShape_1.default {
|
16
|
-
constructor(
|
28
|
+
constructor(
|
29
|
+
// Top left x coordinate
|
30
|
+
x,
|
31
|
+
// Top left y coordinate
|
32
|
+
y,
|
33
|
+
// Width
|
34
|
+
w,
|
35
|
+
// Height
|
36
|
+
h) {
|
17
37
|
super();
|
18
38
|
this.x = x;
|
19
39
|
this.y = y;
|
@@ -40,13 +60,17 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
40
60
|
return new Rect2(this.x, this.y, size.x, size.y);
|
41
61
|
}
|
42
62
|
containsPoint(other) {
|
43
|
-
return this.x <= other.x &&
|
44
|
-
|
63
|
+
return (this.x <= other.x &&
|
64
|
+
this.y <= other.y &&
|
65
|
+
this.x + this.w >= other.x &&
|
66
|
+
this.y + this.h >= other.y);
|
45
67
|
}
|
68
|
+
/** @returns true iff `other` is completely within this `Rect2`. */
|
46
69
|
containsRect(other) {
|
47
|
-
return this.x <= other.x &&
|
48
|
-
|
49
|
-
|
70
|
+
return (this.x <= other.x &&
|
71
|
+
this.y <= other.y &&
|
72
|
+
this.x + this.w >= other.x + other.w &&
|
73
|
+
this.y + this.h >= other.y + other.h);
|
50
74
|
}
|
51
75
|
/**
|
52
76
|
* @returns true iff this and `other` overlap
|
@@ -131,7 +155,7 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
131
155
|
return new Rect2(this.x - margin, this.y - margin, this.w + margin * 2, this.h + margin * 2);
|
132
156
|
}
|
133
157
|
getClosestPointOnBoundaryTo(target) {
|
134
|
-
const closestEdgePoints = this.getEdges().map(edge => {
|
158
|
+
const closestEdgePoints = this.getEdges().map((edge) => {
|
135
159
|
return edge.closestPointTo(target);
|
136
160
|
});
|
137
161
|
let closest = null;
|
@@ -158,15 +182,10 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
158
182
|
return false;
|
159
183
|
}
|
160
184
|
const squareRadius = radius * radius;
|
161
|
-
return this.corners.every(corner => corner.minus(point).magnitudeSquared() < squareRadius);
|
185
|
+
return this.corners.every((corner) => corner.minus(point).magnitudeSquared() < squareRadius);
|
162
186
|
}
|
163
187
|
get corners() {
|
164
|
-
return [
|
165
|
-
this.bottomRight,
|
166
|
-
this.topRight,
|
167
|
-
this.topLeft,
|
168
|
-
this.bottomLeft,
|
169
|
-
];
|
188
|
+
return [this.bottomRight, this.topRight, this.topLeft, this.bottomLeft];
|
170
189
|
}
|
171
190
|
get maxDimension() {
|
172
191
|
return Math.max(this.w, this.h);
|
@@ -207,7 +226,7 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
207
226
|
const result = [];
|
208
227
|
for (const edge of this.getEdges()) {
|
209
228
|
const intersection = edge.intersectsLineSegment(lineSegment);
|
210
|
-
intersection.forEach(point => result.push(point));
|
229
|
+
intersection.forEach((point) => result.push(point));
|
211
230
|
}
|
212
231
|
return result;
|
213
232
|
}
|
@@ -225,7 +244,7 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
225
244
|
// [affineTransform] is a transformation matrix that both scales and **translates**.
|
226
245
|
// the bounding box of this' four corners after transformed by the given affine transformation.
|
227
246
|
transformedBoundingBox(affineTransform) {
|
228
|
-
return Rect2.bboxOf(this.corners.map(corner => affineTransform.transformVec2(corner)));
|
247
|
+
return Rect2.bboxOf(this.corners.map((corner) => affineTransform.transformVec2(corner)));
|
229
248
|
}
|
230
249
|
/** @return true iff this is equal to `other ± tolerance` */
|
231
250
|
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.
|
package/dist/mjs/Color4.d.ts
CHANGED
@@ -28,9 +28,32 @@ export declare class Color4 {
|
|
28
28
|
* Each component should be in the range [0, 1].
|
29
29
|
*/
|
30
30
|
static ofRGB(red: number, green: number, blue: number): Color4;
|
31
|
+
/**
|
32
|
+
* Creates a color from red, green, blue, and transparency components. Each component should
|
33
|
+
* be in the range $[0, 1]$.
|
34
|
+
*/
|
31
35
|
static ofRGBA(red: number, green: number, blue: number, alpha: number): Color4;
|
36
|
+
/**
|
37
|
+
* Creates a color from an RGB (or RGBA) array.
|
38
|
+
*
|
39
|
+
* This is similar to {@link ofRGB} and {@link ofRGBA}, but, by default, takes values
|
40
|
+
* that range from 0 to 255.
|
41
|
+
*
|
42
|
+
* If the array values instead range from 0-1, pass `maxValue` as `1`.
|
43
|
+
*/
|
44
|
+
static fromRGBArray(array: Uint8Array | Uint8ClampedArray | number[], maxValue?: number): Color4;
|
45
|
+
/**
|
46
|
+
* Creates a `Color4` from a three or four-component hexadecimal
|
47
|
+
* [color string](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet).
|
48
|
+
*
|
49
|
+
* Example:
|
50
|
+
* ```ts,runnable,console
|
51
|
+
* import { Color4 } from '@js-draw/math';
|
52
|
+
* console.log(Color4.fromHex('#ff0'));
|
53
|
+
* ```
|
54
|
+
*/
|
32
55
|
static fromHex(hexString: string): Color4;
|
33
|
-
/** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
56
|
+
/** Like {@link fromHex}, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
34
57
|
static fromString(text: string): Color4;
|
35
58
|
/** @returns true if `this` and `other` are approximately equal. */
|
36
59
|
eq(other: Color4 | null | undefined): boolean;
|
package/dist/mjs/Color4.mjs
CHANGED
@@ -36,6 +36,10 @@ export class Color4 {
|
|
36
36
|
static ofRGB(red, green, blue) {
|
37
37
|
return Color4.ofRGBA(red, green, blue, 1.0);
|
38
38
|
}
|
39
|
+
/**
|
40
|
+
* Creates a color from red, green, blue, and transparency components. Each component should
|
41
|
+
* be in the range $[0, 1]$.
|
42
|
+
*/
|
39
43
|
static ofRGBA(red, green, blue, alpha) {
|
40
44
|
red = Math.max(0, Math.min(red, 1));
|
41
45
|
green = Math.max(0, Math.min(green, 1));
|
@@ -43,6 +47,34 @@ export class Color4 {
|
|
43
47
|
alpha = Math.max(0, Math.min(alpha, 1));
|
44
48
|
return new Color4(red, green, blue, alpha);
|
45
49
|
}
|
50
|
+
/**
|
51
|
+
* Creates a color from an RGB (or RGBA) array.
|
52
|
+
*
|
53
|
+
* This is similar to {@link ofRGB} and {@link ofRGBA}, but, by default, takes values
|
54
|
+
* that range from 0 to 255.
|
55
|
+
*
|
56
|
+
* If the array values instead range from 0-1, pass `maxValue` as `1`.
|
57
|
+
*/
|
58
|
+
static fromRGBArray(array, maxValue = 255) {
|
59
|
+
const red = array[0];
|
60
|
+
const green = array[1] ?? red;
|
61
|
+
const blue = array[2] ?? red;
|
62
|
+
let alpha = 255;
|
63
|
+
if (3 < array.length) {
|
64
|
+
alpha = array[3];
|
65
|
+
}
|
66
|
+
return Color4.ofRGBA(red / maxValue, green / maxValue, blue / maxValue, alpha / maxValue);
|
67
|
+
}
|
68
|
+
/**
|
69
|
+
* Creates a `Color4` from a three or four-component hexadecimal
|
70
|
+
* [color string](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet).
|
71
|
+
*
|
72
|
+
* Example:
|
73
|
+
* ```ts,runnable,console
|
74
|
+
* import { Color4 } from '@js-draw/math';
|
75
|
+
* console.log(Color4.fromHex('#ff0'));
|
76
|
+
* ```
|
77
|
+
*/
|
46
78
|
static fromHex(hexString) {
|
47
79
|
// Remove starting '#' (if present)
|
48
80
|
hexString = (hexString.match(/^[#]?(.*)$/) ?? [])[1];
|
@@ -55,7 +87,7 @@ export class Color4 {
|
|
55
87
|
// Each character is a component
|
56
88
|
const components = hexString.split('');
|
57
89
|
// Convert to RRGGBBAA or RRGGBB format
|
58
|
-
hexString = components.map(component => `${component}0`).join('');
|
90
|
+
hexString = components.map((component) => `${component}0`).join('');
|
59
91
|
}
|
60
92
|
if (hexString.length === 6) {
|
61
93
|
// Alpha component
|
@@ -71,7 +103,7 @@ export class Color4 {
|
|
71
103
|
}
|
72
104
|
return Color4.ofRGBA(components[0], components[1], components[2], components[3]);
|
73
105
|
}
|
74
|
-
/** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
106
|
+
/** Like {@link fromHex}, but can handle additional colors if an `HTMLCanvasElement` is available. */
|
75
107
|
static fromString(text) {
|
76
108
|
if (text.startsWith('#')) {
|
77
109
|
return Color4.fromHex(text);
|
@@ -165,7 +197,7 @@ export class Color4 {
|
|
165
197
|
// - https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
|
166
198
|
// - https://stackoverflow.com/a/9733420
|
167
199
|
// Normalize the components, as per above
|
168
|
-
const components = [this.r, this.g, this.b].map(component => {
|
200
|
+
const components = [this.r, this.g, this.b].map((component) => {
|
169
201
|
if (component < 0.03928) {
|
170
202
|
return component / 12.92;
|
171
203
|
}
|
package/dist/mjs/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
|
@@ -216,6 +206,26 @@ export declare class Mat33 {
|
|
216
206
|
* $$
|
217
207
|
*/
|
218
208
|
static translation(amount: Vec2): Mat33;
|
209
|
+
/**
|
210
|
+
* Creates a matrix for rotating `Vec2`s about `center` by some number of `radians`.
|
211
|
+
*
|
212
|
+
* For this function, {@link Vec2}s are considered to be points in 2D space.
|
213
|
+
*
|
214
|
+
* For example,
|
215
|
+
* ```ts,runnable,console
|
216
|
+
* import { Mat33, Vec2 } from '@js-draw/math';
|
217
|
+
*
|
218
|
+
* const halfCircle = Math.PI; // PI radians = 180 degrees = 1/2 circle
|
219
|
+
* const center = Vec2.of(1, 1); // The point (1,1)
|
220
|
+
* const rotationMatrix = Mat33.zRotation(halfCircle, center);
|
221
|
+
*
|
222
|
+
* console.log(
|
223
|
+
* 'Rotating (0,0) 180deg about', center, 'results in',
|
224
|
+
* // Rotates (0,0)
|
225
|
+
* rotationMatrix.transformVec2(Vec2.zero),
|
226
|
+
* );
|
227
|
+
* ```
|
228
|
+
*/
|
219
229
|
static zRotation(radians: number, center?: Point2): Mat33;
|
220
230
|
static scaling2D(amount: number | Vec2, center?: Point2): Mat33;
|
221
231
|
/**
|