@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
@@ -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
+ });