@js-draw/math 1.11.1 → 1.17.0
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/Vec3.d.ts +21 -0
- package/dist/cjs/Vec3.js +28 -0
- package/dist/cjs/lib.d.ts +2 -2
- package/dist/cjs/lib.js +16 -3
- package/dist/cjs/rounding/cleanUpNumber.d.ts +3 -0
- package/dist/cjs/rounding/cleanUpNumber.js +35 -0
- package/dist/cjs/rounding/constants.d.ts +1 -0
- package/dist/cjs/rounding/constants.js +4 -0
- package/dist/cjs/rounding/getLenAfterDecimal.d.ts +10 -0
- package/dist/cjs/rounding/getLenAfterDecimal.js +30 -0
- package/dist/cjs/rounding/lib.d.ts +1 -0
- package/dist/cjs/rounding/lib.js +5 -0
- package/dist/cjs/{rounding.d.ts → rounding/toRoundedString.d.ts} +1 -3
- package/dist/cjs/rounding/toRoundedString.js +54 -0
- package/dist/cjs/rounding/toStringOfSamePrecision.d.ts +2 -0
- package/dist/cjs/rounding/toStringOfSamePrecision.js +58 -0
- package/dist/cjs/rounding/toStringOfSamePrecision.test.d.ts +1 -0
- package/dist/cjs/shapes/Abstract2DShape.d.ts +3 -0
- package/dist/cjs/shapes/BezierJSWrapper.d.ts +15 -5
- package/dist/cjs/shapes/BezierJSWrapper.js +135 -18
- package/dist/cjs/shapes/LineSegment2.d.ts +34 -5
- package/dist/cjs/shapes/LineSegment2.js +63 -10
- package/dist/cjs/shapes/Parameterized2DShape.d.ts +31 -0
- package/dist/cjs/shapes/Parameterized2DShape.js +15 -0
- package/dist/cjs/shapes/Path.d.ts +40 -6
- package/dist/cjs/shapes/Path.js +181 -22
- package/dist/cjs/shapes/PointShape2D.d.ts +14 -3
- package/dist/cjs/shapes/PointShape2D.js +28 -5
- package/dist/cjs/shapes/QuadraticBezier.d.ts +4 -0
- package/dist/cjs/shapes/QuadraticBezier.js +19 -4
- package/dist/cjs/shapes/Rect2.d.ts +3 -0
- package/dist/cjs/shapes/Rect2.js +4 -1
- package/dist/mjs/Vec3.d.ts +21 -0
- package/dist/mjs/Vec3.mjs +28 -0
- package/dist/mjs/lib.d.ts +2 -2
- package/dist/mjs/lib.mjs +1 -1
- package/dist/mjs/rounding/cleanUpNumber.d.ts +3 -0
- package/dist/mjs/rounding/cleanUpNumber.mjs +31 -0
- package/dist/mjs/rounding/cleanUpNumber.test.d.ts +1 -0
- package/dist/mjs/rounding/constants.d.ts +1 -0
- package/dist/mjs/rounding/constants.mjs +1 -0
- package/dist/mjs/rounding/getLenAfterDecimal.d.ts +10 -0
- package/dist/mjs/rounding/getLenAfterDecimal.mjs +26 -0
- package/dist/mjs/rounding/lib.d.ts +1 -0
- package/dist/mjs/rounding/lib.mjs +1 -0
- package/dist/mjs/{rounding.d.ts → rounding/toRoundedString.d.ts} +1 -3
- package/dist/mjs/rounding/toRoundedString.mjs +47 -0
- package/dist/mjs/rounding/toRoundedString.test.d.ts +1 -0
- package/dist/mjs/rounding/toStringOfSamePrecision.d.ts +2 -0
- package/dist/mjs/rounding/toStringOfSamePrecision.mjs +51 -0
- package/dist/mjs/rounding/toStringOfSamePrecision.test.d.ts +1 -0
- package/dist/mjs/shapes/Abstract2DShape.d.ts +3 -0
- package/dist/mjs/shapes/BezierJSWrapper.d.ts +15 -5
- package/dist/mjs/shapes/BezierJSWrapper.mjs +133 -18
- package/dist/mjs/shapes/LineSegment2.d.ts +34 -5
- package/dist/mjs/shapes/LineSegment2.mjs +63 -10
- package/dist/mjs/shapes/Parameterized2DShape.d.ts +31 -0
- package/dist/mjs/shapes/Parameterized2DShape.mjs +8 -0
- package/dist/mjs/shapes/Path.d.ts +40 -6
- package/dist/mjs/shapes/Path.mjs +175 -16
- package/dist/mjs/shapes/PointShape2D.d.ts +14 -3
- package/dist/mjs/shapes/PointShape2D.mjs +28 -5
- package/dist/mjs/shapes/QuadraticBezier.d.ts +4 -0
- package/dist/mjs/shapes/QuadraticBezier.mjs +19 -4
- package/dist/mjs/shapes/Rect2.d.ts +3 -0
- package/dist/mjs/shapes/Rect2.mjs +4 -1
- package/package.json +5 -5
- package/src/Vec3.test.ts +26 -7
- package/src/Vec3.ts +30 -0
- package/src/lib.ts +3 -1
- package/src/rounding/cleanUpNumber.test.ts +15 -0
- package/src/rounding/cleanUpNumber.ts +38 -0
- package/src/rounding/constants.ts +3 -0
- package/src/rounding/getLenAfterDecimal.ts +29 -0
- package/src/rounding/lib.ts +2 -0
- package/src/rounding/toRoundedString.test.ts +32 -0
- package/src/rounding/toRoundedString.ts +57 -0
- package/src/rounding/toStringOfSamePrecision.test.ts +21 -0
- package/src/rounding/toStringOfSamePrecision.ts +63 -0
- package/src/shapes/Abstract2DShape.ts +3 -0
- package/src/shapes/BezierJSWrapper.ts +154 -14
- package/src/shapes/LineSegment2.test.ts +35 -1
- package/src/shapes/LineSegment2.ts +79 -11
- package/src/shapes/Parameterized2DShape.ts +39 -0
- package/src/shapes/Path.test.ts +63 -3
- package/src/shapes/Path.ts +211 -26
- package/src/shapes/PointShape2D.ts +33 -6
- package/src/shapes/QuadraticBezier.test.ts +48 -12
- package/src/shapes/QuadraticBezier.ts +23 -5
- package/src/shapes/Rect2.ts +4 -1
- package/dist/cjs/rounding.js +0 -146
- package/dist/mjs/rounding.mjs +0 -139
- package/src/rounding.test.ts +0 -65
- package/src/rounding.ts +0 -168
- /package/dist/cjs/{rounding.test.d.ts → rounding/cleanUpNumber.test.d.ts} +0 -0
- /package/dist/{mjs/rounding.test.d.ts → cjs/rounding/toRoundedString.test.d.ts} +0 -0
|
@@ -3,29 +3,52 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
6
|
+
const Vec2_1 = require("../Vec2");
|
|
7
|
+
const Parameterized2DShape_1 = __importDefault(require("./Parameterized2DShape"));
|
|
7
8
|
const Rect2_1 = __importDefault(require("./Rect2"));
|
|
8
9
|
/**
|
|
9
10
|
* Like a {@link Point2}, but with additional functionality (e.g. SDF).
|
|
10
11
|
*
|
|
11
12
|
* Access the internal `Point2` using the `p` property.
|
|
12
13
|
*/
|
|
13
|
-
class PointShape2D extends
|
|
14
|
+
class PointShape2D extends Parameterized2DShape_1.default {
|
|
14
15
|
constructor(p) {
|
|
15
16
|
super();
|
|
16
17
|
this.p = p;
|
|
17
18
|
}
|
|
18
19
|
signedDistance(point) {
|
|
19
|
-
return this.p.
|
|
20
|
+
return this.p.distanceTo(point);
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
+
argIntersectsLineSegment(lineSegment, epsilon) {
|
|
22
23
|
if (lineSegment.containsPoint(this.p, epsilon)) {
|
|
23
|
-
return [
|
|
24
|
+
return [0];
|
|
24
25
|
}
|
|
25
26
|
return [];
|
|
26
27
|
}
|
|
27
28
|
getTightBoundingBox() {
|
|
28
29
|
return new Rect2_1.default(this.p.x, this.p.y, 0, 0);
|
|
29
30
|
}
|
|
31
|
+
at(_t) {
|
|
32
|
+
return this.p;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns an arbitrary unit-length vector.
|
|
36
|
+
*/
|
|
37
|
+
normalAt(_t) {
|
|
38
|
+
// Return a vector that makes sense.
|
|
39
|
+
return Vec2_1.Vec2.unitY;
|
|
40
|
+
}
|
|
41
|
+
tangentAt(_t) {
|
|
42
|
+
return Vec2_1.Vec2.unitX;
|
|
43
|
+
}
|
|
44
|
+
splitAt(_t) {
|
|
45
|
+
return [this];
|
|
46
|
+
}
|
|
47
|
+
nearestPointTo(_point) {
|
|
48
|
+
return {
|
|
49
|
+
point: this.p,
|
|
50
|
+
parameterValue: 0,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
30
53
|
}
|
|
31
54
|
exports.default = PointShape2D;
|
|
@@ -18,11 +18,15 @@ export declare class QuadraticBezier extends BezierJSWrapper {
|
|
|
18
18
|
*/
|
|
19
19
|
private static componentAt;
|
|
20
20
|
private static derivativeComponentAt;
|
|
21
|
+
private static secondDerivativeComponentAt;
|
|
21
22
|
/**
|
|
22
23
|
* @returns the curve evaluated at `t`.
|
|
24
|
+
*
|
|
25
|
+
* `t` should be a number in `[0, 1]`.
|
|
23
26
|
*/
|
|
24
27
|
at(t: number): Point2;
|
|
25
28
|
derivativeAt(t: number): Point2;
|
|
29
|
+
secondDerivativeAt(t: number): Point2;
|
|
26
30
|
normal(t: number): Vec2;
|
|
27
31
|
/** @returns an overestimate of this shape's bounding box. */
|
|
28
32
|
getLooseBoundingBox(): Rect2;
|
|
@@ -31,10 +31,19 @@ class QuadraticBezier extends BezierJSWrapper_1.default {
|
|
|
31
31
|
static derivativeComponentAt(t, p0, p1, p2) {
|
|
32
32
|
return -2 * p0 + 2 * p1 + 2 * t * (p0 - 2 * p1 + p2);
|
|
33
33
|
}
|
|
34
|
+
static secondDerivativeComponentAt(t, p0, p1, p2) {
|
|
35
|
+
return 2 * (p0 - 2 * p1 + p2);
|
|
36
|
+
}
|
|
34
37
|
/**
|
|
35
38
|
* @returns the curve evaluated at `t`.
|
|
39
|
+
*
|
|
40
|
+
* `t` should be a number in `[0, 1]`.
|
|
36
41
|
*/
|
|
37
42
|
at(t) {
|
|
43
|
+
if (t === 0)
|
|
44
|
+
return this.p0;
|
|
45
|
+
if (t === 1)
|
|
46
|
+
return this.p2;
|
|
38
47
|
const p0 = this.p0;
|
|
39
48
|
const p1 = this.p1;
|
|
40
49
|
const p2 = this.p2;
|
|
@@ -46,6 +55,12 @@ class QuadraticBezier extends BezierJSWrapper_1.default {
|
|
|
46
55
|
const p2 = this.p2;
|
|
47
56
|
return Vec2_1.Vec2.of(QuadraticBezier.derivativeComponentAt(t, p0.x, p1.x, p2.x), QuadraticBezier.derivativeComponentAt(t, p0.y, p1.y, p2.y));
|
|
48
57
|
}
|
|
58
|
+
secondDerivativeAt(t) {
|
|
59
|
+
const p0 = this.p0;
|
|
60
|
+
const p1 = this.p1;
|
|
61
|
+
const p2 = this.p2;
|
|
62
|
+
return Vec2_1.Vec2.of(QuadraticBezier.secondDerivativeComponentAt(t, p0.x, p1.x, p2.x), QuadraticBezier.secondDerivativeComponentAt(t, p0.y, p1.y, p2.y));
|
|
63
|
+
}
|
|
49
64
|
normal(t) {
|
|
50
65
|
const tangent = this.derivativeAt(t);
|
|
51
66
|
return tangent.orthog().normalized();
|
|
@@ -106,10 +121,10 @@ class QuadraticBezier extends BezierJSWrapper_1.default {
|
|
|
106
121
|
}
|
|
107
122
|
const at1 = this.at(min1);
|
|
108
123
|
const at2 = this.at(min2);
|
|
109
|
-
const sqrDist1 = at1.
|
|
110
|
-
const sqrDist2 = at2.
|
|
111
|
-
const sqrDist3 = this.at(0).
|
|
112
|
-
const sqrDist4 = this.at(1).
|
|
124
|
+
const sqrDist1 = at1.squareDistanceTo(point);
|
|
125
|
+
const sqrDist2 = at2.squareDistanceTo(point);
|
|
126
|
+
const sqrDist3 = this.at(0).squareDistanceTo(point);
|
|
127
|
+
const sqrDist4 = this.at(1).squareDistanceTo(point);
|
|
113
128
|
return Math.sqrt(Math.min(sqrDist1, sqrDist2, sqrDist3, sqrDist4));
|
|
114
129
|
}
|
|
115
130
|
getPoints() {
|
|
@@ -25,6 +25,9 @@ export declare class Rect2 extends Abstract2DShape {
|
|
|
25
25
|
resizedTo(size: Vec2): Rect2;
|
|
26
26
|
containsPoint(other: Point2): boolean;
|
|
27
27
|
containsRect(other: Rect2): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* @returns true iff this and `other` overlap
|
|
30
|
+
*/
|
|
28
31
|
intersects(other: Rect2): boolean;
|
|
29
32
|
intersection(other: Rect2): Rect2 | null;
|
|
30
33
|
union(other: Rect2): Rect2;
|
package/dist/cjs/shapes/Rect2.js
CHANGED
|
@@ -44,6 +44,9 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
|
44
44
|
&& this.x + this.w >= other.x + other.w
|
|
45
45
|
&& this.y + this.h >= other.y + other.h;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* @returns true iff this and `other` overlap
|
|
49
|
+
*/
|
|
47
50
|
intersects(other) {
|
|
48
51
|
// Project along x/y axes.
|
|
49
52
|
const thisMinX = this.x;
|
|
@@ -130,7 +133,7 @@ class Rect2 extends Abstract2DShape_1.default {
|
|
|
130
133
|
let closest = null;
|
|
131
134
|
let closestDist = null;
|
|
132
135
|
for (const point of closestEdgePoints) {
|
|
133
|
-
const dist = point.
|
|
136
|
+
const dist = point.distanceTo(target);
|
|
134
137
|
if (closestDist === null || dist < closestDist) {
|
|
135
138
|
closest = point;
|
|
136
139
|
closestDist = dist;
|
package/dist/mjs/Vec3.d.ts
CHANGED
|
@@ -35,11 +35,31 @@ export declare class Vec3 {
|
|
|
35
35
|
length(): number;
|
|
36
36
|
magnitude(): number;
|
|
37
37
|
magnitudeSquared(): number;
|
|
38
|
+
/**
|
|
39
|
+
* Interpreting this vector as a point in ℝ^3, computes the square distance
|
|
40
|
+
* to another point, `p`.
|
|
41
|
+
*
|
|
42
|
+
* Equivalent to `.minus(p).magnitudeSquared()`.
|
|
43
|
+
*/
|
|
44
|
+
squareDistanceTo(p: Vec3): number;
|
|
45
|
+
/**
|
|
46
|
+
* Interpreting this vector as a point in ℝ³, returns the distance to the point
|
|
47
|
+
* `p`.
|
|
48
|
+
*
|
|
49
|
+
* Equivalent to `.minus(p).magnitude()`.
|
|
50
|
+
*/
|
|
51
|
+
distanceTo(p: Vec3): number;
|
|
38
52
|
/**
|
|
39
53
|
* Returns the entry of this with the greatest magnitude.
|
|
40
54
|
*
|
|
41
55
|
* In other words, returns $\max \{ |x| : x \in {\bf v} \}$, where ${\bf v}$ is the set of
|
|
42
56
|
* all entries of this vector.
|
|
57
|
+
*
|
|
58
|
+
* **Example**:
|
|
59
|
+
* ```ts,runnable,console
|
|
60
|
+
* import { Vec3 } from '@js-draw/math';
|
|
61
|
+
* console.log(Vec3.of(-1, -10, 8).maximumEntryMagnitude()); // -> 10
|
|
62
|
+
* ```
|
|
43
63
|
*/
|
|
44
64
|
maximumEntryMagnitude(): number;
|
|
45
65
|
/**
|
|
@@ -50,6 +70,7 @@ export declare class Vec3 {
|
|
|
50
70
|
* As such, observing that `Math.atan2(-0, -1)` $\approx -\pi$ and `Math.atan2(0, -1)`$\approx \pi$
|
|
51
71
|
* the resultant angle is in the range $[-\pi, pi]$.
|
|
52
72
|
*
|
|
73
|
+
* **Example**:
|
|
53
74
|
* ```ts,runnable,console
|
|
54
75
|
* import { Vec2 } from '@js-draw/math';
|
|
55
76
|
* console.log(Vec2.of(-1, -0).angle()); // atan2(-0, -1)
|
package/dist/mjs/Vec3.mjs
CHANGED
|
@@ -55,11 +55,38 @@ export class Vec3 {
|
|
|
55
55
|
magnitudeSquared() {
|
|
56
56
|
return this.dot(this);
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Interpreting this vector as a point in ℝ^3, computes the square distance
|
|
60
|
+
* to another point, `p`.
|
|
61
|
+
*
|
|
62
|
+
* Equivalent to `.minus(p).magnitudeSquared()`.
|
|
63
|
+
*/
|
|
64
|
+
squareDistanceTo(p) {
|
|
65
|
+
const dx = this.x - p.x;
|
|
66
|
+
const dy = this.y - p.y;
|
|
67
|
+
const dz = this.z - p.z;
|
|
68
|
+
return dx * dx + dy * dy + dz * dz;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Interpreting this vector as a point in ℝ³, returns the distance to the point
|
|
72
|
+
* `p`.
|
|
73
|
+
*
|
|
74
|
+
* Equivalent to `.minus(p).magnitude()`.
|
|
75
|
+
*/
|
|
76
|
+
distanceTo(p) {
|
|
77
|
+
return Math.sqrt(this.squareDistanceTo(p));
|
|
78
|
+
}
|
|
58
79
|
/**
|
|
59
80
|
* Returns the entry of this with the greatest magnitude.
|
|
60
81
|
*
|
|
61
82
|
* In other words, returns $\max \{ |x| : x \in {\bf v} \}$, where ${\bf v}$ is the set of
|
|
62
83
|
* all entries of this vector.
|
|
84
|
+
*
|
|
85
|
+
* **Example**:
|
|
86
|
+
* ```ts,runnable,console
|
|
87
|
+
* import { Vec3 } from '@js-draw/math';
|
|
88
|
+
* console.log(Vec3.of(-1, -10, 8).maximumEntryMagnitude()); // -> 10
|
|
89
|
+
* ```
|
|
63
90
|
*/
|
|
64
91
|
maximumEntryMagnitude() {
|
|
65
92
|
return Math.max(Math.abs(this.x), Math.max(Math.abs(this.y), Math.abs(this.z)));
|
|
@@ -72,6 +99,7 @@ export class Vec3 {
|
|
|
72
99
|
* As such, observing that `Math.atan2(-0, -1)` $\approx -\pi$ and `Math.atan2(0, -1)`$\approx \pi$
|
|
73
100
|
* the resultant angle is in the range $[-\pi, pi]$.
|
|
74
101
|
*
|
|
102
|
+
* **Example**:
|
|
75
103
|
* ```ts,runnable,console
|
|
76
104
|
* import { Vec2 } from '@js-draw/math';
|
|
77
105
|
* console.log(Vec2.of(-1, -0).angle()); // atan2(-0, -1)
|
package/dist/mjs/lib.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* @packageDocumentation
|
|
18
18
|
*/
|
|
19
19
|
export { LineSegment2 } from './shapes/LineSegment2';
|
|
20
|
-
export { Path, PathCommandType, PathCommand, LinePathCommand, MoveToPathCommand, QuadraticBezierPathCommand, CubicBezierPathCommand, } from './shapes/Path';
|
|
20
|
+
export { Path, IntersectionResult as PathIntersectionResult, CurveIndexRecord as PathCurveIndex, PathCommandType, PathCommand, LinePathCommand, MoveToPathCommand, QuadraticBezierPathCommand, CubicBezierPathCommand, } from './shapes/Path';
|
|
21
21
|
export { Rect2 } from './shapes/Rect2';
|
|
22
22
|
export { QuadraticBezier } from './shapes/QuadraticBezier';
|
|
23
23
|
export { Abstract2DShape } from './shapes/Abstract2DShape';
|
|
@@ -25,4 +25,4 @@ export { Mat33, Mat33Array } from './Mat33';
|
|
|
25
25
|
export { Point2, Vec2 } from './Vec2';
|
|
26
26
|
export { Vec3 } from './Vec3';
|
|
27
27
|
export { Color4 } from './Color4';
|
|
28
|
-
export
|
|
28
|
+
export * from './rounding/lib';
|
package/dist/mjs/lib.mjs
CHANGED
|
@@ -25,6 +25,6 @@ export { Mat33 } from './Mat33.mjs';
|
|
|
25
25
|
export { Vec2 } from './Vec2.mjs';
|
|
26
26
|
export { Vec3 } from './Vec3.mjs';
|
|
27
27
|
export { Color4 } from './Color4.mjs';
|
|
28
|
-
export
|
|
28
|
+
export * from './rounding/lib.mjs';
|
|
29
29
|
// Note: All above exports cannot use `export { default as ... } from "..."` because this
|
|
30
30
|
// breaks TypeDoc -- TypeDoc otherwise labels any imports of these classes as `default`.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Cleans up stringified numbers */
|
|
2
|
+
export const cleanUpNumber = (text) => {
|
|
3
|
+
// Regular expression substitions can be somewhat expensive. Only do them
|
|
4
|
+
// if necessary.
|
|
5
|
+
if (text.indexOf('e') > 0) {
|
|
6
|
+
// Round to zero.
|
|
7
|
+
if (text.match(/[eE][-]\d{2,}$/)) {
|
|
8
|
+
return '0';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const lastChar = text.charAt(text.length - 1);
|
|
12
|
+
if (lastChar === '0' || lastChar === '.') {
|
|
13
|
+
// Remove trailing zeroes
|
|
14
|
+
text = text.replace(/([.]\d*[^0]+)0+$/, '$1');
|
|
15
|
+
text = text.replace(/[.]0+$/, '.');
|
|
16
|
+
// Remove trailing period
|
|
17
|
+
text = text.replace(/[.]$/, '');
|
|
18
|
+
}
|
|
19
|
+
const firstChar = text.charAt(0);
|
|
20
|
+
if (firstChar === '0' || firstChar === '-') {
|
|
21
|
+
// Remove unnecessary leading zeroes.
|
|
22
|
+
text = text.replace(/^(0+)[.]/, '.');
|
|
23
|
+
text = text.replace(/^-(0+)[.]/, '-.');
|
|
24
|
+
text = text.replace(/^(-?)0+$/, '$10');
|
|
25
|
+
}
|
|
26
|
+
if (text === '-0') {
|
|
27
|
+
return '0';
|
|
28
|
+
}
|
|
29
|
+
return text;
|
|
30
|
+
};
|
|
31
|
+
export default cleanUpNumber;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const numberRegex: RegExp;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const numberRegex = /^([-]?)(\d*)[.](\d+)$/;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the length of `numberAsString` after a decimal point.
|
|
3
|
+
*
|
|
4
|
+
* For example,
|
|
5
|
+
* ```ts
|
|
6
|
+
* getLenAfterDecimal('1.001') // -> 3
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export declare const getLenAfterDecimal: (numberAsString: string) => number;
|
|
10
|
+
export default getLenAfterDecimal;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { numberRegex } from './constants.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the length of `numberAsString` after a decimal point.
|
|
4
|
+
*
|
|
5
|
+
* For example,
|
|
6
|
+
* ```ts
|
|
7
|
+
* getLenAfterDecimal('1.001') // -> 3
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export const getLenAfterDecimal = (numberAsString) => {
|
|
11
|
+
const numberMatch = numberRegex.exec(numberAsString);
|
|
12
|
+
if (!numberMatch) {
|
|
13
|
+
// If not a match, either the number is exponential notation (or is something
|
|
14
|
+
// like NaN or Infinity)
|
|
15
|
+
if (numberAsString.search(/[eE]/) !== -1 || /^[a-zA-Z]+$/.exec(numberAsString)) {
|
|
16
|
+
return -1;
|
|
17
|
+
// Or it has no decimal point
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const afterDecimalLen = numberMatch[3].length;
|
|
24
|
+
return afterDecimalLen;
|
|
25
|
+
};
|
|
26
|
+
export default getLenAfterDecimal;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { toRoundedString } from './toRoundedString';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { toRoundedString } from './toRoundedString.mjs';
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export declare const cleanUpNumber: (text: string) => string;
|
|
2
1
|
/**
|
|
3
2
|
* Converts `num` to a string, removing trailing digits that were likely caused by
|
|
4
3
|
* precision errors.
|
|
@@ -11,5 +10,4 @@ export declare const cleanUpNumber: (text: string) => string;
|
|
|
11
10
|
* ```
|
|
12
11
|
*/
|
|
13
12
|
export declare const toRoundedString: (num: number) => string;
|
|
14
|
-
export
|
|
15
|
-
export declare const toStringOfSamePrecision: (num: number, ...references: string[]) => string;
|
|
13
|
+
export default toRoundedString;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import cleanUpNumber from './cleanUpNumber.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Converts `num` to a string, removing trailing digits that were likely caused by
|
|
4
|
+
* precision errors.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts,runnable,console
|
|
8
|
+
* import { toRoundedString } from '@js-draw/math';
|
|
9
|
+
*
|
|
10
|
+
* console.log('Rounded: ', toRoundedString(1.000000011));
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export const toRoundedString = (num) => {
|
|
14
|
+
// Try to remove rounding errors. If the number ends in at least three/four zeroes
|
|
15
|
+
// (or nines) just one or two digits, it's probably a rounding error.
|
|
16
|
+
const fixRoundingUpExp = /^([-]?\d*\.\d{3,})0{4,}\d{1,4}$/;
|
|
17
|
+
const hasRoundingDownExp = /^([-]?)(\d*)\.(\d{3,}9{4,})\d{1,4}$/;
|
|
18
|
+
let text = num.toString(10);
|
|
19
|
+
if (text.indexOf('.') === -1) {
|
|
20
|
+
return text;
|
|
21
|
+
}
|
|
22
|
+
const roundingDownMatch = hasRoundingDownExp.exec(text);
|
|
23
|
+
if (roundingDownMatch) {
|
|
24
|
+
const negativeSign = roundingDownMatch[1];
|
|
25
|
+
const postDecimalString = roundingDownMatch[3];
|
|
26
|
+
const lastDigit = parseInt(postDecimalString.charAt(postDecimalString.length - 1), 10);
|
|
27
|
+
const postDecimal = parseInt(postDecimalString, 10);
|
|
28
|
+
const preDecimal = parseInt(roundingDownMatch[2], 10);
|
|
29
|
+
const origPostDecimalString = roundingDownMatch[3];
|
|
30
|
+
let newPostDecimal = (postDecimal + 10 - lastDigit).toString();
|
|
31
|
+
let carry = 0;
|
|
32
|
+
if (newPostDecimal.length > postDecimal.toString().length) {
|
|
33
|
+
// Left-shift
|
|
34
|
+
newPostDecimal = newPostDecimal.substring(1);
|
|
35
|
+
carry = 1;
|
|
36
|
+
}
|
|
37
|
+
// parseInt(...).toString() removes leading zeroes. Add them back.
|
|
38
|
+
while (newPostDecimal.length < origPostDecimalString.length) {
|
|
39
|
+
newPostDecimal = carry.toString(10) + newPostDecimal;
|
|
40
|
+
carry = 0;
|
|
41
|
+
}
|
|
42
|
+
text = `${negativeSign + (preDecimal + carry).toString()}.${newPostDecimal}`;
|
|
43
|
+
}
|
|
44
|
+
text = text.replace(fixRoundingUpExp, '$1');
|
|
45
|
+
return cleanUpNumber(text);
|
|
46
|
+
};
|
|
47
|
+
export default toRoundedString;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import cleanUpNumber from './cleanUpNumber.mjs';
|
|
2
|
+
import { numberRegex } from './constants.mjs';
|
|
3
|
+
import getLenAfterDecimal from './getLenAfterDecimal.mjs';
|
|
4
|
+
import toRoundedString from './toRoundedString.mjs';
|
|
5
|
+
// [reference] should be a string representation of a base-10 number (no exponential (e.g. 10e10))
|
|
6
|
+
export const toStringOfSamePrecision = (num, ...references) => {
|
|
7
|
+
const text = num.toString(10);
|
|
8
|
+
const textMatch = numberRegex.exec(text);
|
|
9
|
+
if (!textMatch) {
|
|
10
|
+
return text;
|
|
11
|
+
}
|
|
12
|
+
let decimalPlaces = -1;
|
|
13
|
+
for (const reference of references) {
|
|
14
|
+
decimalPlaces = Math.max(getLenAfterDecimal(reference), decimalPlaces);
|
|
15
|
+
}
|
|
16
|
+
if (decimalPlaces === -1) {
|
|
17
|
+
return toRoundedString(num);
|
|
18
|
+
}
|
|
19
|
+
// Make text's after decimal length match [afterDecimalLen].
|
|
20
|
+
let postDecimal = textMatch[3].substring(0, decimalPlaces);
|
|
21
|
+
let preDecimal = textMatch[2];
|
|
22
|
+
const nextDigit = textMatch[3].charAt(decimalPlaces);
|
|
23
|
+
if (nextDigit !== '') {
|
|
24
|
+
const asNumber = parseInt(nextDigit, 10);
|
|
25
|
+
if (asNumber >= 5) {
|
|
26
|
+
// Don't attempt to parseInt() an empty string.
|
|
27
|
+
if (postDecimal.length > 0) {
|
|
28
|
+
const leadingZeroMatch = /^(0+)(\d*)$/.exec(postDecimal);
|
|
29
|
+
let leadingZeroes = '';
|
|
30
|
+
let postLeading = postDecimal;
|
|
31
|
+
if (leadingZeroMatch) {
|
|
32
|
+
leadingZeroes = leadingZeroMatch[1];
|
|
33
|
+
postLeading = leadingZeroMatch[2];
|
|
34
|
+
}
|
|
35
|
+
postDecimal = (parseInt(postDecimal) + 1).toString();
|
|
36
|
+
// If postDecimal got longer, remove leading zeroes if possible
|
|
37
|
+
if (postDecimal.length > postLeading.length && leadingZeroes.length > 0) {
|
|
38
|
+
leadingZeroes = leadingZeroes.substring(1);
|
|
39
|
+
}
|
|
40
|
+
postDecimal = leadingZeroes + postDecimal;
|
|
41
|
+
}
|
|
42
|
+
if (postDecimal.length === 0 || postDecimal.length > decimalPlaces) {
|
|
43
|
+
preDecimal = (parseInt(preDecimal) + 1).toString();
|
|
44
|
+
postDecimal = postDecimal.substring(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const negativeSign = textMatch[1];
|
|
49
|
+
return cleanUpNumber(`${negativeSign}${preDecimal}.${postDecimal}`);
|
|
50
|
+
};
|
|
51
|
+
export default toStringOfSamePrecision;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -38,6 +38,9 @@ export declare abstract class Abstract2DShape {
|
|
|
38
38
|
containsPoint(point: Point2, epsilon?: number): boolean;
|
|
39
39
|
/**
|
|
40
40
|
* Returns a bounding box that precisely fits the content of this shape.
|
|
41
|
+
*
|
|
42
|
+
* **Note**: This bounding box should aligned with the x/y axes. (Thus, it may be
|
|
43
|
+
* possible to find a tighter bounding box not axes-aligned).
|
|
41
44
|
*/
|
|
42
45
|
abstract getTightBoundingBox(): Rect2;
|
|
43
46
|
/**
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { Bezier } from 'bezier-js';
|
|
2
2
|
import { Point2, Vec2 } from '../Vec2';
|
|
3
|
-
import Abstract2DShape from './Abstract2DShape';
|
|
4
3
|
import LineSegment2 from './LineSegment2';
|
|
5
4
|
import Rect2 from './Rect2';
|
|
5
|
+
import Parameterized2DShape from './Parameterized2DShape';
|
|
6
6
|
/**
|
|
7
7
|
* A lazy-initializing wrapper around Bezier-js.
|
|
8
8
|
*
|
|
9
9
|
* Subclasses may override `at`, `derivativeAt`, and `normal` with functions
|
|
10
10
|
* that do not initialize a `bezier-js` `Bezier`.
|
|
11
11
|
*
|
|
12
|
-
* Do not use this class directly
|
|
12
|
+
* **Do not use this class directly.** It may be removed/replaced in a future release.
|
|
13
13
|
* @internal
|
|
14
14
|
*/
|
|
15
|
-
declare abstract class BezierJSWrapper extends
|
|
15
|
+
export declare abstract class BezierJSWrapper extends Parameterized2DShape {
|
|
16
16
|
#private;
|
|
17
|
+
protected constructor(bezierJsBezier?: Bezier);
|
|
17
18
|
/** Returns the start, control points, and end point of this Bézier. */
|
|
18
|
-
abstract getPoints(): Point2[];
|
|
19
|
+
abstract getPoints(): readonly Point2[];
|
|
19
20
|
protected getBezier(): Bezier;
|
|
20
21
|
signedDistance(point: Point2): number;
|
|
21
22
|
/**
|
|
@@ -29,8 +30,17 @@ declare abstract class BezierJSWrapper extends Abstract2DShape {
|
|
|
29
30
|
*/
|
|
30
31
|
at(t: number): Point2;
|
|
31
32
|
derivativeAt(t: number): Point2;
|
|
33
|
+
secondDerivativeAt(t: number): Point2;
|
|
32
34
|
normal(t: number): Vec2;
|
|
35
|
+
normalAt(t: number): Vec2;
|
|
36
|
+
tangentAt(t: number): Vec2;
|
|
33
37
|
getTightBoundingBox(): Rect2;
|
|
34
|
-
|
|
38
|
+
argIntersectsLineSegment(line: LineSegment2): number[];
|
|
39
|
+
splitAt(t: number): [BezierJSWrapper] | [BezierJSWrapper, BezierJSWrapper];
|
|
40
|
+
nearestPointTo(point: Point2): {
|
|
41
|
+
parameterValue: number;
|
|
42
|
+
point: import("../Vec3").Vec3;
|
|
43
|
+
};
|
|
44
|
+
toString(): string;
|
|
35
45
|
}
|
|
36
46
|
export default BezierJSWrapper;
|