@js-draw/math 1.22.0 → 1.24.1

Sign up to get free protection for your applications and to get access to all the features.
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