@js-draw/math 1.22.0 → 1.24.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 (49) hide show
  1. package/dist/cjs/Color4.d.ts +24 -1
  2. package/dist/cjs/Color4.js +33 -1
  3. package/dist/cjs/Mat33.d.ts +20 -0
  4. package/dist/cjs/Mat33.js +23 -1
  5. package/dist/cjs/Vec3.d.ts +14 -5
  6. package/dist/cjs/Vec3.js +11 -2
  7. package/dist/cjs/lib.d.ts +3 -0
  8. package/dist/cjs/lib.js +3 -0
  9. package/dist/cjs/rounding/cleanUpNumber.js +1 -1
  10. package/dist/cjs/shapes/BezierJSWrapper.d.ts +9 -1
  11. package/dist/cjs/shapes/BezierJSWrapper.js +2 -0
  12. package/dist/cjs/shapes/Path.d.ts +1 -0
  13. package/dist/cjs/shapes/Path.js +1 -0
  14. package/dist/cjs/shapes/QuadraticBezier.d.ts +19 -2
  15. package/dist/cjs/shapes/QuadraticBezier.js +26 -3
  16. package/dist/cjs/shapes/Rect2.d.ts +13 -0
  17. package/dist/cjs/shapes/Rect2.js +22 -1
  18. package/dist/mjs/Color4.d.ts +24 -1
  19. package/dist/mjs/Color4.mjs +33 -1
  20. package/dist/mjs/Mat33.d.ts +20 -0
  21. package/dist/mjs/Mat33.mjs +23 -1
  22. package/dist/mjs/Vec3.d.ts +14 -5
  23. package/dist/mjs/Vec3.mjs +11 -2
  24. package/dist/mjs/lib.d.ts +3 -0
  25. package/dist/mjs/lib.mjs +3 -0
  26. package/dist/mjs/rounding/cleanUpNumber.mjs +1 -1
  27. package/dist/mjs/shapes/BezierJSWrapper.d.ts +9 -1
  28. package/dist/mjs/shapes/BezierJSWrapper.mjs +2 -0
  29. package/dist/mjs/shapes/Path.d.ts +1 -0
  30. package/dist/mjs/shapes/Path.mjs +1 -0
  31. package/dist/mjs/shapes/QuadraticBezier.d.ts +19 -2
  32. package/dist/mjs/shapes/QuadraticBezier.mjs +26 -3
  33. package/dist/mjs/shapes/Rect2.d.ts +13 -0
  34. package/dist/mjs/shapes/Rect2.mjs +22 -1
  35. package/dist-test/test_imports/test-require.cjs +1 -0
  36. package/package.json +3 -3
  37. package/src/Color4.test.ts +5 -0
  38. package/src/Color4.ts +39 -1
  39. package/src/Mat33.fromCSSMatrix.test.ts +4 -1
  40. package/src/Mat33.test.ts +6 -6
  41. package/src/Mat33.ts +23 -1
  42. package/src/Vec3.ts +14 -5
  43. package/src/lib.ts +3 -0
  44. package/src/rounding/cleanUpNumber.test.ts +2 -0
  45. package/src/rounding/cleanUpNumber.ts +1 -1
  46. package/src/shapes/BezierJSWrapper.ts +11 -4
  47. package/src/shapes/Path.ts +1 -0
  48. package/src/shapes/QuadraticBezier.ts +22 -2
  49. package/src/shapes/Rect2.ts +17 -0
@@ -68,8 +68,8 @@ export interface Vec3 {
68
68
  *
69
69
  * This is equivalent to `Math.atan2(vec.y, vec.x)`.
70
70
  *
71
- * As such, observing that `Math.atan2(-0, -1)` $\approx -\pi$ and `Math.atan2(0, -1)`$\approx \pi$
72
- * the resultant angle is in the range $[-\pi, pi]$.
71
+ * As such, observing that `Math.atan2(-0, -1)` $\approx -\pi$ and `Math.atan2(0, -1)` $\approx \pi$
72
+ * the resultant angle is in the range $[-\pi, \pi]$.
73
73
  *
74
74
  * **Example**:
75
75
  * ```ts,runnable,console
@@ -150,7 +150,7 @@ export interface Vec3 {
150
150
  map(fn: (component: number, index: number) => number): Vec3;
151
151
  asArray(): [number, number, number];
152
152
  /**
153
- * [fuzz] The maximum difference between two components for this and [other]
153
+ * @param tolerance The maximum difference between two components for this and [other]
154
154
  * to be considered equal.
155
155
  *
156
156
  * @example
@@ -200,12 +200,16 @@ declare class Vec2Impl implements Vec3 {
200
200
  toString(): string;
201
201
  }
202
202
  /**
203
- * A `Vec2` is a `Vec3` optimized for working in a plane. As such, they have an
203
+ * A `Vec2` is a {@link Vec3} optimized for working in a plane. `Vec2`s have an
204
204
  * always-zero `z` component.
205
205
  *
206
206
  * ```ts,runnable,console
207
207
  * import { Vec2 } from '@js-draw/math';
208
- * console.log(Vec2.of(1, 2));
208
+ *
209
+ * const v = Vec2.of(1, 2);
210
+ * console.log('a Vec2:', v);
211
+ * console.log('x component:', v.x);
212
+ * console.log('z component:', v.z);
209
213
  * ```
210
214
  */
211
215
  export declare namespace Vec2 {
@@ -240,6 +244,7 @@ export declare namespace Vec2 {
240
244
  /** The zero vector: A vector with x=0, y=0. */
241
245
  const zero: Vec2Impl;
242
246
  }
247
+ /** Contains static methods for constructing a {@link Vec3}. */
243
248
  export declare namespace Vec3 {
244
249
  /**
245
250
  * Construct a vector from three components.
@@ -248,11 +253,15 @@ export declare namespace Vec3 {
248
253
  * ```ts,runnable,console
249
254
  * import { Vec3 } from '@js-draw/math';
250
255
  * const v1 = Vec3.of(1, 2, 3);
256
+ * console.log(v1.plus(Vec3.of(0, 100, 0)));
251
257
  * ```
252
258
  */
253
259
  const of: (x: number, y: number, z: number) => Vec3;
260
+ /** A unit vector in the x direction (`[1, 0, 0]`). */
254
261
  const unitX: Vec2Impl;
262
+ /** A unit vector in the y direction (`[0, 1, 0]`). */
255
263
  const unitY: Vec2Impl;
264
+ /** The zero vector (`[0, 0, 0]`). */
256
265
  const zero: Vec2Impl;
257
266
  /** A vector of length 1 in the z direction. */
258
267
  const unitZ: Vec3;
package/dist/mjs/Vec3.mjs CHANGED
@@ -224,12 +224,16 @@ class Vec2Impl {
224
224
  }
225
225
  }
226
226
  /**
227
- * A `Vec2` is a `Vec3` optimized for working in a plane. As such, they have an
227
+ * A `Vec2` is a {@link Vec3} optimized for working in a plane. `Vec2`s have an
228
228
  * always-zero `z` component.
229
229
  *
230
230
  * ```ts,runnable,console
231
231
  * import { Vec2 } from '@js-draw/math';
232
- * console.log(Vec2.of(1, 2));
232
+ *
233
+ * const v = Vec2.of(1, 2);
234
+ * console.log('a Vec2:', v);
235
+ * console.log('x component:', v.x);
236
+ * console.log('z component:', v.z);
233
237
  * ```
234
238
  */
235
239
  export var Vec2;
@@ -266,6 +270,7 @@ export var Vec2;
266
270
  /** The zero vector: A vector with x=0, y=0. */
267
271
  Vec2.zero = Vec2.of(0, 0);
268
272
  })(Vec2 || (Vec2 = {}));
273
+ /** Contains static methods for constructing a {@link Vec3}. */
269
274
  export var Vec3;
270
275
  (function (Vec3) {
271
276
  /**
@@ -275,6 +280,7 @@ export var Vec3;
275
280
  * ```ts,runnable,console
276
281
  * import { Vec3 } from '@js-draw/math';
277
282
  * const v1 = Vec3.of(1, 2, 3);
283
+ * console.log(v1.plus(Vec3.of(0, 100, 0)));
278
284
  * ```
279
285
  */
280
286
  Vec3.of = (x, y, z) => {
@@ -285,8 +291,11 @@ export var Vec3;
285
291
  return new Vec3Impl(x, y, z);
286
292
  }
287
293
  };
294
+ /** A unit vector in the x direction (`[1, 0, 0]`). */
288
295
  Vec3.unitX = Vec2.unitX;
296
+ /** A unit vector in the y direction (`[0, 1, 0]`). */
289
297
  Vec3.unitY = Vec2.unitY;
298
+ /** The zero vector (`[0, 0, 0]`). */
290
299
  Vec3.zero = Vec2.zero;
291
300
  /** A vector of length 1 in the z direction. */
292
301
  Vec3.unitZ = Vec3.of(0, 0, 1);
package/dist/mjs/lib.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * This package contains general math utilities used by `js-draw`.
3
+ * These include 2D and 3D vectors, 2D paths, and 3x3 matrices.
4
+ *
2
5
  * ```ts,runnable,console
3
6
  * import { Vec2, Mat33, Rect2 } from '@js-draw/math';
4
7
  *
package/dist/mjs/lib.mjs CHANGED
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * This package contains general math utilities used by `js-draw`.
3
+ * These include 2D and 3D vectors, 2D paths, and 3x3 matrices.
4
+ *
2
5
  * ```ts,runnable,console
3
6
  * import { Vec2, Mat33, Rect2 } from '@js-draw/math';
4
7
  *
@@ -11,7 +11,7 @@ export const cleanUpNumber = (text) => {
11
11
  const lastChar = text.charAt(text.length - 1);
12
12
  if (lastChar === '0' || lastChar === '.') {
13
13
  // Remove trailing zeroes
14
- text = text.replace(/([.]\d*[^0]+)0+$/, '$1');
14
+ text = text.replace(/([.]\d*[^0])0+$/, '$1');
15
15
  text = text.replace(/[.]0+$/, '.');
16
16
  // Remove trailing period
17
17
  text = text.replace(/[.]$/, '');
@@ -3,6 +3,12 @@ import { Point2, Vec2 } from '../Vec2';
3
3
  import LineSegment2 from './LineSegment2';
4
4
  import Rect2 from './Rect2';
5
5
  import Parameterized2DShape from './Parameterized2DShape';
6
+ interface CorrectedBezierType extends Bezier {
7
+ dderivative(t: number): {
8
+ x: number;
9
+ y: number;
10
+ };
11
+ }
6
12
  /**
7
13
  * A lazy-initializing wrapper around Bezier-js.
8
14
  *
@@ -17,7 +23,7 @@ export declare abstract class BezierJSWrapper extends Parameterized2DShape {
17
23
  protected constructor(bezierJsBezier?: Bezier);
18
24
  /** Returns the start, control points, and end point of this Bézier. */
19
25
  abstract getPoints(): readonly Point2[];
20
- protected getBezier(): Bezier;
26
+ protected getBezier(): CorrectedBezierType;
21
27
  signedDistance(point: Point2): number;
22
28
  /**
23
29
  * @returns the (more) exact distance from `point` to this.
@@ -29,8 +35,10 @@ export declare abstract class BezierJSWrapper extends Parameterized2DShape {
29
35
  * @returns the curve evaluated at `t`.
30
36
  */
31
37
  at(t: number): Point2;
38
+ /** @returns the curve's directional derivative at `t`. */
32
39
  derivativeAt(t: number): Point2;
33
40
  secondDerivativeAt(t: number): Point2;
41
+ /** @returns the [normal vector](https://en.wikipedia.org/wiki/Normal_(geometry)) to this curve at `t`. */
34
42
  normal(t: number): Vec2;
35
43
  normalAt(t: number): Vec2;
36
44
  tangentAt(t: number): Vec2;
@@ -57,12 +57,14 @@ export class BezierJSWrapper extends Parameterized2DShape {
57
57
  at(t) {
58
58
  return Vec2.ofXY(this.getBezier().get(t));
59
59
  }
60
+ /** @returns the curve's directional derivative at `t`. */
60
61
  derivativeAt(t) {
61
62
  return Vec2.ofXY(this.getBezier().derivative(t));
62
63
  }
63
64
  secondDerivativeAt(t) {
64
65
  return Vec2.ofXY(this.getBezier().dderivative(t));
65
66
  }
67
+ /** @returns the [normal vector](https://en.wikipedia.org/wiki/Normal_(geometry)) to this curve at `t`. */
66
68
  normal(t) {
67
69
  return Vec2.ofXY(this.getBezier().normal(t));
68
70
  }
@@ -3,6 +3,7 @@ import Mat33 from '../Mat33';
3
3
  import Rect2 from './Rect2';
4
4
  import { Point2 } from '../Vec2';
5
5
  import Parameterized2DShape from './Parameterized2DShape';
6
+ /** Identifiers for different path commands. These commands can make up a {@link Path}. */
6
7
  export declare enum PathCommandType {
7
8
  LineTo = 0,
8
9
  MoveTo = 1,
@@ -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";
@@ -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;
@@ -39,6 +59,7 @@ export class Rect2 extends Abstract2DShape {
39
59
  this.x + this.w >= other.x &&
40
60
  this.y + this.h >= other.y);
41
61
  }
62
+ /** @returns true iff `other` is completely within this `Rect2`. */
42
63
  containsRect(other) {
43
64
  return (this.x <= other.x &&
44
65
  this.y <= other.y &&
@@ -1,5 +1,6 @@
1
1
  console.log('Testing require()...');
2
2
 
3
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- This is a .cjs file
3
4
  const { Vec2, Color4, Mat33 } = require('@js-draw/math');
4
5
 
5
6
  if (Vec2.of(1, 1).x !== 1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@js-draw/math",
3
- "version": "1.22.0",
3
+ "version": "1.24.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.22.0",
30
+ "@js-draw/build-tool": "^1.24.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": "c922cf6e44d078133100e01383ba1bacdebe01bd"
47
+ "gitHead": "ef847374748e32d6d96d993a2236a99d9109a32c"
48
48
  }
@@ -86,4 +86,9 @@ describe('Color4', () => {
86
86
  expect(Color4.contrastRatio(colorA, colorB)).toBeCloseTo(expectedContrast, 1);
87
87
  }
88
88
  });
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
+ });
89
94
  });
package/src/Color4.ts CHANGED
@@ -37,6 +37,10 @@ export class Color4 {
37
37
  return Color4.ofRGBA(red, green, blue, 1.0);
38
38
  }
39
39
 
40
+ /**
41
+ * Creates a color from red, green, blue, and transparency components. Each component should
42
+ * be in the range $[0, 1]$.
43
+ */
40
44
  public static ofRGBA(red: number, green: number, blue: number, alpha: number): Color4 {
41
45
  red = Math.max(0, Math.min(red, 1));
42
46
  green = Math.max(0, Math.min(green, 1));
@@ -46,6 +50,40 @@ export class Color4 {
46
50
  return new Color4(red, green, blue, alpha);
47
51
  }
48
52
 
53
+ /**
54
+ * Creates a color from an RGB (or RGBA) array.
55
+ *
56
+ * This is similar to {@link ofRGB} and {@link ofRGBA}, but, by default, takes values
57
+ * that range from 0 to 255.
58
+ *
59
+ * If the array values instead range from 0-1, pass `maxValue` as `1`.
60
+ */
61
+ public static fromRGBArray(
62
+ array: Uint8Array | Uint8ClampedArray | number[],
63
+ maxValue: number = 255,
64
+ ) {
65
+ const red = array[0];
66
+ const green = array[1] ?? red;
67
+ const blue = array[2] ?? red;
68
+
69
+ let alpha = 255;
70
+ if (3 < array.length) {
71
+ alpha = array[3];
72
+ }
73
+
74
+ return Color4.ofRGBA(red / maxValue, green / maxValue, blue / maxValue, alpha / maxValue);
75
+ }
76
+
77
+ /**
78
+ * Creates a `Color4` from a three or four-component hexadecimal
79
+ * [color string](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet).
80
+ *
81
+ * Example:
82
+ * ```ts,runnable,console
83
+ * import { Color4 } from '@js-draw/math';
84
+ * console.log(Color4.fromHex('#ff0'));
85
+ * ```
86
+ */
49
87
  public static fromHex(hexString: string): Color4 {
50
88
  // Remove starting '#' (if present)
51
89
  hexString = (hexString.match(/^[#]?(.*)$/) ?? [])[1];
@@ -82,7 +120,7 @@ export class Color4 {
82
120
  return Color4.ofRGBA(components[0], components[1], components[2], components[3]);
83
121
  }
84
122
 
85
- /** Like fromHex, but can handle additional colors if an `HTMLCanvasElement` is available. */
123
+ /** Like {@link fromHex}, but can handle additional colors if an `HTMLCanvasElement` is available. */
86
124
  public static fromString(text: string): Color4 {
87
125
  if (text.startsWith('#')) {
88
126
  return Color4.fromHex(text);
@@ -2,7 +2,7 @@ import Mat33 from './Mat33';
2
2
  import { Vec2 } from './Vec2';
3
3
 
4
4
  describe('Mat33.fromCSSMatrix', () => {
5
- it('should convert CSS matrix(...) strings to matricies', () => {
5
+ it('should convert CSS matrix(...) strings to matrices', () => {
6
6
  // From MDN:
7
7
  // ⎡ a c e ⎤
8
8
  // ⎢ b d f ⎥ = matrix(a,b,c,d,e,f)
@@ -30,6 +30,9 @@ describe('Mat33.fromCSSMatrix', () => {
30
30
  expect(Mat33.fromCSSMatrix('matrix(-1, 2e6, 3E-2,-5.123, -6.5e-1, 0.01)')).objEq(
31
31
  new Mat33(-1, 3e-2, -6.5e-1, 2e6, -5.123, 0.01, 0, 0, 1),
32
32
  );
33
+ expect(Mat33.fromCSSMatrix('matrix(-1,\t2e6,3E-2,-5.123, -6.5e-1,\n0.01\n)')).objEq(
34
+ new Mat33(-1, 3e-2, -6.5e-1, 2e6, -5.123, 0.01, 0, 0, 1),
35
+ );
33
36
  });
34
37
 
35
38
  it('should convert multi-matrix arguments into a single CSS matrix', () => {
package/src/Mat33.test.ts CHANGED
@@ -39,7 +39,7 @@ describe('Mat33 tests', () => {
39
39
  expect(M.inverse().rightMul(M)).objEq(Mat33.identity, fuzz);
40
40
  });
41
41
 
42
- it('90 degree z-rotation matricies should rotate 90 degrees counter clockwise', () => {
42
+ it('90 degree z-rotation matrices should rotate 90 degrees counter clockwise', () => {
43
43
  const fuzz = 0.001;
44
44
 
45
45
  const M = Mat33.zRotation(Math.PI / 2);
@@ -48,7 +48,7 @@ describe('Mat33 tests', () => {
48
48
  expect(M.transformVec2(rotated)).objEq(Vec2.unitX.times(-1), fuzz);
49
49
  });
50
50
 
51
- it('z-rotation matricies should preserve the given origin', () => {
51
+ it('z-rotation matrices should preserve the given origin', () => {
52
52
  const testPairs: Array<[number, Vec2]> = [
53
53
  [Math.PI / 2, Vec2.zero],
54
54
  [-Math.PI / 2, Vec2.zero],
@@ -60,7 +60,7 @@ describe('Mat33 tests', () => {
60
60
  }
61
61
  });
62
62
 
63
- it('translation matricies should translate Vec2s', () => {
63
+ it('translation matrices should translate Vec2s', () => {
64
64
  const fuzz = 0.01;
65
65
 
66
66
  const M = Mat33.translation(Vec2.of(1, -4));
@@ -68,7 +68,7 @@ describe('Mat33 tests', () => {
68
68
  expect(M.transformVec2(Vec2.of(-1, 3))).objEq(Vec2.of(0, -1), fuzz);
69
69
  });
70
70
 
71
- it('scaling matricies should scale about the provided center', () => {
71
+ it('scaling matrices should scale about the provided center', () => {
72
72
  const fuzz = 0.01;
73
73
 
74
74
  const center = Vec2.of(1, -4);
@@ -77,14 +77,14 @@ describe('Mat33 tests', () => {
77
77
  expect(M.transformVec2(Vec2.of(0, 0))).objEq(Vec2.of(-1, 4), fuzz);
78
78
  });
79
79
 
80
- it('calling inverse on singular matricies should result in the identity matrix', () => {
80
+ it('calling inverse on singular matrices should result in the identity matrix', () => {
81
81
  const fuzz = 0.001;
82
82
  const singularMat = Mat33.ofRows(Vec3.of(0, 0, 1), Vec3.of(0, 1, 0), Vec3.of(0, 1, 1));
83
83
  expect(singularMat.invertable()).toBe(false);
84
84
  expect(singularMat.inverse()).objEq(Mat33.identity, fuzz);
85
85
  });
86
86
 
87
- it('z-rotation matricies should be invertable', () => {
87
+ it('z-rotation matrices should be invertable', () => {
88
88
  const fuzz = 0.01;
89
89
  const M = Mat33.zRotation(-0.2617993877991494, Vec2.of(481, 329.5));
90
90
  expect(M.inverse().transformVec2(M.transformVec2(Vec2.unitX))).objEq(Vec2.unitX, fuzz);
package/src/Mat33.ts CHANGED
@@ -440,6 +440,26 @@ export class Mat33 {
440
440
  return new Mat33(1, 0, amount.x, 0, 1, amount.y, 0, 0, 1);
441
441
  }
442
442
 
443
+ /**
444
+ * Creates a matrix for rotating `Vec2`s about `center` by some number of `radians`.
445
+ *
446
+ * For this function, {@link Vec2}s are considered to be points in 2D space.
447
+ *
448
+ * For example,
449
+ * ```ts,runnable,console
450
+ * import { Mat33, Vec2 } from '@js-draw/math';
451
+ *
452
+ * const halfCircle = Math.PI; // PI radians = 180 degrees = 1/2 circle
453
+ * const center = Vec2.of(1, 1); // The point (1,1)
454
+ * const rotationMatrix = Mat33.zRotation(halfCircle, center);
455
+ *
456
+ * console.log(
457
+ * 'Rotating (0,0) 180deg about', center, 'results in',
458
+ * // Rotates (0,0)
459
+ * rotationMatrix.transformVec2(Vec2.zero),
460
+ * );
461
+ * ```
462
+ */
443
463
  public static zRotation(radians: number, center: Point2 = Vec2.zero): Mat33 {
444
464
  if (radians === 0) {
445
465
  return Mat33.identity;
@@ -496,6 +516,8 @@ export class Mat33 {
496
516
  if (cssString === '' || cssString === 'none') {
497
517
  return Mat33.identity;
498
518
  }
519
+ // Normalize spacing
520
+ cssString = cssString.trim().replace(/\s+/g, ' ');
499
521
 
500
522
  const parseArguments = (argumentString: string): number[] => {
501
523
  const parsed = argumentString.split(/[, \t\n]+/g).map((argString) => {
@@ -589,7 +611,7 @@ export class Mat33 {
589
611
 
590
612
  // A command (\w+)
591
613
  // followed by a set of arguments ([ \t\n0-9eE.,\-%]+)
592
- const partRegex = /\s*(\w+)\s*\(([^)]*)\)/gi;
614
+ const partRegex = /(\w+)\s?\(([^)]*)\)/gi;
593
615
  let match;
594
616
  let matrix: Mat33 | null = null;
595
617
 
package/src/Vec3.ts CHANGED
@@ -72,8 +72,8 @@ export interface Vec3 {
72
72
  *
73
73
  * This is equivalent to `Math.atan2(vec.y, vec.x)`.
74
74
  *
75
- * As such, observing that `Math.atan2(-0, -1)` $\approx -\pi$ and `Math.atan2(0, -1)`$\approx \pi$
76
- * the resultant angle is in the range $[-\pi, pi]$.
75
+ * As such, observing that `Math.atan2(-0, -1)` $\approx -\pi$ and `Math.atan2(0, -1)` $\approx \pi$
76
+ * the resultant angle is in the range $[-\pi, \pi]$.
77
77
  *
78
78
  * **Example**:
79
79
  * ```ts,runnable,console
@@ -168,7 +168,7 @@ export interface Vec3 {
168
168
  asArray(): [number, number, number];
169
169
 
170
170
  /**
171
- * [fuzz] The maximum difference between two components for this and [other]
171
+ * @param tolerance The maximum difference between two components for this and [other]
172
172
  * to be considered equal.
173
173
  *
174
174
  * @example
@@ -481,12 +481,16 @@ class Vec2Impl implements Vec3 {
481
481
  }
482
482
 
483
483
  /**
484
- * A `Vec2` is a `Vec3` optimized for working in a plane. As such, they have an
484
+ * A `Vec2` is a {@link Vec3} optimized for working in a plane. `Vec2`s have an
485
485
  * always-zero `z` component.
486
486
  *
487
487
  * ```ts,runnable,console
488
488
  * import { Vec2 } from '@js-draw/math';
489
- * console.log(Vec2.of(1, 2));
489
+ *
490
+ * const v = Vec2.of(1, 2);
491
+ * console.log('a Vec2:', v);
492
+ * console.log('x component:', v.x);
493
+ * console.log('z component:', v.z);
490
494
  * ```
491
495
  */
492
496
  export namespace Vec2 {
@@ -527,6 +531,7 @@ export namespace Vec2 {
527
531
  export const zero = Vec2.of(0, 0);
528
532
  }
529
533
 
534
+ /** Contains static methods for constructing a {@link Vec3}. */
530
535
  export namespace Vec3 {
531
536
  /**
532
537
  * Construct a vector from three components.
@@ -535,6 +540,7 @@ export namespace Vec3 {
535
540
  * ```ts,runnable,console
536
541
  * import { Vec3 } from '@js-draw/math';
537
542
  * const v1 = Vec3.of(1, 2, 3);
543
+ * console.log(v1.plus(Vec3.of(0, 100, 0)));
538
544
  * ```
539
545
  */
540
546
  export const of = (x: number, y: number, z: number): Vec3 => {
@@ -545,8 +551,11 @@ export namespace Vec3 {
545
551
  }
546
552
  };
547
553
 
554
+ /** A unit vector in the x direction (`[1, 0, 0]`). */
548
555
  export const unitX = Vec2.unitX;
556
+ /** A unit vector in the y direction (`[0, 1, 0]`). */
549
557
  export const unitY = Vec2.unitY;
558
+ /** The zero vector (`[0, 0, 0]`). */
550
559
  export const zero = Vec2.zero;
551
560
 
552
561
  /** A vector of length 1 in the z direction. */
package/src/lib.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * This package contains general math utilities used by `js-draw`.
3
+ * These include 2D and 3D vectors, 2D paths, and 3x3 matrices.
4
+ *
2
5
  * ```ts,runnable,console
3
6
  * import { Vec2, Mat33, Rect2 } from '@js-draw/math';
4
7
  *
@@ -11,5 +11,7 @@ it('cleanUpNumber', () => {
11
11
  expect(cleanUpNumber('1234')).toBe('1234');
12
12
  expect(cleanUpNumber('1234.5')).toBe('1234.5');
13
13
  expect(cleanUpNumber('1234.500')).toBe('1234.5');
14
+ expect(cleanUpNumber('1234.00500')).toBe('1234.005');
15
+ expect(cleanUpNumber('1234.001234500')).toBe('1234.0012345');
14
16
  expect(cleanUpNumber('1.1368683772161603e-13')).toBe('0');
15
17
  });
@@ -13,7 +13,7 @@ export const cleanUpNumber = (text: string) => {
13
13
  const lastChar = text.charAt(text.length - 1);
14
14
  if (lastChar === '0' || lastChar === '.') {
15
15
  // Remove trailing zeroes
16
- text = text.replace(/([.]\d*[^0]+)0+$/, '$1');
16
+ text = text.replace(/([.]\d*[^0])0+$/, '$1');
17
17
  text = text.replace(/[.]0+$/, '.');
18
18
 
19
19
  // Remove trailing period