@js-draw/math 1.23.1 → 1.24.2

Sign up to get free protection for your applications and to get access to all the features.
package/dist/cjs/Mat33.js CHANGED
@@ -430,6 +430,8 @@ class Mat33 {
430
430
  if (cssString === '' || cssString === 'none') {
431
431
  return Mat33.identity;
432
432
  }
433
+ // Normalize spacing
434
+ cssString = cssString.trim().replace(/\s+/g, ' ');
433
435
  const parseArguments = (argumentString) => {
434
436
  const parsed = argumentString.split(/[, \t\n]+/g).map((argString) => {
435
437
  // Handle trailing spaces/commands
@@ -505,8 +507,8 @@ class Mat33 {
505
507
  },
506
508
  };
507
509
  // A command (\w+)
508
- // followed by a set of arguments ([ \t\n0-9eE.,\-%]+)
509
- const partRegex = /\s*(\w+)\s*\(([^)]*)\)/gi;
510
+ // followed by a set of arguments ([^)]*)
511
+ const partRegex = /(?:^|\W)(\w+)\s?\(([^)]*)\)/gi;
510
512
  let match;
511
513
  let matrix = null;
512
514
  while ((match = partRegex.exec(cssString)) !== null) {
@@ -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
@@ -14,7 +14,7 @@ const cleanUpNumber = (text) => {
14
14
  const lastChar = text.charAt(text.length - 1);
15
15
  if (lastChar === '0' || lastChar === '.') {
16
16
  // Remove trailing zeroes
17
- text = text.replace(/([.]\d*[^0]+)0+$/, '$1');
17
+ text = text.replace(/([.]\d*[^0])0+$/, '$1');
18
18
  text = text.replace(/[.]0+$/, '.');
19
19
  // Remove trailing period
20
20
  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.
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Rect2 = void 0;
7
7
  const LineSegment2_1 = __importDefault(require("./LineSegment2"));
8
+ const Mat33_1 = __importDefault(require("../Mat33"));
8
9
  const Vec2_1 = require("../Vec2");
9
10
  const Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
10
11
  /**
@@ -244,6 +245,10 @@ class Rect2 extends Abstract2DShape_1.default {
244
245
  // [affineTransform] is a transformation matrix that both scales and **translates**.
245
246
  // the bounding box of this' four corners after transformed by the given affine transformation.
246
247
  transformedBoundingBox(affineTransform) {
248
+ // Optimize transforming by the identity matrix (a common case).
249
+ if (affineTransform === Mat33_1.default.identity) {
250
+ return this;
251
+ }
247
252
  return Rect2.bboxOf(this.corners.map((corner) => affineTransform.transformVec2(corner)));
248
253
  }
249
254
  /** @return true iff this is equal to `other ± tolerance` */
@@ -424,6 +424,8 @@ export class Mat33 {
424
424
  if (cssString === '' || cssString === 'none') {
425
425
  return Mat33.identity;
426
426
  }
427
+ // Normalize spacing
428
+ cssString = cssString.trim().replace(/\s+/g, ' ');
427
429
  const parseArguments = (argumentString) => {
428
430
  const parsed = argumentString.split(/[, \t\n]+/g).map((argString) => {
429
431
  // Handle trailing spaces/commands
@@ -499,8 +501,8 @@ export class Mat33 {
499
501
  },
500
502
  };
501
503
  // A command (\w+)
502
- // followed by a set of arguments ([ \t\n0-9eE.,\-%]+)
503
- const partRegex = /\s*(\w+)\s*\(([^)]*)\)/gi;
504
+ // followed by a set of arguments ([^)]*)
505
+ const partRegex = /(?:^|\W)(\w+)\s?\(([^)]*)\)/gi;
504
506
  let match;
505
507
  let matrix = null;
506
508
  while ((match = partRegex.exec(cssString)) !== null) {
@@ -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
@@ -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.
@@ -1,4 +1,5 @@
1
1
  import LineSegment2 from './LineSegment2.mjs';
2
+ import Mat33 from '../Mat33.mjs';
2
3
  import { Vec2 } from '../Vec2.mjs';
3
4
  import Abstract2DShape from './Abstract2DShape.mjs';
4
5
  /**
@@ -238,6 +239,10 @@ export class Rect2 extends Abstract2DShape {
238
239
  // [affineTransform] is a transformation matrix that both scales and **translates**.
239
240
  // the bounding box of this' four corners after transformed by the given affine transformation.
240
241
  transformedBoundingBox(affineTransform) {
242
+ // Optimize transforming by the identity matrix (a common case).
243
+ if (affineTransform === Mat33.identity) {
244
+ return this;
245
+ }
241
246
  return Rect2.bboxOf(this.corners.map((corner) => affineTransform.transformVec2(corner)));
242
247
  }
243
248
  /** @return true iff this is equal to `other ± tolerance` */
@@ -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.23.1",
3
+ "version": "1.24.2",
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.23.1",
30
+ "@js-draw/build-tool": "^1.24.2",
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": "e0bb3336d5f3a94533c823906778d39a4880f4cf"
47
+ "gitHead": "32c8db56fc8996c8d485118d1ee37077428344a3"
48
48
  }
@@ -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.ts CHANGED
@@ -516,6 +516,8 @@ export class Mat33 {
516
516
  if (cssString === '' || cssString === 'none') {
517
517
  return Mat33.identity;
518
518
  }
519
+ // Normalize spacing
520
+ cssString = cssString.trim().replace(/\s+/g, ' ');
519
521
 
520
522
  const parseArguments = (argumentString: string): number[] => {
521
523
  const parsed = argumentString.split(/[, \t\n]+/g).map((argString) => {
@@ -608,8 +610,8 @@ export class Mat33 {
608
610
  };
609
611
 
610
612
  // A command (\w+)
611
- // followed by a set of arguments ([ \t\n0-9eE.,\-%]+)
612
- const partRegex = /\s*(\w+)\s*\(([^)]*)\)/gi;
613
+ // followed by a set of arguments ([^)]*)
614
+ const partRegex = /(?:^|\W)(\w+)\s?\(([^)]*)\)/gi;
613
615
  let match;
614
616
  let matrix: Mat33 | null = null;
615
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
@@ -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
@@ -4,6 +4,11 @@ import LineSegment2 from './LineSegment2';
4
4
  import Rect2 from './Rect2';
5
5
  import Parameterized2DShape from './Parameterized2DShape';
6
6
 
7
+ // The typings for Bezier are incorrect in some cases:
8
+ interface CorrectedBezierType extends Bezier {
9
+ dderivative(t: number): { x: number; y: number };
10
+ }
11
+
7
12
  /**
8
13
  * A lazy-initializing wrapper around Bezier-js.
9
14
  *
@@ -14,13 +19,13 @@ import Parameterized2DShape from './Parameterized2DShape';
14
19
  * @internal
15
20
  */
16
21
  export abstract class BezierJSWrapper extends Parameterized2DShape {
17
- #bezierJs: Bezier | null = null;
22
+ #bezierJs: CorrectedBezierType | null = null;
18
23
 
19
24
  protected constructor(bezierJsBezier?: Bezier) {
20
25
  super();
21
26
 
22
27
  if (bezierJsBezier) {
23
- this.#bezierJs = bezierJsBezier;
28
+ this.#bezierJs = bezierJsBezier as CorrectedBezierType;
24
29
  }
25
30
  }
26
31
 
@@ -29,7 +34,7 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
29
34
 
30
35
  protected getBezier() {
31
36
  if (!this.#bezierJs) {
32
- this.#bezierJs = new Bezier(this.getPoints().map((p) => p.xy));
37
+ this.#bezierJs = new Bezier(this.getPoints().map((p) => p.xy)) as CorrectedBezierType;
33
38
  }
34
39
  return this.#bezierJs;
35
40
  }
@@ -62,7 +67,7 @@ export abstract class BezierJSWrapper extends Parameterized2DShape {
62
67
  }
63
68
 
64
69
  public secondDerivativeAt(t: number): Point2 {
65
- return Vec2.ofXY((this.getBezier() as any).dderivative(t));
70
+ return Vec2.ofXY(this.getBezier().dderivative(t));
66
71
  }
67
72
 
68
73
  /** @returns the [normal vector](https://en.wikipedia.org/wiki/Normal_(geometry)) to this curve at `t`. */
@@ -114,6 +114,10 @@ describe('Rect2', () => {
114
114
  });
115
115
 
116
116
  it('A transformed bounding box', () => {
117
+ expect(Rect2.unitSquare.transformedBoundingBox(Mat33.scaling2D(2))).objEq(
118
+ new Rect2(0, 0, 2, 2),
119
+ );
120
+
117
121
  const rotationMat = Mat33.zRotation(Math.PI / 4);
118
122
  const rect = Rect2.unitSquare.translatedBy(Vec2.of(-0.5, -0.5));
119
123
  const transformedBBox = rect.transformedBoundingBox(rotationMat);
@@ -310,6 +310,11 @@ export class Rect2 extends Abstract2DShape {
310
310
  // [affineTransform] is a transformation matrix that both scales and **translates**.
311
311
  // the bounding box of this' four corners after transformed by the given affine transformation.
312
312
  public transformedBoundingBox(affineTransform: Mat33): Rect2 {
313
+ // Optimize transforming by the identity matrix (a common case).
314
+ if (affineTransform === Mat33.identity) {
315
+ return this;
316
+ }
317
+
313
318
  return Rect2.bboxOf(this.corners.map((corner) => affineTransform.transformVec2(corner)));
314
319
  }
315
320