@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.
Files changed (81) hide show
  1. package/build-config.json +1 -1
  2. package/dist/cjs/Color4.d.ts +24 -1
  3. package/dist/cjs/Color4.js +35 -3
  4. package/dist/cjs/Mat33.d.ts +21 -11
  5. package/dist/cjs/Mat33.js +28 -24
  6. package/dist/cjs/Vec3.d.ts +12 -3
  7. package/dist/cjs/Vec3.js +20 -9
  8. package/dist/cjs/lib.d.ts +3 -0
  9. package/dist/cjs/lib.js +3 -0
  10. package/dist/cjs/shapes/BezierJSWrapper.d.ts +2 -0
  11. package/dist/cjs/shapes/BezierJSWrapper.js +22 -13
  12. package/dist/cjs/shapes/LineSegment2.js +13 -17
  13. package/dist/cjs/shapes/Parameterized2DShape.js +1 -1
  14. package/dist/cjs/shapes/Path.d.ts +1 -0
  15. package/dist/cjs/shapes/Path.js +50 -47
  16. package/dist/cjs/shapes/QuadraticBezier.d.ts +19 -2
  17. package/dist/cjs/shapes/QuadraticBezier.js +26 -3
  18. package/dist/cjs/shapes/Rect2.d.ts +13 -0
  19. package/dist/cjs/shapes/Rect2.js +35 -16
  20. package/dist/cjs/shapes/Triangle.js +4 -5
  21. package/dist/cjs/utils/convexHull2Of.js +3 -3
  22. package/dist/mjs/Color4.d.ts +24 -1
  23. package/dist/mjs/Color4.mjs +35 -3
  24. package/dist/mjs/Mat33.d.ts +21 -11
  25. package/dist/mjs/Mat33.mjs +28 -24
  26. package/dist/mjs/Vec3.d.ts +12 -3
  27. package/dist/mjs/Vec3.mjs +20 -9
  28. package/dist/mjs/lib.d.ts +3 -0
  29. package/dist/mjs/lib.mjs +3 -0
  30. package/dist/mjs/shapes/BezierJSWrapper.d.ts +2 -0
  31. package/dist/mjs/shapes/BezierJSWrapper.mjs +22 -13
  32. package/dist/mjs/shapes/LineSegment2.mjs +13 -17
  33. package/dist/mjs/shapes/Parameterized2DShape.mjs +1 -1
  34. package/dist/mjs/shapes/Path.d.ts +1 -0
  35. package/dist/mjs/shapes/Path.mjs +50 -47
  36. package/dist/mjs/shapes/QuadraticBezier.d.ts +19 -2
  37. package/dist/mjs/shapes/QuadraticBezier.mjs +26 -3
  38. package/dist/mjs/shapes/Rect2.d.ts +13 -0
  39. package/dist/mjs/shapes/Rect2.mjs +35 -16
  40. package/dist/mjs/shapes/Triangle.mjs +4 -5
  41. package/dist/mjs/utils/convexHull2Of.mjs +3 -3
  42. package/dist-test/test_imports/test-require.cjs +1 -1
  43. package/package.json +3 -3
  44. package/src/Color4.test.ts +21 -21
  45. package/src/Color4.ts +61 -18
  46. package/src/Mat33.fromCSSMatrix.test.ts +32 -46
  47. package/src/Mat33.test.ts +64 -102
  48. package/src/Mat33.ts +81 -104
  49. package/src/Vec2.test.ts +3 -3
  50. package/src/Vec3.test.ts +2 -3
  51. package/src/Vec3.ts +46 -61
  52. package/src/lib.ts +3 -2
  53. package/src/polynomial/solveQuadratic.test.ts +39 -13
  54. package/src/polynomial/solveQuadratic.ts +5 -6
  55. package/src/rounding/cleanUpNumber.test.ts +1 -1
  56. package/src/rounding/constants.ts +1 -3
  57. package/src/rounding/getLenAfterDecimal.ts +1 -2
  58. package/src/rounding/lib.ts +1 -2
  59. package/src/rounding/toRoundedString.test.ts +1 -1
  60. package/src/rounding/toStringOfSamePrecision.test.ts +1 -2
  61. package/src/rounding/toStringOfSamePrecision.ts +1 -1
  62. package/src/shapes/BezierJSWrapper.ts +56 -37
  63. package/src/shapes/CubicBezier.ts +3 -3
  64. package/src/shapes/LineSegment2.test.ts +24 -17
  65. package/src/shapes/LineSegment2.ts +26 -29
  66. package/src/shapes/Parameterized2DShape.ts +5 -4
  67. package/src/shapes/Path.fromString.test.ts +5 -5
  68. package/src/shapes/Path.test.ts +122 -120
  69. package/src/shapes/Path.toString.test.ts +7 -7
  70. package/src/shapes/Path.ts +379 -352
  71. package/src/shapes/PointShape2D.ts +3 -3
  72. package/src/shapes/QuadraticBezier.test.ts +27 -21
  73. package/src/shapes/QuadraticBezier.ts +26 -11
  74. package/src/shapes/Rect2.test.ts +44 -75
  75. package/src/shapes/Rect2.ts +47 -35
  76. package/src/shapes/Triangle.test.ts +31 -29
  77. package/src/shapes/Triangle.ts +17 -18
  78. package/src/utils/convexHull2Of.test.ts +54 -15
  79. package/src/utils/convexHull2Of.ts +9 -7
  80. package/tsconfig.json +1 -3
  81. package/typedoc.json +2 -2
@@ -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, [{ kind: PathCommandType.MoveTo, point: this.startPoint }]).intersection(line, strokeRadius);
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 || (a.curveIndex === b.curveIndex && a.parameterValue <= b.parameterValue);
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
- && splitAt[splitAt.length - 1].curveIndex >= this.parts.length - 1
552
- && splitAt[splitAt.length - 1].parameterValue >= 1) {
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
- && splitAt[splitAt.length - 1].curveIndex <= 0
558
- && splitAt[splitAt.length - 1].parameterValue <= 0) {
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
- const exhaustivenessCheck = part;
841
- return exhaustivenessCheck;
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 || lastPart.kind === PathCommandType.CubicBezierTo) {
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
- || !part1.controlPoint2.eq(part2.controlPoint2, tolerance)
965
- || !part1.endPoint.eq(part2.endPoint, tolerance)) {
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
- || !part1.endPoint.eq(part2.endPoint, tolerance)) {
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
- const exhaustivenessCheck = part1;
981
- return exhaustivenessCheck;
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
- 'm': 1,
1192
- 'l': 1,
1193
- 'c': 3,
1194
- 'q': 2,
1195
- 'z': 0,
1196
- 'h': 1,
1197
- 'v': 1,
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]*)/ig;
1197
+ const commandExp = /([MZLHVCSQTA])\s*([^MZLHVCSQTA]*)/gi;
1201
1198
  let current;
1202
1199
  while ((current = commandExp.exec(pathString)) !== null) {
1203
- const argParts = current[2].trim().split(/[^0-9Ee.-]/).filter(part => part.length > 0).reduce((accumualtor, current) => {
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.reduce((accumulator, current, index, parts) => {
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
- }, []).map((coordinate, index) => {
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
- * **Note**: Many Bézier operations use `bezier-js`'s.
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
- * **Note**: Many Bézier operations use `bezier-js`'s.
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(p0, p1, p2) {
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
@@ -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(x, y, w, h) {
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 && this.y <= other.y
44
- && this.x + this.w >= other.x && this.y + this.h >= other.y;
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 && this.y <= other.y
48
- && this.x + this.w >= other.x + other.w
49
- && this.y + this.h >= other.y + other.h;
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.
@@ -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;
@@ -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
  }
@@ -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
  /**