@emasoft/svg-matrix 1.0.30 → 1.0.31
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/bin/svg-matrix.js +310 -61
- package/bin/svglinter.cjs +102 -3
- package/bin/svgm.js +236 -27
- package/package.json +1 -1
- package/src/animation-optimization.js +137 -17
- package/src/animation-references.js +123 -6
- package/src/arc-length.js +213 -4
- package/src/bezier-analysis.js +217 -21
- package/src/bezier-intersections.js +275 -12
- package/src/browser-verify.js +237 -4
- package/src/clip-path-resolver.js +168 -0
- package/src/convert-path-data.js +479 -28
- package/src/css-specificity.js +73 -10
- package/src/douglas-peucker.js +219 -2
- package/src/flatten-pipeline.js +284 -26
- package/src/geometry-to-path.js +250 -25
- package/src/gjk-collision.js +236 -33
- package/src/index.js +261 -3
- package/src/inkscape-support.js +86 -28
- package/src/logger.js +48 -3
- package/src/marker-resolver.js +278 -74
- package/src/mask-resolver.js +265 -66
- package/src/matrix.js +44 -5
- package/src/mesh-gradient.js +352 -102
- package/src/off-canvas-detection.js +382 -13
- package/src/path-analysis.js +192 -18
- package/src/path-data-plugins.js +309 -5
- package/src/path-optimization.js +129 -5
- package/src/path-simplification.js +188 -32
- package/src/pattern-resolver.js +454 -106
- package/src/polygon-clip.js +324 -1
- package/src/svg-boolean-ops.js +226 -9
- package/src/svg-collections.js +7 -5
- package/src/svg-flatten.js +386 -62
- package/src/svg-parser.js +179 -8
- package/src/svg-rendering-context.js +235 -6
- package/src/svg-toolbox.js +45 -8
- package/src/svg2-polyfills.js +40 -10
- package/src/transform-decomposition.js +258 -32
- package/src/transform-optimization.js +259 -13
- package/src/transforms2d.js +82 -9
- package/src/transforms3d.js +62 -10
- package/src/use-symbol-resolver.js +286 -42
- package/src/vector.js +64 -8
- package/src/verification.js +392 -1
package/src/bezier-analysis.js
CHANGED
|
@@ -28,7 +28,14 @@ Decimal.set({ precision: 80 });
|
|
|
28
28
|
* @param {number|string|Decimal} x - Value to convert
|
|
29
29
|
* @returns {Decimal}
|
|
30
30
|
*/
|
|
31
|
-
const D = (x) =>
|
|
31
|
+
const D = (x) => {
|
|
32
|
+
// INPUT VALIDATION: Ensure input is not null or undefined
|
|
33
|
+
// WHY: Passing null/undefined to Decimal constructor throws cryptic errors
|
|
34
|
+
if (x == null) {
|
|
35
|
+
throw new Error(`D(): cannot convert null or undefined to Decimal`);
|
|
36
|
+
}
|
|
37
|
+
return x instanceof Decimal ? x : new Decimal(x);
|
|
38
|
+
};
|
|
32
39
|
|
|
33
40
|
/**
|
|
34
41
|
* Validate that a value is a finite number (not NaN or Infinity).
|
|
@@ -39,6 +46,13 @@ const D = (x) => (x instanceof Decimal ? x : new Decimal(x));
|
|
|
39
46
|
* @throws {Error} If value is not finite
|
|
40
47
|
*/
|
|
41
48
|
function _assertFinite(val, context) {
|
|
49
|
+
// INPUT VALIDATION: Ensure val is a Decimal instance
|
|
50
|
+
// WHY: Calling .isFinite() on null/undefined/non-Decimal throws errors
|
|
51
|
+
if (!val || !(val instanceof Decimal)) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`${context}: expected Decimal instance, got ${typeof val}`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
42
56
|
if (!val.isFinite()) {
|
|
43
57
|
throw new Error(`${context}: encountered non-finite value ${val}`);
|
|
44
58
|
}
|
|
@@ -135,6 +149,22 @@ export function bezierPoint(points, t) {
|
|
|
135
149
|
);
|
|
136
150
|
}
|
|
137
151
|
|
|
152
|
+
// POINT VALIDATION: Ensure each point is a valid [x, y] array
|
|
153
|
+
// WHY: Invalid point structure causes crashes when accessing indices
|
|
154
|
+
for (let i = 0; i < points.length; i++) {
|
|
155
|
+
if (
|
|
156
|
+
!points[i] ||
|
|
157
|
+
!Array.isArray(points[i]) ||
|
|
158
|
+
points[i].length < 2 ||
|
|
159
|
+
points[i][0] == null ||
|
|
160
|
+
points[i][1] == null
|
|
161
|
+
) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`bezierPoint: points[${i}] must be a valid [x, y] array with non-null coordinates`,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
138
168
|
const tD = D(t);
|
|
139
169
|
// PARAMETER VALIDATION: Warn if t is outside [0,1] but still compute
|
|
140
170
|
// WHY: Values slightly outside [0,1] may occur in numerical algorithms
|
|
@@ -184,6 +214,22 @@ export function bezierPointHorner(points, t) {
|
|
|
184
214
|
);
|
|
185
215
|
}
|
|
186
216
|
|
|
217
|
+
// POINT VALIDATION: Ensure each point is a valid [x, y] array
|
|
218
|
+
// WHY: Invalid point structure causes crashes when accessing indices
|
|
219
|
+
for (let i = 0; i < points.length; i++) {
|
|
220
|
+
if (
|
|
221
|
+
!points[i] ||
|
|
222
|
+
!Array.isArray(points[i]) ||
|
|
223
|
+
points[i].length < 2 ||
|
|
224
|
+
points[i][0] == null ||
|
|
225
|
+
points[i][1] == null
|
|
226
|
+
) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
`bezierPointHorner: points[${i}] must be a valid [x, y] array with non-null coordinates`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
187
233
|
const tD = D(t);
|
|
188
234
|
const n = points.length - 1; // Degree
|
|
189
235
|
|
|
@@ -268,6 +314,14 @@ export function bezierDerivative(points, t, n = 1) {
|
|
|
268
314
|
);
|
|
269
315
|
}
|
|
270
316
|
|
|
317
|
+
// PARAMETER VALIDATION: Ensure n is a non-negative integer
|
|
318
|
+
// WHY: Negative or non-integer derivative orders are not meaningful
|
|
319
|
+
if (typeof n !== "number" || n < 0 || !Number.isInteger(n)) {
|
|
320
|
+
throw new Error(
|
|
321
|
+
`bezierDerivative: n must be a non-negative integer, got ${n}`,
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
271
325
|
if (n === 0) {
|
|
272
326
|
return bezierPoint(points, t);
|
|
273
327
|
}
|
|
@@ -625,6 +679,15 @@ export function bezierCrop(points, t0, t1) {
|
|
|
625
679
|
throw new Error("bezierCrop: t1 must be in range [0, 1]");
|
|
626
680
|
}
|
|
627
681
|
|
|
682
|
+
// DIVISION BY ZERO PROTECTION: Check if t0 is too close to 1
|
|
683
|
+
// WHY: When t0 approaches 1, the denominator (1 - t0) approaches zero,
|
|
684
|
+
// causing division by zero in the parameter adjustment calculation
|
|
685
|
+
if (D(1).minus(t0D).abs().lt(NEAR_ZERO_THRESHOLD)) {
|
|
686
|
+
throw new Error(
|
|
687
|
+
"bezierCrop: t0 too close to 1, would cause division by zero in parameter adjustment",
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
628
691
|
// First split at t0, take the right portion
|
|
629
692
|
const { right: afterT0 } = bezierSplit(points, t0);
|
|
630
693
|
|
|
@@ -713,6 +776,14 @@ function findBezierRoots1D(points, component) {
|
|
|
713
776
|
return []; // No roots possible for empty input
|
|
714
777
|
}
|
|
715
778
|
|
|
779
|
+
// PARAMETER VALIDATION: Ensure component is valid
|
|
780
|
+
// WHY: Invalid component values would cause incorrect index access
|
|
781
|
+
if (component !== "x" && component !== "y") {
|
|
782
|
+
throw new Error(
|
|
783
|
+
`findBezierRoots1D: component must be 'x' or 'y', got '${component}'`,
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
|
|
716
787
|
const idx = component === "x" ? 0 : 1;
|
|
717
788
|
const roots = [];
|
|
718
789
|
|
|
@@ -776,37 +847,56 @@ function findBezierRoots1D(points, component) {
|
|
|
776
847
|
* @returns {Decimal[]} Real roots
|
|
777
848
|
*/
|
|
778
849
|
function solveQuadratic(a, b, c) {
|
|
850
|
+
// INPUT VALIDATION: Ensure all coefficients are valid Decimal instances
|
|
851
|
+
// WHY: Invalid inputs cause cryptic errors in arithmetic operations
|
|
852
|
+
if (a == null || b == null || c == null) {
|
|
853
|
+
throw new Error(
|
|
854
|
+
"solveQuadratic: coefficients a, b, c must not be null or undefined",
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
// Convert to Decimal if needed (use local variables to avoid param reassignment)
|
|
858
|
+
const aD = a instanceof Decimal ? a : D(a);
|
|
859
|
+
const bD = b instanceof Decimal ? b : D(b);
|
|
860
|
+
const cD = c instanceof Decimal ? c : D(c);
|
|
861
|
+
|
|
862
|
+
// Check for non-finite values
|
|
863
|
+
if (!aD.isFinite() || !bD.isFinite() || !cD.isFinite()) {
|
|
864
|
+
throw new Error(
|
|
865
|
+
"solveQuadratic: coefficients must be finite numbers, got non-finite values",
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
|
|
779
869
|
// NUMERICAL STABILITY: Use threshold relative to coefficient magnitudes
|
|
780
870
|
// to determine if 'a' is effectively zero (degenerate to linear equation)
|
|
781
871
|
// WHY: Absolute thresholds fail when coefficients are scaled; relative threshold adapts
|
|
782
|
-
const coeffMag = Decimal.max(
|
|
872
|
+
const coeffMag = Decimal.max(aD.abs(), bD.abs(), cD.abs());
|
|
783
873
|
|
|
784
874
|
if (
|
|
785
875
|
coeffMag.gt(0) &&
|
|
786
|
-
|
|
876
|
+
aD.abs().div(coeffMag).lt(QUADRATIC_DEGENERATE_THRESHOLD)
|
|
787
877
|
) {
|
|
788
878
|
// Linear equation: bx + c = 0
|
|
789
|
-
if (
|
|
790
|
-
return [
|
|
879
|
+
if (bD.isZero()) return [];
|
|
880
|
+
return [cD.neg().div(bD)];
|
|
791
881
|
}
|
|
792
882
|
|
|
793
|
-
if (
|
|
794
|
-
if (
|
|
795
|
-
return [
|
|
883
|
+
if (aD.isZero()) {
|
|
884
|
+
if (bD.isZero()) return [];
|
|
885
|
+
return [cD.neg().div(bD)];
|
|
796
886
|
}
|
|
797
887
|
|
|
798
|
-
const discriminant =
|
|
888
|
+
const discriminant = bD.times(bD).minus(aD.times(cD).times(4));
|
|
799
889
|
|
|
800
890
|
if (discriminant.lt(0)) {
|
|
801
891
|
return []; // No real roots
|
|
802
892
|
}
|
|
803
893
|
|
|
804
894
|
if (discriminant.isZero()) {
|
|
805
|
-
return [
|
|
895
|
+
return [bD.neg().div(aD.times(2))];
|
|
806
896
|
}
|
|
807
897
|
|
|
808
898
|
const sqrtD = discriminant.sqrt();
|
|
809
|
-
const twoA =
|
|
899
|
+
const twoA = aD.times(2);
|
|
810
900
|
|
|
811
901
|
// NUMERICAL STABILITY: Use Vieta's formula to compute the second root
|
|
812
902
|
// when catastrophic cancellation would occur in the standard formula.
|
|
@@ -817,16 +907,28 @@ function solveQuadratic(a, b, c) {
|
|
|
817
907
|
// x1 * x2 = c/a, so x2 = (c/a) / x1
|
|
818
908
|
|
|
819
909
|
let root1, root2;
|
|
820
|
-
if (
|
|
910
|
+
if (bD.isNegative()) {
|
|
821
911
|
// -b is positive, so -b + sqrt(D) is well-conditioned
|
|
822
|
-
root1 =
|
|
912
|
+
root1 = bD.neg().plus(sqrtD).div(twoA);
|
|
823
913
|
// Use Vieta's formula: x1 * x2 = c/a
|
|
824
|
-
|
|
914
|
+
// DIVISION BY ZERO PROTECTION: Check if root1 is zero before dividing
|
|
915
|
+
// WHY: When root1 is zero, Vieta's formula degenerates; use direct formula instead
|
|
916
|
+
if (root1.abs().lt(NEAR_ZERO_THRESHOLD)) {
|
|
917
|
+
root2 = bD.neg().minus(sqrtD).div(twoA);
|
|
918
|
+
} else {
|
|
919
|
+
root2 = cD.div(aD).div(root1);
|
|
920
|
+
}
|
|
825
921
|
} else {
|
|
826
922
|
// -b is negative or zero, so -b - sqrt(D) is well-conditioned
|
|
827
|
-
root1 =
|
|
923
|
+
root1 = bD.neg().minus(sqrtD).div(twoA);
|
|
828
924
|
// Use Vieta's formula: x1 * x2 = c/a
|
|
829
|
-
|
|
925
|
+
// DIVISION BY ZERO PROTECTION: Check if root1 is zero before dividing
|
|
926
|
+
// WHY: When root1 is zero, Vieta's formula degenerates; use direct formula instead
|
|
927
|
+
if (root1.abs().lt(NEAR_ZERO_THRESHOLD)) {
|
|
928
|
+
root2 = bD.neg().plus(sqrtD).div(twoA);
|
|
929
|
+
} else {
|
|
930
|
+
root2 = cD.div(aD).div(root1);
|
|
931
|
+
}
|
|
830
932
|
}
|
|
831
933
|
|
|
832
934
|
return [root1, root2];
|
|
@@ -842,6 +944,45 @@ function solveQuadratic(a, b, c) {
|
|
|
842
944
|
* @returns {Decimal[]} Roots in interval
|
|
843
945
|
*/
|
|
844
946
|
function findRootsBySubdivision(coeffs, t0, t1, maxDepth) {
|
|
947
|
+
// INPUT VALIDATION: Ensure coeffs is valid and maxDepth is non-negative
|
|
948
|
+
// WHY: Empty coeffs would cause errors; negative maxDepth could cause infinite recursion
|
|
949
|
+
if (!coeffs || !Array.isArray(coeffs) || coeffs.length === 0) {
|
|
950
|
+
return []; // No roots possible for empty input
|
|
951
|
+
}
|
|
952
|
+
if (typeof maxDepth !== "number" || maxDepth < 0) {
|
|
953
|
+
throw new Error(
|
|
954
|
+
`findRootsBySubdivision: maxDepth must be non-negative number, got ${maxDepth}`,
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// PARAMETER VALIDATION: Ensure t0 and t1 are valid
|
|
959
|
+
// WHY: Invalid interval bounds cause arithmetic errors
|
|
960
|
+
if (t0 == null || t1 == null) {
|
|
961
|
+
throw new Error(
|
|
962
|
+
"findRootsBySubdivision: t0 and t1 must not be null or undefined",
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
const t0D = t0 instanceof Decimal ? t0 : D(t0);
|
|
966
|
+
const t1D = t1 instanceof Decimal ? t1 : D(t1);
|
|
967
|
+
if (!t0D.isFinite() || !t1D.isFinite()) {
|
|
968
|
+
throw new Error(
|
|
969
|
+
"findRootsBySubdivision: t0 and t1 must be finite numbers",
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
if (t1D.lte(t0D)) {
|
|
973
|
+
throw new Error("findRootsBySubdivision: t1 must be greater than t0");
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// COEFFICIENT VALIDATION: Ensure all coefficients are valid Decimals
|
|
977
|
+
// WHY: Sign checking requires .isNegative() and .isZero() methods
|
|
978
|
+
for (let i = 0; i < coeffs.length; i++) {
|
|
979
|
+
if (!coeffs[i] || !(coeffs[i] instanceof Decimal)) {
|
|
980
|
+
throw new Error(
|
|
981
|
+
`findRootsBySubdivision: coeffs[${i}] must be a Decimal instance`,
|
|
982
|
+
);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
845
986
|
// Check if interval might contain a root (sign change in convex hull)
|
|
846
987
|
const signs = coeffs.map((c) => (c.isNegative() ? -1 : c.isZero() ? 0 : 1));
|
|
847
988
|
const minSign = Math.min(...signs);
|
|
@@ -853,19 +994,19 @@ function findRootsBySubdivision(coeffs, t0, t1, maxDepth) {
|
|
|
853
994
|
}
|
|
854
995
|
|
|
855
996
|
// WHY: Use named constant for subdivision convergence check
|
|
856
|
-
if (maxDepth <= 0 ||
|
|
997
|
+
if (maxDepth <= 0 || t1D.minus(t0D).lt(SUBDIVISION_CONVERGENCE_THRESHOLD)) {
|
|
857
998
|
// Converged, return midpoint
|
|
858
|
-
return [
|
|
999
|
+
return [t0D.plus(t1D).div(2)];
|
|
859
1000
|
}
|
|
860
1001
|
|
|
861
1002
|
// Subdivide at midpoint
|
|
862
|
-
const tMid =
|
|
1003
|
+
const tMid = t0D.plus(t1D).div(2);
|
|
863
1004
|
|
|
864
1005
|
// Compute subdivided control points using de Casteljau
|
|
865
1006
|
const { left, right } = subdivideBezier1D(coeffs);
|
|
866
1007
|
|
|
867
|
-
const leftRoots = findRootsBySubdivision(left,
|
|
868
|
-
const rightRoots = findRootsBySubdivision(right, tMid,
|
|
1008
|
+
const leftRoots = findRootsBySubdivision(left, t0D, tMid, maxDepth - 1);
|
|
1009
|
+
const rightRoots = findRootsBySubdivision(right, tMid, t1D, maxDepth - 1);
|
|
869
1010
|
|
|
870
1011
|
return leftRoots.concat(rightRoots);
|
|
871
1012
|
}
|
|
@@ -880,6 +1021,14 @@ function findRootsBySubdivision(coeffs, t0, t1, maxDepth) {
|
|
|
880
1021
|
* @returns {{left: Decimal[], right: Decimal[]}} Two 1D Bezier curves representing left and right halves
|
|
881
1022
|
*/
|
|
882
1023
|
function subdivideBezier1D(coeffs) {
|
|
1024
|
+
// INPUT VALIDATION: Ensure coeffs is valid and non-empty
|
|
1025
|
+
// WHY: Empty coeffs would cause errors in de Casteljau iteration
|
|
1026
|
+
if (!coeffs || !Array.isArray(coeffs) || coeffs.length === 0) {
|
|
1027
|
+
throw new Error(
|
|
1028
|
+
"subdivideBezier1D: coeffs must be a non-empty array of control values",
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
883
1032
|
const half = D(0.5);
|
|
884
1033
|
let pts = coeffs.map((c) => D(c));
|
|
885
1034
|
|
|
@@ -926,6 +1075,22 @@ export function bezierToPolynomial(points) {
|
|
|
926
1075
|
);
|
|
927
1076
|
}
|
|
928
1077
|
|
|
1078
|
+
// POINT VALIDATION: Ensure each point is a valid [x, y] array
|
|
1079
|
+
// WHY: Destructuring and index access fail on invalid point structures
|
|
1080
|
+
for (let i = 0; i < points.length; i++) {
|
|
1081
|
+
if (
|
|
1082
|
+
!points[i] ||
|
|
1083
|
+
!Array.isArray(points[i]) ||
|
|
1084
|
+
points[i].length < 2 ||
|
|
1085
|
+
points[i][0] == null ||
|
|
1086
|
+
points[i][1] == null
|
|
1087
|
+
) {
|
|
1088
|
+
throw new Error(
|
|
1089
|
+
`bezierToPolynomial: points[${i}] must be a valid [x, y] array with non-null coordinates`,
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
929
1094
|
const n = points.length - 1;
|
|
930
1095
|
const xCoeffs = [];
|
|
931
1096
|
const yCoeffs = [];
|
|
@@ -1003,6 +1168,21 @@ export function polynomialToBezier(xCoeffs, yCoeffs) {
|
|
|
1003
1168
|
);
|
|
1004
1169
|
}
|
|
1005
1170
|
|
|
1171
|
+
// COEFFICIENT VALIDATION: Ensure all coefficients are valid (not null/undefined)
|
|
1172
|
+
// WHY: Arithmetic operations fail on null/undefined values
|
|
1173
|
+
for (let i = 0; i < xCoeffs.length; i++) {
|
|
1174
|
+
if (xCoeffs[i] == null) {
|
|
1175
|
+
throw new Error(
|
|
1176
|
+
`polynomialToBezier: xCoeffs[${i}] is null or undefined`,
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
if (yCoeffs[i] == null) {
|
|
1180
|
+
throw new Error(
|
|
1181
|
+
`polynomialToBezier: yCoeffs[${i}] is null or undefined`,
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1006
1186
|
const n = xCoeffs.length - 1;
|
|
1007
1187
|
|
|
1008
1188
|
if (n === 1) {
|
|
@@ -1487,6 +1667,14 @@ export function verifyBoundingBox(points, samples = 100, tolerance = "1e-40") {
|
|
|
1487
1667
|
);
|
|
1488
1668
|
}
|
|
1489
1669
|
|
|
1670
|
+
// PARAMETER VALIDATION: Ensure samples is a positive integer
|
|
1671
|
+
// WHY: Non-positive or non-integer samples would cause loop errors or division by zero
|
|
1672
|
+
if (typeof samples !== "number" || samples < 1 || !Number.isInteger(samples)) {
|
|
1673
|
+
throw new Error(
|
|
1674
|
+
`verifyBoundingBox: samples must be a positive integer, got ${samples}`,
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1490
1678
|
const tol = D(tolerance);
|
|
1491
1679
|
const errors = [];
|
|
1492
1680
|
|
|
@@ -1564,6 +1752,14 @@ export function verifyDerivative(points, t, order = 1, tolerance = "1e-8") {
|
|
|
1564
1752
|
);
|
|
1565
1753
|
}
|
|
1566
1754
|
|
|
1755
|
+
// PARAMETER VALIDATION: Ensure order is a positive integer
|
|
1756
|
+
// WHY: Negative or non-integer orders are not meaningful for derivatives
|
|
1757
|
+
if (typeof order !== "number" || order < 0 || !Number.isInteger(order)) {
|
|
1758
|
+
throw new Error(
|
|
1759
|
+
`verifyDerivative: order must be a non-negative integer, got ${order}`,
|
|
1760
|
+
);
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1567
1763
|
const tol = D(tolerance);
|
|
1568
1764
|
const tD = D(t);
|
|
1569
1765
|
|