@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
@@ -7,6 +7,7 @@ import PointShape2D from './PointShape2D.mjs';
7
7
  import toRoundedString from '../rounding/toRoundedString.mjs';
8
8
  import toStringOfSamePrecision from '../rounding/toStringOfSamePrecision.mjs';
9
9
  import convexHull2Of from '../utils/convexHull2Of.mjs';
10
+ /** Identifiers for different path commands. These commands can make up a {@link Path}. */
10
11
  export var PathCommandType;
11
12
  (function (PathCommandType) {
12
13
  PathCommandType[PathCommandType["LineTo"] = 0] = "LineTo";
@@ -319,9 +320,7 @@ export class Path {
319
320
  const maxRaymarchSteps = 8;
320
321
  // Start raymarching from each of these points. This allows detection of multiple
321
322
  // intersections.
322
- const startPoints = [
323
- line.p1, ...additionalRaymarchStartPoints, line.p2
324
- ];
323
+ const startPoints = [line.p1, ...additionalRaymarchStartPoints, line.p2];
325
324
  // Converts a point ON THE LINE to a parameter
326
325
  const pointToParameter = (point) => {
327
326
  // Because line.direction is a unit vector, this computes the length
@@ -427,7 +426,9 @@ export class Path {
427
426
  return [];
428
427
  }
429
428
  if (this.parts.length === 0) {
430
- return new Path(this.startPoint, [{ kind: PathCommandType.MoveTo, point: this.startPoint }]).intersection(line, strokeRadius);
429
+ return new Path(this.startPoint, [
430
+ { kind: PathCommandType.MoveTo, point: this.startPoint },
431
+ ]).intersection(line, strokeRadius);
431
432
  }
432
433
  let index = 0;
433
434
  for (const part of this.geometry) {
@@ -448,7 +449,7 @@ export class Path {
448
449
  const doRaymarching = strokeRadius && strokeRadius > 1e-8;
449
450
  if (doRaymarching) {
450
451
  // Starting points for raymarching (in addition to the end points of the line).
451
- const startPoints = result.map(intersection => intersection.point);
452
+ const startPoints = result.map((intersection) => intersection.point);
452
453
  result = this.raymarchIntersectionWith(line, strokeRadius, startPoints);
453
454
  }
454
455
  return result;
@@ -501,7 +502,8 @@ export class Path {
501
502
  */
502
503
  spliced(deleteFrom, deleteTo, insert, options) {
503
504
  const isBeforeOrEqual = (a, b) => {
504
- return a.curveIndex < b.curveIndex || (a.curveIndex === b.curveIndex && a.parameterValue <= b.parameterValue);
505
+ return (a.curveIndex < b.curveIndex ||
506
+ (a.curveIndex === b.curveIndex && a.parameterValue <= b.parameterValue));
505
507
  };
506
508
  if (isBeforeOrEqual(deleteFrom, deleteTo)) {
507
509
  // deleteFrom deleteTo
@@ -539,15 +541,15 @@ export class Path {
539
541
  //
540
542
  // Bounds checking & reversal.
541
543
  //
542
- while (splitAt.length > 0
543
- && splitAt[splitAt.length - 1].curveIndex >= this.parts.length - 1
544
- && splitAt[splitAt.length - 1].parameterValue >= 1) {
544
+ while (splitAt.length > 0 &&
545
+ splitAt[splitAt.length - 1].curveIndex >= this.parts.length - 1 &&
546
+ splitAt[splitAt.length - 1].parameterValue >= 1) {
545
547
  splitAt.pop();
546
548
  }
547
549
  splitAt.reverse(); // .reverse() <-- We're `.pop`ing from the end
548
- while (splitAt.length > 0
549
- && splitAt[splitAt.length - 1].curveIndex <= 0
550
- && splitAt[splitAt.length - 1].parameterValue <= 0) {
550
+ while (splitAt.length > 0 &&
551
+ splitAt[splitAt.length - 1].curveIndex <= 0 &&
552
+ splitAt[splitAt.length - 1].parameterValue <= 0) {
551
553
  splitAt.pop();
552
554
  }
553
555
  if (splitAt.length === 0 || this.parts.length === 0) {
@@ -738,7 +740,7 @@ export class Path {
738
740
  if (affineTransfm.isIdentity()) {
739
741
  return this;
740
742
  }
741
- return this.mapPoints(point => affineTransfm.transformVec2(point));
743
+ return this.mapPoints((point) => affineTransfm.transformVec2(point));
742
744
  }
743
745
  /**
744
746
  * @internal
@@ -827,11 +829,10 @@ export class Path {
827
829
  });
828
830
  lastPoint = part.endPoint;
829
831
  break;
830
- default:
831
- {
832
- const exhaustivenessCheck = part;
833
- return exhaustivenessCheck;
834
- }
832
+ default: {
833
+ const exhaustivenessCheck = part;
834
+ return exhaustivenessCheck;
835
+ }
835
836
  }
836
837
  }
837
838
  newParts.reverse();
@@ -843,7 +844,8 @@ export class Path {
843
844
  return this.startPoint;
844
845
  }
845
846
  const lastPart = this.parts[this.parts.length - 1];
846
- if (lastPart.kind === PathCommandType.QuadraticBezierTo || lastPart.kind === PathCommandType.CubicBezierTo) {
847
+ if (lastPart.kind === PathCommandType.QuadraticBezierTo ||
848
+ lastPart.kind === PathCommandType.CubicBezierTo) {
847
849
  return lastPart.endPoint;
848
850
  }
849
851
  else {
@@ -952,9 +954,9 @@ export class Path {
952
954
  if (part1.kind !== part2.kind) {
953
955
  return false;
954
956
  }
955
- else if (!part1.controlPoint1.eq(part2.controlPoint1, tolerance)
956
- || !part1.controlPoint2.eq(part2.controlPoint2, tolerance)
957
- || !part1.endPoint.eq(part2.endPoint, tolerance)) {
957
+ else if (!part1.controlPoint1.eq(part2.controlPoint1, tolerance) ||
958
+ !part1.controlPoint2.eq(part2.controlPoint2, tolerance) ||
959
+ !part1.endPoint.eq(part2.endPoint, tolerance)) {
958
960
  return false;
959
961
  }
960
962
  break;
@@ -962,16 +964,15 @@ export class Path {
962
964
  if (part1.kind !== part2.kind) {
963
965
  return false;
964
966
  }
965
- else if (!part1.controlPoint.eq(part2.controlPoint, tolerance)
966
- || !part1.endPoint.eq(part2.endPoint, tolerance)) {
967
+ else if (!part1.controlPoint.eq(part2.controlPoint, tolerance) ||
968
+ !part1.endPoint.eq(part2.endPoint, tolerance)) {
967
969
  return false;
968
970
  }
969
971
  break;
970
- default:
971
- {
972
- const exhaustivenessCheck = part1;
973
- return exhaustivenessCheck;
974
- }
972
+ default: {
973
+ const exhaustivenessCheck = part1;
974
+ return exhaustivenessCheck;
975
+ }
975
976
  }
976
977
  }
977
978
  return true;
@@ -993,11 +994,7 @@ export class Path {
993
994
  const cornerToEdge = Vec2.of(lineWidth, lineWidth).times(0.5);
994
995
  const innerRect = Rect2.fromCorners(rect.topLeft.plus(cornerToEdge), rect.bottomRight.minus(cornerToEdge));
995
996
  const outerRect = Rect2.fromCorners(rect.topLeft.minus(cornerToEdge), rect.bottomRight.plus(cornerToEdge));
996
- corners = [
997
- innerRect.corners[3],
998
- ...innerRect.corners,
999
- ...outerRect.corners.reverse(),
1000
- ];
997
+ corners = [innerRect.corners[3], ...innerRect.corners, ...outerRect.corners.reverse()];
1001
998
  startPoint = outerRect.corners[3];
1002
999
  }
1003
1000
  else {
@@ -1180,19 +1177,23 @@ export class Path {
1180
1177
  });
1181
1178
  };
1182
1179
  const commandArgCounts = {
1183
- 'm': 1,
1184
- 'l': 1,
1185
- 'c': 3,
1186
- 'q': 2,
1187
- 'z': 0,
1188
- 'h': 1,
1189
- 'v': 1,
1180
+ m: 1,
1181
+ l: 1,
1182
+ c: 3,
1183
+ q: 2,
1184
+ z: 0,
1185
+ h: 1,
1186
+ v: 1,
1190
1187
  };
1191
1188
  // Each command: Command character followed by anything that isn't a command character
1192
- const commandExp = /([MZLHVCSQTA])\s*([^MZLHVCSQTA]*)/ig;
1189
+ const commandExp = /([MZLHVCSQTA])\s*([^MZLHVCSQTA]*)/gi;
1193
1190
  let current;
1194
1191
  while ((current = commandExp.exec(pathString)) !== null) {
1195
- const argParts = current[2].trim().split(/[^0-9Ee.-]/).filter(part => part.length > 0).reduce((accumualtor, current) => {
1192
+ const argParts = current[2]
1193
+ .trim()
1194
+ .split(/[^0-9Ee.-]/)
1195
+ .filter((part) => part.length > 0)
1196
+ .reduce((accumualtor, current) => {
1196
1197
  // As of 09/2022, iOS Safari doesn't support support lookbehind in regular
1197
1198
  // expressions. As such, we need an alternative.
1198
1199
  // Because '-' can be used as a path separator, unless preceeded by an 'e' (as in 1e-5),
@@ -1202,10 +1203,10 @@ export class Path {
1202
1203
  if (parts[0] !== '') {
1203
1204
  accumualtor.push(parts[0]);
1204
1205
  }
1205
- accumualtor.push(...parts.slice(1).map(part => `-${part}`));
1206
+ accumualtor.push(...parts.slice(1).map((part) => `-${part}`));
1206
1207
  return accumualtor;
1207
1208
  }, []);
1208
- let numericArgs = argParts.map(arg => parseFloat(arg));
1209
+ let numericArgs = argParts.map((arg) => parseFloat(arg));
1209
1210
  let commandChar = current[1].toLowerCase();
1210
1211
  let uppercaseCommand = current[1] !== commandChar;
1211
1212
  // Convert commands that don't take points into commands that do.
@@ -1233,7 +1234,8 @@ export class Path {
1233
1234
  commandChar = 'l';
1234
1235
  }
1235
1236
  const commandArgCount = commandArgCounts[commandChar] ?? 0;
1236
- const allArgs = numericArgs.reduce((accumulator, current, index, parts) => {
1237
+ const allArgs = numericArgs
1238
+ .reduce((accumulator, current, index, parts) => {
1237
1239
  if (index % 2 !== 0) {
1238
1240
  const currentAsFloat = current;
1239
1241
  const prevAsFloat = parts[index - 1];
@@ -1242,7 +1244,8 @@ export class Path {
1242
1244
  else {
1243
1245
  return accumulator;
1244
1246
  }
1245
- }, []).map((coordinate, index) => {
1247
+ }, [])
1248
+ .map((coordinate, index) => {
1246
1249
  // Lowercase commands are relative, uppercase commands use absolute
1247
1250
  // positioning
1248
1251
  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;
@@ -3,12 +3,35 @@ import solveQuadratic from '../polynomial/solveQuadratic.mjs';
3
3
  import BezierJSWrapper from './BezierJSWrapper.mjs';
4
4
  import Rect2 from './Rect2.mjs';
5
5
  /**
6
- * Represents a 2D Bézier curve.
6
+ * Represents a 2D [Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
7
7
  *
8
- * **Note**: Many Bézier operations use `bezier-js`'s.
8
+ * Example:
9
+ * ```ts,runnable,console
10
+ * import { QuadraticBezier, Vec2 } from '@js-draw/math';
11
+ *
12
+ * const startPoint = Vec2.of(4, 3);
13
+ * const controlPoint = Vec2.of(1, 1);
14
+ * const endPoint = Vec2.of(1, 3);
15
+ *
16
+ * const curve = new QuadraticBezier(
17
+ * startPoint,
18
+ * controlPoint,
19
+ * endPoint,
20
+ * );
21
+ *
22
+ * console.log('Curve:', curve);
23
+ * ```
24
+ *
25
+ * **Note**: Some Bézier operations internally use the `bezier-js` library.
9
26
  */
10
27
  export class QuadraticBezier extends BezierJSWrapper {
11
- constructor(p0, p1, p2) {
28
+ constructor(
29
+ // Start point
30
+ p0,
31
+ // Control point
32
+ p1,
33
+ // End point
34
+ p2) {
12
35
  super();
13
36
  this.p0 = p0;
14
37
  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
@@ -4,10 +4,30 @@ import Abstract2DShape from './Abstract2DShape.mjs';
4
4
  /**
5
5
  * Represents a rectangle in 2D space, parallel to the XY axes.
6
6
  *
7
+ * **Example**:
8
+ * ```ts,runnable,console
9
+ * import { Rect2, Vec2 } from '@js-draw/math';
10
+ *
11
+ * const rect = Rect2.fromCorners(
12
+ * Vec2.of(0, 0),
13
+ * Vec2.of(10, 10),
14
+ * );
15
+ * console.log('area', rect.area);
16
+ * console.log('topLeft', rect.topLeft);
17
+ * ```
18
+ *
7
19
  * `invariant: w ≥ 0, h ≥ 0, immutable`
8
20
  */
9
21
  export class Rect2 extends Abstract2DShape {
10
- constructor(x, y, w, h) {
22
+ constructor(
23
+ // Top left x coordinate
24
+ x,
25
+ // Top left y coordinate
26
+ y,
27
+ // Width
28
+ w,
29
+ // Height
30
+ h) {
11
31
  super();
12
32
  this.x = x;
13
33
  this.y = y;
@@ -34,13 +54,17 @@ export class Rect2 extends Abstract2DShape {
34
54
  return new Rect2(this.x, this.y, size.x, size.y);
35
55
  }
36
56
  containsPoint(other) {
37
- return this.x <= other.x && this.y <= other.y
38
- && this.x + this.w >= other.x && this.y + this.h >= other.y;
57
+ return (this.x <= other.x &&
58
+ this.y <= other.y &&
59
+ this.x + this.w >= other.x &&
60
+ this.y + this.h >= other.y);
39
61
  }
62
+ /** @returns true iff `other` is completely within this `Rect2`. */
40
63
  containsRect(other) {
41
- return this.x <= other.x && this.y <= other.y
42
- && this.x + this.w >= other.x + other.w
43
- && this.y + this.h >= other.y + other.h;
64
+ return (this.x <= other.x &&
65
+ this.y <= other.y &&
66
+ this.x + this.w >= other.x + other.w &&
67
+ this.y + this.h >= other.y + other.h);
44
68
  }
45
69
  /**
46
70
  * @returns true iff this and `other` overlap
@@ -125,7 +149,7 @@ export class Rect2 extends Abstract2DShape {
125
149
  return new Rect2(this.x - margin, this.y - margin, this.w + margin * 2, this.h + margin * 2);
126
150
  }
127
151
  getClosestPointOnBoundaryTo(target) {
128
- const closestEdgePoints = this.getEdges().map(edge => {
152
+ const closestEdgePoints = this.getEdges().map((edge) => {
129
153
  return edge.closestPointTo(target);
130
154
  });
131
155
  let closest = null;
@@ -152,15 +176,10 @@ export class Rect2 extends Abstract2DShape {
152
176
  return false;
153
177
  }
154
178
  const squareRadius = radius * radius;
155
- return this.corners.every(corner => corner.minus(point).magnitudeSquared() < squareRadius);
179
+ return this.corners.every((corner) => corner.minus(point).magnitudeSquared() < squareRadius);
156
180
  }
157
181
  get corners() {
158
- return [
159
- this.bottomRight,
160
- this.topRight,
161
- this.topLeft,
162
- this.bottomLeft,
163
- ];
182
+ return [this.bottomRight, this.topRight, this.topLeft, this.bottomLeft];
164
183
  }
165
184
  get maxDimension() {
166
185
  return Math.max(this.w, this.h);
@@ -201,7 +220,7 @@ export class Rect2 extends Abstract2DShape {
201
220
  const result = [];
202
221
  for (const edge of this.getEdges()) {
203
222
  const intersection = edge.intersectsLineSegment(lineSegment);
204
- intersection.forEach(point => result.push(point));
223
+ intersection.forEach((point) => result.push(point));
205
224
  }
206
225
  return result;
207
226
  }
@@ -219,7 +238,7 @@ export class Rect2 extends Abstract2DShape {
219
238
  // [affineTransform] is a transformation matrix that both scales and **translates**.
220
239
  // the bounding box of this' four corners after transformed by the given affine transformation.
221
240
  transformedBoundingBox(affineTransform) {
222
- return Rect2.bboxOf(this.corners.map(corner => affineTransform.transformVec2(corner)));
241
+ return Rect2.bboxOf(this.corners.map((corner) => affineTransform.transformVec2(corner)));
223
242
  }
224
243
  /** @return true iff this is equal to `other ± tolerance` */
225
244
  eq(other, tolerance = 0) {
@@ -39,12 +39,12 @@ class Triangle extends Abstract2DShape {
39
39
  }
40
40
  // Transform, treating this as composed of 2D points.
41
41
  transformed2DBy(affineTransform) {
42
- return this.map(vertex => affineTransform.transformVec2(vertex));
42
+ return this.map((vertex) => affineTransform.transformVec2(vertex));
43
43
  }
44
44
  // Transforms this by a linear transform --- verticies are treated as
45
45
  // 3D points.
46
46
  transformedBy(linearTransform) {
47
- return this.map(vertex => linearTransform.transformVec3(vertex));
47
+ return this.map((vertex) => linearTransform.transformVec3(vertex));
48
48
  }
49
49
  /**
50
50
  * Returns the sides of this triangle, as an array of `LineSegment2`s.
@@ -66,8 +66,7 @@ class Triangle extends Abstract2DShape {
66
66
  intersectsLineSegment(lineSegment) {
67
67
  const result = [];
68
68
  for (const edge of this.getEdges()) {
69
- edge.intersectsLineSegment(lineSegment)
70
- .forEach(point => result.push(point));
69
+ edge.intersectsLineSegment(lineSegment).forEach((point) => result.push(point));
71
70
  }
72
71
  return result;
73
72
  }
@@ -101,7 +100,7 @@ class Triangle extends Abstract2DShape {
101
100
  */
102
101
  signedDistance(point) {
103
102
  const sides = this.getEdges();
104
- const distances = sides.map(side => side.distance(point));
103
+ const distances = sides.map((side) => side.distance(point));
105
104
  const distance = Math.min(...distances);
106
105
  // If the point is in this' interior, signedDistance must return a negative
107
106
  // number.
@@ -10,9 +10,9 @@ const convexHull2Of = (points) => {
10
10
  return [];
11
11
  }
12
12
  // 1. Start with a vertex on the hull
13
- const lowestPoint = points.reduce((lowest, current) => current.y < lowest.y ? current : lowest, points[0]);
13
+ const lowestPoint = points.reduce((lowest, current) => (current.y < lowest.y ? current : lowest), points[0]);
14
14
  const vertices = [lowestPoint];
15
- let toProcess = [...points.filter(p => !p.eq(lowestPoint))];
15
+ let toProcess = [...points.filter((p) => !p.eq(lowestPoint))];
16
16
  let lastBaseDirection = Vec2.of(-1, 0);
17
17
  // 2. Find the point with greatest angle from the vertex:
18
18
  //
@@ -38,7 +38,7 @@ const convexHull2Of = (points) => {
38
38
  smallestDotProductSoFar = currentDotProduct;
39
39
  }
40
40
  }
41
- toProcess = toProcess.filter(p => !p.eq(furthestPointSoFar));
41
+ toProcess = toProcess.filter((p) => !p.eq(furthestPointSoFar));
42
42
  const newBaseDirection = furthestPointSoFar.minus(lastVertex).normalized();
43
43
  // If the last vertex is on the same edge as the current, there's no need to include
44
44
  // the previous one.
@@ -12,4 +12,4 @@ if (!Mat33.identity) {
12
12
 
13
13
  if (!Color4.red) {
14
14
  throw new Error('Failed to import Color4 from js-draw');
15
- }
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@js-draw/math",
3
- "version": "1.21.3",
3
+ "version": "1.23.1",
4
4
  "description": "A math library for js-draw. ",
5
5
  "types": "./dist/mjs/lib.d.ts",
6
6
  "main": "./dist/cjs/lib.js",
@@ -27,7 +27,7 @@
27
27
  "bezier-js": "6.1.3"
28
28
  },
29
29
  "devDependencies": {
30
- "@js-draw/build-tool": "^1.21.3",
30
+ "@js-draw/build-tool": "^1.23.1",
31
31
  "@types/bezier-js": "4.1.0",
32
32
  "@types/jest": "29.5.5",
33
33
  "@types/jsdom": "21.1.3"
@@ -44,5 +44,5 @@
44
44
  "svg",
45
45
  "math"
46
46
  ],
47
- "gitHead": "2b11a1efafeeb3b84effb7d849343d273df371f8"
47
+ "gitHead": "e0bb3336d5f3a94533c823906778d39a4880f4cf"
48
48
  }
@@ -15,7 +15,7 @@ describe('Color4', () => {
15
15
  expect(Color4.fromString('rgb(0, 0, 0)')).objEq(Color4.black);
16
16
  expect(Color4.fromString('rgb ( 255, 0,\t 0)')).objEq(Color4.ofRGBA(1, 0, 0, 1));
17
17
  expect(Color4.fromString('rgba ( 255, 0,\t 0, 0.5)')).objEq(Color4.ofRGBA(1, 0, 0, 0.5));
18
- expect(Color4.fromString('rgba( 0, 0, 128, 0)')).objEq(Color4.ofRGBA(0, 0, 128/255, 0));
18
+ expect(Color4.fromString('rgba( 0, 0, 128, 0)')).objEq(Color4.ofRGBA(0, 0, 128 / 255, 0));
19
19
  });
20
20
 
21
21
  it('should parse transparent/none as completely transparent', () => {
@@ -29,13 +29,11 @@ describe('Color4', () => {
29
29
  });
30
30
 
31
31
  it('should mix red and green to get yellow', () => {
32
- expect(Color4.ofRGB(1, 0, 0).mix(Color4.ofRGB(0, 1, 0), 0.3)).objEq(
33
- Color4.ofRGB(0.7, 0.3, 0)
34
- );
32
+ expect(Color4.ofRGB(1, 0, 0).mix(Color4.ofRGB(0, 1, 0), 0.3)).objEq(Color4.ofRGB(0.7, 0.3, 0));
35
33
  });
36
34
 
37
35
  it('should mix red with nothing and get red', () => {
38
- expect(Color4.average([ Color4.red ])).objEq(Color4.red);
36
+ expect(Color4.average([Color4.red])).objEq(Color4.red);
39
37
  });
40
38
 
41
39
  it('different colors should be different', () => {
@@ -47,14 +45,11 @@ describe('Color4', () => {
47
45
  it('should correctly convert to hsv', () => {
48
46
  expect(Color4.red.asHSV()).objEq(Vec3.of(0, 1, 1));
49
47
  expect(Color4.ofRGB(0.5, 0.5, 0.5).asHSV()).objEq(Vec3.of(0, 0, 0.5));
50
- expect(Color4.ofRGB(0.5, 0.25, 0.5).asHSV()).objEq(Vec3.of(Math.PI * 5 / 3, 0.5, 0.5), 0.1);
48
+ expect(Color4.ofRGB(0.5, 0.25, 0.5).asHSV()).objEq(Vec3.of((Math.PI * 5) / 3, 0.5, 0.5), 0.1);
51
49
  });
52
50
 
53
51
  it('fromHSV(color.asHSV) should return the original color', () => {
54
- const testColors = [
55
- Color4.red, Color4.green, Color4.blue,
56
- Color4.white, Color4.black,
57
- ];
52
+ const testColors = [Color4.red, Color4.green, Color4.blue, Color4.white, Color4.black];
58
53
 
59
54
  const testWithColor = (color: Color4) => {
60
55
  expect(Color4.fromHSV(...color.asHSV().asArray())).objEq(color);
@@ -65,8 +60,8 @@ describe('Color4', () => {
65
60
  }
66
61
 
67
62
  for (let i = 0; i <= 6; i++) {
68
- testWithColor(Color4.fromHSV(i * Math.PI / 7, 0.5, 0.5));
69
- testWithColor(Color4.fromHSV(i * Math.PI / 6, 0.5, 0.5));
63
+ testWithColor(Color4.fromHSV((i * Math.PI) / 7, 0.5, 0.5));
64
+ testWithColor(Color4.fromHSV((i * Math.PI) / 6, 0.5, 0.5));
70
65
  }
71
66
  });
72
67
 
@@ -78,17 +73,22 @@ describe('Color4', () => {
78
73
 
79
74
  it('should return correct contrast ratios', () => {
80
75
  // Expected values from https://webaim.org/resources/contrastchecker/
81
- const testCases: [ Color4, Color4, number ][] = [
82
- [ Color4.white, Color4.black, 21 ],
83
- [ Color4.fromHex('#FF0000'), Color4.black, 5.25 ],
84
- [ Color4.fromHex('#FF0000'), Color4.fromHex('#0000FF'), 2.14 ],
85
- [ Color4.fromHex('#300000'), Color4.fromHex('#003000'), 1.26 ],
86
- [ Color4.fromHex('#300000'), Color4.fromHex('#003000'), 1.26 ],
87
- [ Color4.fromHex('#D60000'), Color4.fromHex('#003000'), 2.71 ],
76
+ const testCases: [Color4, Color4, number][] = [
77
+ [Color4.white, Color4.black, 21],
78
+ [Color4.fromHex('#FF0000'), Color4.black, 5.25],
79
+ [Color4.fromHex('#FF0000'), Color4.fromHex('#0000FF'), 2.14],
80
+ [Color4.fromHex('#300000'), Color4.fromHex('#003000'), 1.26],
81
+ [Color4.fromHex('#300000'), Color4.fromHex('#003000'), 1.26],
82
+ [Color4.fromHex('#D60000'), Color4.fromHex('#003000'), 2.71],
88
83
  ];
89
84
 
90
- for (const [ colorA, colorB, expectedContrast ] of testCases) {
85
+ for (const [colorA, colorB, expectedContrast] of testCases) {
91
86
  expect(Color4.contrastRatio(colorA, colorB)).toBeCloseTo(expectedContrast, 1);
92
87
  }
93
88
  });
94
- });
89
+
90
+ it('should support creating colors from an RGBA array', () => {
91
+ expect(Color4.fromRGBArray([255, 0, 0])).objEq(Color4.ofRGB(1, 0, 0));
92
+ expect(Color4.fromRGBArray([255, 0, 0, 128])).objEq(Color4.ofRGBA(1, 0, 0, 0.5));
93
+ });
94
+ });