@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.
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
  /**