@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 +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
|
|