@js-draw/math 1.23.1 → 1.24.2
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.
- package/dist/cjs/Mat33.js +4 -2
- package/dist/cjs/Vec3.d.ts +2 -2
- package/dist/cjs/rounding/cleanUpNumber.js +1 -1
- package/dist/cjs/shapes/BezierJSWrapper.d.ts +7 -1
- package/dist/cjs/shapes/Rect2.js +5 -0
- package/dist/mjs/Mat33.mjs +4 -2
- package/dist/mjs/Vec3.d.ts +2 -2
- package/dist/mjs/rounding/cleanUpNumber.mjs +1 -1
- package/dist/mjs/shapes/BezierJSWrapper.d.ts +7 -1
- package/dist/mjs/shapes/Rect2.mjs +5 -0
- package/dist-test/test_imports/test-require.cjs +1 -0
- package/package.json +3 -3
- package/src/Mat33.fromCSSMatrix.test.ts +3 -0
- package/src/Mat33.ts +4 -2
- package/src/Vec3.ts +2 -2
- package/src/rounding/cleanUpNumber.test.ts +2 -0
- package/src/rounding/cleanUpNumber.ts +1 -1
- package/src/shapes/BezierJSWrapper.ts +9 -4
- package/src/shapes/Rect2.test.ts +4 -0
- package/src/shapes/Rect2.ts +5 -0
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 ([
|
509
|
-
const partRegex =
|
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) {
|
package/dist/cjs/Vec3.d.ts
CHANGED
@@ -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)
|
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]
|
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():
|
26
|
+
protected getBezier(): CorrectedBezierType;
|
21
27
|
signedDistance(point: Point2): number;
|
22
28
|
/**
|
23
29
|
* @returns the (more) exact distance from `point` to this.
|
package/dist/cjs/shapes/Rect2.js
CHANGED
@@ -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` */
|
package/dist/mjs/Mat33.mjs
CHANGED
@@ -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 ([
|
503
|
-
const partRegex =
|
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) {
|
package/dist/mjs/Vec3.d.ts
CHANGED
@@ -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)
|
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]
|
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():
|
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` */
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@js-draw/math",
|
3
|
-
"version": "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.
|
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": "
|
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 ([
|
612
|
-
const partRegex =
|
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)
|
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]
|
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:
|
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(
|
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`. */
|
package/src/shapes/Rect2.test.ts
CHANGED
@@ -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);
|
package/src/shapes/Rect2.ts
CHANGED
@@ -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
|
|