@js-draw/math 1.21.3 → 1.23.1
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.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
|
/**
|