@js-draw/math 1.11.1 → 1.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. package/dist/cjs/Vec3.d.ts +21 -0
  2. package/dist/cjs/Vec3.js +28 -0
  3. package/dist/cjs/lib.d.ts +2 -2
  4. package/dist/cjs/lib.js +16 -3
  5. package/dist/cjs/rounding/cleanUpNumber.d.ts +3 -0
  6. package/dist/cjs/rounding/cleanUpNumber.js +35 -0
  7. package/dist/cjs/rounding/constants.d.ts +1 -0
  8. package/dist/cjs/rounding/constants.js +4 -0
  9. package/dist/cjs/rounding/getLenAfterDecimal.d.ts +10 -0
  10. package/dist/cjs/rounding/getLenAfterDecimal.js +30 -0
  11. package/dist/cjs/rounding/lib.d.ts +1 -0
  12. package/dist/cjs/rounding/lib.js +5 -0
  13. package/dist/cjs/{rounding.d.ts → rounding/toRoundedString.d.ts} +1 -3
  14. package/dist/cjs/rounding/toRoundedString.js +54 -0
  15. package/dist/cjs/rounding/toStringOfSamePrecision.d.ts +2 -0
  16. package/dist/cjs/rounding/toStringOfSamePrecision.js +58 -0
  17. package/dist/cjs/rounding/toStringOfSamePrecision.test.d.ts +1 -0
  18. package/dist/cjs/shapes/Abstract2DShape.d.ts +3 -0
  19. package/dist/cjs/shapes/BezierJSWrapper.d.ts +15 -5
  20. package/dist/cjs/shapes/BezierJSWrapper.js +135 -18
  21. package/dist/cjs/shapes/LineSegment2.d.ts +34 -5
  22. package/dist/cjs/shapes/LineSegment2.js +63 -10
  23. package/dist/cjs/shapes/Parameterized2DShape.d.ts +31 -0
  24. package/dist/cjs/shapes/Parameterized2DShape.js +15 -0
  25. package/dist/cjs/shapes/Path.d.ts +40 -6
  26. package/dist/cjs/shapes/Path.js +181 -22
  27. package/dist/cjs/shapes/PointShape2D.d.ts +14 -3
  28. package/dist/cjs/shapes/PointShape2D.js +28 -5
  29. package/dist/cjs/shapes/QuadraticBezier.d.ts +4 -0
  30. package/dist/cjs/shapes/QuadraticBezier.js +19 -4
  31. package/dist/cjs/shapes/Rect2.d.ts +3 -0
  32. package/dist/cjs/shapes/Rect2.js +4 -1
  33. package/dist/mjs/Vec3.d.ts +21 -0
  34. package/dist/mjs/Vec3.mjs +28 -0
  35. package/dist/mjs/lib.d.ts +2 -2
  36. package/dist/mjs/lib.mjs +1 -1
  37. package/dist/mjs/rounding/cleanUpNumber.d.ts +3 -0
  38. package/dist/mjs/rounding/cleanUpNumber.mjs +31 -0
  39. package/dist/mjs/rounding/cleanUpNumber.test.d.ts +1 -0
  40. package/dist/mjs/rounding/constants.d.ts +1 -0
  41. package/dist/mjs/rounding/constants.mjs +1 -0
  42. package/dist/mjs/rounding/getLenAfterDecimal.d.ts +10 -0
  43. package/dist/mjs/rounding/getLenAfterDecimal.mjs +26 -0
  44. package/dist/mjs/rounding/lib.d.ts +1 -0
  45. package/dist/mjs/rounding/lib.mjs +1 -0
  46. package/dist/mjs/{rounding.d.ts → rounding/toRoundedString.d.ts} +1 -3
  47. package/dist/mjs/rounding/toRoundedString.mjs +47 -0
  48. package/dist/mjs/rounding/toRoundedString.test.d.ts +1 -0
  49. package/dist/mjs/rounding/toStringOfSamePrecision.d.ts +2 -0
  50. package/dist/mjs/rounding/toStringOfSamePrecision.mjs +51 -0
  51. package/dist/mjs/rounding/toStringOfSamePrecision.test.d.ts +1 -0
  52. package/dist/mjs/shapes/Abstract2DShape.d.ts +3 -0
  53. package/dist/mjs/shapes/BezierJSWrapper.d.ts +15 -5
  54. package/dist/mjs/shapes/BezierJSWrapper.mjs +133 -18
  55. package/dist/mjs/shapes/LineSegment2.d.ts +34 -5
  56. package/dist/mjs/shapes/LineSegment2.mjs +63 -10
  57. package/dist/mjs/shapes/Parameterized2DShape.d.ts +31 -0
  58. package/dist/mjs/shapes/Parameterized2DShape.mjs +8 -0
  59. package/dist/mjs/shapes/Path.d.ts +40 -6
  60. package/dist/mjs/shapes/Path.mjs +175 -16
  61. package/dist/mjs/shapes/PointShape2D.d.ts +14 -3
  62. package/dist/mjs/shapes/PointShape2D.mjs +28 -5
  63. package/dist/mjs/shapes/QuadraticBezier.d.ts +4 -0
  64. package/dist/mjs/shapes/QuadraticBezier.mjs +19 -4
  65. package/dist/mjs/shapes/Rect2.d.ts +3 -0
  66. package/dist/mjs/shapes/Rect2.mjs +4 -1
  67. package/package.json +5 -5
  68. package/src/Vec3.test.ts +26 -7
  69. package/src/Vec3.ts +30 -0
  70. package/src/lib.ts +3 -1
  71. package/src/rounding/cleanUpNumber.test.ts +15 -0
  72. package/src/rounding/cleanUpNumber.ts +38 -0
  73. package/src/rounding/constants.ts +3 -0
  74. package/src/rounding/getLenAfterDecimal.ts +29 -0
  75. package/src/rounding/lib.ts +2 -0
  76. package/src/rounding/toRoundedString.test.ts +32 -0
  77. package/src/rounding/toRoundedString.ts +57 -0
  78. package/src/rounding/toStringOfSamePrecision.test.ts +21 -0
  79. package/src/rounding/toStringOfSamePrecision.ts +63 -0
  80. package/src/shapes/Abstract2DShape.ts +3 -0
  81. package/src/shapes/BezierJSWrapper.ts +154 -14
  82. package/src/shapes/LineSegment2.test.ts +35 -1
  83. package/src/shapes/LineSegment2.ts +79 -11
  84. package/src/shapes/Parameterized2DShape.ts +39 -0
  85. package/src/shapes/Path.test.ts +63 -3
  86. package/src/shapes/Path.ts +211 -26
  87. package/src/shapes/PointShape2D.ts +33 -6
  88. package/src/shapes/QuadraticBezier.test.ts +48 -12
  89. package/src/shapes/QuadraticBezier.ts +23 -5
  90. package/src/shapes/Rect2.ts +4 -1
  91. package/dist/cjs/rounding.js +0 -146
  92. package/dist/mjs/rounding.mjs +0 -139
  93. package/src/rounding.test.ts +0 -65
  94. package/src/rounding.ts +0 -168
  95. /package/dist/cjs/{rounding.test.d.ts → rounding/cleanUpNumber.test.d.ts} +0 -0
  96. /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 Abstract2DShape_1 = __importDefault(require("./Abstract2DShape"));
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 Abstract2DShape_1.default {
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.minus(point).magnitude();
20
+ return this.p.distanceTo(point);
20
21
  }
21
- intersectsLineSegment(lineSegment, epsilon) {
22
+ argIntersectsLineSegment(lineSegment, epsilon) {
22
23
  if (lineSegment.containsPoint(this.p, epsilon)) {
23
- return [this.p];
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.minus(point).magnitudeSquared();
110
- const sqrDist2 = at2.minus(point).magnitudeSquared();
111
- const sqrDist3 = this.at(0).minus(point).magnitudeSquared();
112
- const sqrDist4 = this.at(1).minus(point).magnitudeSquared();
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;
@@ -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.minus(target).length();
136
+ const dist = point.distanceTo(target);
134
137
  if (closestDist === null || dist < closestDist) {
135
138
  closest = point;
136
139
  closestDist = dist;
@@ -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 { toRoundedString } from './rounding';
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 { toRoundedString } from './rounding.mjs';
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,3 @@
1
+ /** Cleans up stringified numbers */
2
+ export declare const cleanUpNumber: (text: string) => string;
3
+ export default cleanUpNumber;
@@ -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 declare const getLenAfterDecimal: (numberAsString: string) => number;
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,2 @@
1
+ export declare const toStringOfSamePrecision: (num: number, ...references: string[]) => string;
2
+ export default toStringOfSamePrecision;
@@ -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;
@@ -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. It may be removed/replaced in a future release.
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 Abstract2DShape {
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
- intersectsLineSegment(line: LineSegment2): Point2[];
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;