@pacem/pacem-numerical 1.0.0-bernoulli → 1.0.0-binet
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/browser/pacem-numerical.js +360 -1
- package/dist/browser/pacem-numerical.js.map +1 -1
- package/dist/browser/pacem-numerical.min.js +1 -2
- package/dist/bundle/pacem-numerical.min.mjs +1 -1
- package/dist/bundle/pacem-numerical.mjs +311 -1
- package/dist/bundle/pacem-numerical.mjs.map +2 -2
- package/dist/docs/pacem-numerical.json +13706 -0
- package/dist/esm/index-geometry-linearalgebra.js +1 -1
- package/dist/esm/index-geometry.js +1 -1
- package/dist/esm/index-iife.js +1 -1
- package/dist/esm/index-mathematics-dataanalysis.js +1 -1
- package/dist/esm/index-mathematics.js +1 -1
- package/dist/esm/index.js +1 -1
- package/package.json +2 -1
- package/typings/index.d.ts +541 -0
- package/dist/browser/pacem-numerical.min.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @pacem/pacem-numerical v1.0.0-
|
|
2
|
+
* @pacem/pacem-numerical v1.0.0-binet (https://js.pacem.it)
|
|
3
3
|
* Pacem (https://pacem.it)
|
|
4
4
|
* Licensed under Apache-2.0
|
|
5
5
|
*/
|
|
@@ -17,9 +17,17 @@
|
|
|
17
17
|
this.normalize(clone);
|
|
18
18
|
return clone;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Returns the squared magnitude (length) of the vector. Cheaper than {@link mag} since it avoids the square root.
|
|
22
|
+
* @param v
|
|
23
|
+
*/
|
|
20
24
|
static magSqr(v) {
|
|
21
25
|
return v.x * v.x + v.y * v.y;
|
|
22
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Returns the magnitude (length) of the vector.
|
|
29
|
+
* @param v
|
|
30
|
+
*/
|
|
23
31
|
static mag(v) {
|
|
24
32
|
return Math.sqrt(Vector.magSqr(v));
|
|
25
33
|
}
|
|
@@ -239,7 +247,32 @@
|
|
|
239
247
|
// This implementation is self-contained, numerically robust (small epsilon), and works with floating-point coordinates.
|
|
240
248
|
// It assumes general position (no four points exactly cocircular). For production use with degenerate cases,
|
|
241
249
|
// you may want to add a tiny random jitter to the points if needed.
|
|
250
|
+
/**
|
|
251
|
+
* Computes the (unconstrained) 2D Delaunay triangulation of a set of points, using the
|
|
252
|
+
* incremental Bowyer-Watson algorithm.
|
|
253
|
+
*
|
|
254
|
+
* A Delaunay triangulation connects a set of points into triangles such that no point lies
|
|
255
|
+
* strictly inside the circumcircle of any triangle. Among all the ways a point set can be
|
|
256
|
+
* triangulated, this property makes it the one that maximizes the minimum angle of all the
|
|
257
|
+
* triangles, avoiding thin "sliver" triangles — which is why it's the standard choice for
|
|
258
|
+
* meshes, terrain modeling, and (via its dual) Voronoi diagrams.
|
|
259
|
+
*
|
|
260
|
+
* The Bowyer-Watson algorithm builds the triangulation incrementally: starting from a single
|
|
261
|
+
* "super triangle" that encloses every input point, each point is inserted one at a time by
|
|
262
|
+
* removing every existing triangle whose circumcircle contains it (which necessarily leaves a
|
|
263
|
+
* star-shaped polygonal hole around the new point) and re-triangulating that hole by connecting
|
|
264
|
+
* the new point to the hole's boundary edges. Once every point has been inserted, any triangle
|
|
265
|
+
* still touching a vertex of the initial super triangle is discarded, leaving the final
|
|
266
|
+
* triangulation of the original points.
|
|
267
|
+
*/
|
|
242
268
|
class Delaunay {
|
|
269
|
+
/**
|
|
270
|
+
* Computes the Delaunay triangulation of the given points.
|
|
271
|
+
* @param vertices Points to triangulate.
|
|
272
|
+
* @returns A flat array of triangle indices into {@link vertices} (each consecutive group of 3
|
|
273
|
+
* entries identifies one triangle), in the same "vertices + triangle indices" format used by
|
|
274
|
+
* most graphics/meshing libraries. Returns an empty array if fewer than 3 points are provided.
|
|
275
|
+
*/
|
|
243
276
|
static triangulate(vertices) {
|
|
244
277
|
return delaunayTriangulation(vertices);
|
|
245
278
|
}
|
|
@@ -256,6 +289,7 @@
|
|
|
256
289
|
return s != null && Array.isArray(s) && s.length === 2
|
|
257
290
|
&& isPoint(s[0]) && isPoint(s[1]);
|
|
258
291
|
}
|
|
292
|
+
/** Utility tools for 2D geometry: points, segments, polygons and their interactions. */
|
|
259
293
|
let Utils$2 = class Utils {
|
|
260
294
|
/**
|
|
261
295
|
* Computes the slope (in radians) of the segment joining two points.
|
|
@@ -303,6 +337,11 @@
|
|
|
303
337
|
return null;
|
|
304
338
|
}
|
|
305
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Computes the axis-aligned bounding box of a set of points.
|
|
342
|
+
* @param vertices Points to enclose.
|
|
343
|
+
* @returns The smallest rect containing all provided points.
|
|
344
|
+
*/
|
|
306
345
|
static boundingBox(vertices) {
|
|
307
346
|
let xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = -Number.MAX_VALUE, ymax = -Number.MAX_VALUE;
|
|
308
347
|
vertices.length;
|
|
@@ -314,6 +353,15 @@
|
|
|
314
353
|
}
|
|
315
354
|
return { x: xmin, y: ymin, width: xmax - xmin, height: ymax - ymin };
|
|
316
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Combines two polygons using a boolean set operator (union, intersection or difference).
|
|
358
|
+
* Internally triangulates the merged vertex set (via {@link mesh}) and filters the resulting
|
|
359
|
+
* triangles by whether their barycenter falls inside either source polygon.
|
|
360
|
+
* @param polygon1 First polygon.
|
|
361
|
+
* @param polygon2 Second polygon.
|
|
362
|
+
* @param operator Boolean operator to apply.
|
|
363
|
+
* @returns The resulting polygon(s), plus the underlying triangulation mesh and triangles used to compute them; or `null` if the operation yields no polygon.
|
|
364
|
+
*/
|
|
317
365
|
static combinePolygons(polygon1, polygon2, operator) {
|
|
318
366
|
return Utils._mergePolygons(polygon1, polygon2, operator);
|
|
319
367
|
}
|
|
@@ -720,6 +768,12 @@
|
|
|
720
768
|
const minx = Math.min(segment[0].x, segment[1].x), maxx = Math.max(segment[0].x, segment[1].x), miny = Math.min(segment[0].y, segment[1].y), maxy = Math.max(segment[0].y, segment[1].y);
|
|
721
769
|
return p.x >= minx && p.x <= maxx && p.y >= miny && p.y <= maxy && Utils.inLine(p, segment, precision);
|
|
722
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* Checks whether a point lies inside (or on the boundary of) a triangle, by verifying that it
|
|
773
|
+
* is on the same rotational side of all three edges.
|
|
774
|
+
* @param p Point to test.
|
|
775
|
+
* @param triangle Triangle vertices.
|
|
776
|
+
*/
|
|
723
777
|
static inTriangle(p, triangle) {
|
|
724
778
|
let last;
|
|
725
779
|
for (let j = 0; j < 3; j++) {
|
|
@@ -850,24 +904,52 @@
|
|
|
850
904
|
&& (vertices = obj.vertices).length >= 3
|
|
851
905
|
&& vertices.every(i => pacemFoundation.Point.isPoint(i));
|
|
852
906
|
}
|
|
907
|
+
/** Utility tools to build and inspect {@link Polygon}s. */
|
|
853
908
|
class Polygon {
|
|
854
909
|
static { this._eps = 1e-12; }
|
|
910
|
+
/**
|
|
911
|
+
* Type guard checking whether the provided value is a valid {@link Polygon} (has at least 3 point vertices).
|
|
912
|
+
* @param obj
|
|
913
|
+
*/
|
|
855
914
|
static isPolygon(obj) {
|
|
856
915
|
return isPolygon(obj);
|
|
857
916
|
}
|
|
917
|
+
/**
|
|
918
|
+
* Builds a polygon from the provided vertices, in order.
|
|
919
|
+
* @param points Vertices of the polygon.
|
|
920
|
+
*/
|
|
858
921
|
static from(...points) {
|
|
859
922
|
return { vertices: Array.from(points) };
|
|
860
923
|
}
|
|
924
|
+
/**
|
|
925
|
+
* Checks whether a point lies inside the polygon.
|
|
926
|
+
* @param polygon
|
|
927
|
+
* @param p Point to test.
|
|
928
|
+
*/
|
|
861
929
|
static contains(polygon, p) {
|
|
862
930
|
return Utils$2.inPolygon(p, polygon.vertices, 12);
|
|
863
931
|
}
|
|
932
|
+
/**
|
|
933
|
+
* Returns the center of the polygon's bounding box.
|
|
934
|
+
* Note: this is not the geometric centroid (center of mass) for non-symmetric polygons.
|
|
935
|
+
* @param polygon
|
|
936
|
+
*/
|
|
864
937
|
static centroid(polygon) {
|
|
865
938
|
const bbox = Polygon.boundingBox(polygon);
|
|
866
939
|
return { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 };
|
|
867
940
|
}
|
|
941
|
+
/**
|
|
942
|
+
* Computes the axis-aligned bounding box of the polygon.
|
|
943
|
+
* @param polygon
|
|
944
|
+
*/
|
|
868
945
|
static boundingBox(polygon) {
|
|
869
946
|
return Utils$2.boundingBox(polygon.vertices);
|
|
870
947
|
}
|
|
948
|
+
/**
|
|
949
|
+
* Returns the polygon's edges as an ordered list of segments, each joining a vertex to the next
|
|
950
|
+
* (the last one wrapping back to the first).
|
|
951
|
+
* @param polygon
|
|
952
|
+
*/
|
|
871
953
|
static sides(polygon) {
|
|
872
954
|
const { vertices } = polygon;
|
|
873
955
|
const retval = [];
|
|
@@ -877,6 +959,10 @@
|
|
|
877
959
|
}
|
|
878
960
|
return retval;
|
|
879
961
|
}
|
|
962
|
+
/**
|
|
963
|
+
* Checks whether the polygon is convex, i.e. all its interior angles turn in the same rotational direction.
|
|
964
|
+
* @param polygon
|
|
965
|
+
*/
|
|
880
966
|
static isConvex(polygon) {
|
|
881
967
|
const { vertices } = polygon, l = vertices.length;
|
|
882
968
|
if (l <= 3) {
|
|
@@ -965,6 +1051,12 @@
|
|
|
965
1051
|
static area(polygon) {
|
|
966
1052
|
return Utils$2.area(polygon.vertices);
|
|
967
1053
|
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Computes the intersection area(s) between two polygons.
|
|
1056
|
+
* @param polygon1
|
|
1057
|
+
* @param polygon2
|
|
1058
|
+
* @returns The overlapping area(s) as one or more polygons.
|
|
1059
|
+
*/
|
|
968
1060
|
static intersect(polygon1, polygon2) {
|
|
969
1061
|
return Utils$2.intersect(polygon1, polygon2);
|
|
970
1062
|
}
|
|
@@ -983,7 +1075,13 @@
|
|
|
983
1075
|
// namespace Pacem.Geometry.LinearAlgebra {
|
|
984
1076
|
const RAD2DEG = 180.0 / Math.PI;
|
|
985
1077
|
const DEG2RAD = 1.0 / RAD2DEG;
|
|
1078
|
+
/** 3D vector utils. */
|
|
986
1079
|
class Vector3D {
|
|
1080
|
+
/**
|
|
1081
|
+
* Builds a {@link Vector3D} from its x, y, z components, in that order.
|
|
1082
|
+
* @param args Exactly 3 numbers: x, y, z.
|
|
1083
|
+
* @throws If the number of arguments is not exactly 3.
|
|
1084
|
+
*/
|
|
987
1085
|
static from(...args) {
|
|
988
1086
|
const l = 3;
|
|
989
1087
|
if (args.length !== l) {
|
|
@@ -991,6 +1089,11 @@
|
|
|
991
1089
|
}
|
|
992
1090
|
return { x: args[0], y: args[1], z: args[2] };
|
|
993
1091
|
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Parses a string representation (e.g. `"1 2 3"` or `"1,2,3"`) into a {@link Vector3D}.
|
|
1094
|
+
* @param input String to parse.
|
|
1095
|
+
* @throws If the input cannot be parsed as exactly 3 numbers.
|
|
1096
|
+
*/
|
|
994
1097
|
static parse(input) {
|
|
995
1098
|
const arr = pacemFoundation.parseAsNumericalArray(input);
|
|
996
1099
|
if (arr && arr.length === 3) {
|
|
@@ -999,15 +1102,19 @@
|
|
|
999
1102
|
throw new Error(`Cannot parse "${input}" as a valid Vector3D.`);
|
|
1000
1103
|
}
|
|
1001
1104
|
// notable vectors
|
|
1105
|
+
/** Returns the unit vector along the x axis: `(1, 0, 0)`. */
|
|
1002
1106
|
static i() {
|
|
1003
1107
|
return { x: 1, y: 0, z: 0 };
|
|
1004
1108
|
}
|
|
1109
|
+
/** Returns the unit vector along the y axis: `(0, 1, 0)`. */
|
|
1005
1110
|
static j() {
|
|
1006
1111
|
return { x: 0, y: 1, z: 0 };
|
|
1007
1112
|
}
|
|
1113
|
+
/** Returns the unit vector along the z axis: `(0, 0, 1)`. */
|
|
1008
1114
|
static k() {
|
|
1009
1115
|
return { x: 0, y: 0, z: 1 };
|
|
1010
1116
|
}
|
|
1117
|
+
/** Returns the zero vector: `(0, 0, 0)`. */
|
|
1011
1118
|
static zero() {
|
|
1012
1119
|
return { x: 0, y: 0, z: 0 };
|
|
1013
1120
|
}
|
|
@@ -1055,6 +1162,10 @@
|
|
|
1055
1162
|
static scale(v, fx, fy = fx, fz = fx) {
|
|
1056
1163
|
return { x: v.x * fx, y: v.y * fy, z: v.z * fz };
|
|
1057
1164
|
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Returns the squared magnitude (length) of the vector. Cheaper than {@link mag} since it avoids the square root.
|
|
1167
|
+
* @param v
|
|
1168
|
+
*/
|
|
1058
1169
|
static magSqr(v) {
|
|
1059
1170
|
return v.x * v.x + v.y * v.y + v.z * v.z;
|
|
1060
1171
|
}
|
|
@@ -1069,9 +1180,17 @@
|
|
|
1069
1180
|
&& (v2.y - v1.y).isCloseTo(0)
|
|
1070
1181
|
&& (v2.z - v1.z).isCloseTo(0);
|
|
1071
1182
|
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Returns the magnitude (length) of the vector.
|
|
1185
|
+
* @param v
|
|
1186
|
+
*/
|
|
1072
1187
|
static mag(v) {
|
|
1073
1188
|
return Math.sqrt(Vector3D.magSqr(v));
|
|
1074
1189
|
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Returns the opposite vector (each component negated).
|
|
1192
|
+
* @param v
|
|
1193
|
+
*/
|
|
1075
1194
|
static negate(v) {
|
|
1076
1195
|
return { x: -v.x, y: -v.y, z: -v.z };
|
|
1077
1196
|
}
|
|
@@ -1121,6 +1240,10 @@
|
|
|
1121
1240
|
return RAD2DEG * num;
|
|
1122
1241
|
}
|
|
1123
1242
|
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Utilities to convert between spherical coordinates (radial distance `rho`, azimuth `theta` and
|
|
1245
|
+
* polar angle `phi`) and their cartesian ({@link Vector3D}) or rotation-matrix ({@link Matrix3D}) equivalents.
|
|
1246
|
+
*/
|
|
1124
1247
|
class Spherical {
|
|
1125
1248
|
/**
|
|
1126
1249
|
* Creates a new {@link Spherical} object.
|
|
@@ -1143,6 +1266,10 @@
|
|
|
1143
1266
|
const theta = Math.atan2(v.x, v.z), phi = Math.acos((v.y / rho).clamp(-1, 1));
|
|
1144
1267
|
return Spherical.from(rho, theta * RAD2DEG, phi * RAD2DEG);
|
|
1145
1268
|
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Converts {@link Spherical} coordinates back into cartesian {@link Vector3D} coordinates.
|
|
1271
|
+
* @param coords
|
|
1272
|
+
*/
|
|
1146
1273
|
static toVector(coords) {
|
|
1147
1274
|
const { rho, theta: thetaDeg, phi: phiDeg } = coords;
|
|
1148
1275
|
const phi = phiDeg * DEG2RAD;
|
|
@@ -1202,6 +1329,7 @@
|
|
|
1202
1329
|
return Matrix3D.from((((m1.m11 * m2.m11) + (m1.m12 * m2.m21)) + (m1.m13 * m2.m31)) + (m1.m14 * m2.offsetX), (((m1.m11 * m2.m12) + (m1.m12 * m2.m22)) + (m1.m13 * m2.m32)) + (m1.m14 * m2.offsetY), (((m1.m11 * m2.m13) + (m1.m12 * m2.m23)) + (m1.m13 * m2.m33)) + (m1.m14 * m2.offsetZ), (((m1.m11 * m2.m14) + (m1.m12 * m2.m24)) + (m1.m13 * m2.m34)) + (m1.m14 * m2.m44), (((m1.m21 * m2.m11) + (m1.m22 * m2.m21)) + (m1.m23 * m2.m31)) + (m1.m24 * m2.offsetX), (((m1.m21 * m2.m12) + (m1.m22 * m2.m22)) + (m1.m23 * m2.m32)) + (m1.m24 * m2.offsetY), (((m1.m21 * m2.m13) + (m1.m22 * m2.m23)) + (m1.m23 * m2.m33)) + (m1.m24 * m2.offsetZ), (((m1.m21 * m2.m14) + (m1.m22 * m2.m24)) + (m1.m23 * m2.m34)) + (m1.m24 * m2.m44), (((m1.m31 * m2.m11) + (m1.m32 * m2.m21)) + (m1.m33 * m2.m31)) + (m1.m34 * m2.offsetX), (((m1.m31 * m2.m12) + (m1.m32 * m2.m22)) + (m1.m33 * m2.m32)) + (m1.m34 * m2.offsetY), (((m1.m31 * m2.m13) + (m1.m32 * m2.m23)) + (m1.m33 * m2.m33)) + (m1.m34 * m2.offsetZ), (((m1.m31 * m2.m14) + (m1.m32 * m2.m24)) + (m1.m33 * m2.m34)) + (m1.m34 * m2.m44), (((m1.offsetX * m2.m11) + (m1.offsetY * m2.m21)) + (m1.offsetZ * m2.m31)) + (m1.m44 * m2.offsetX), (((m1.offsetX * m2.m12) + (m1.offsetY * m2.m22)) + (m1.offsetZ * m2.m32)) + (m1.m44 * m2.offsetY), (((m1.offsetX * m2.m13) + (m1.offsetY * m2.m23)) + (m1.offsetZ * m2.m33)) + (m1.m44 * m2.offsetZ), (((m1.offsetX * m2.m14) + (m1.offsetY * m2.m24)) + (m1.offsetZ * m2.m34)) + (m1.m44 * m2.m44));
|
|
1203
1330
|
}
|
|
1204
1331
|
}
|
|
1332
|
+
/** Utilities to build, combine and invert 4x4 transformation matrices ({@link Matrix3D}) in 3D space. */
|
|
1205
1333
|
class Matrix3D {
|
|
1206
1334
|
//#region notable matrices
|
|
1207
1335
|
/**
|
|
@@ -1217,6 +1345,11 @@
|
|
|
1217
1345
|
};
|
|
1218
1346
|
}
|
|
1219
1347
|
//#endregion
|
|
1348
|
+
/**
|
|
1349
|
+
* Builds a {@link Matrix3D} from its 16 components, in row-major order.
|
|
1350
|
+
* @param args Exactly 16 numbers.
|
|
1351
|
+
* @throws If the number of arguments is not exactly 16.
|
|
1352
|
+
*/
|
|
1220
1353
|
static from(...args) {
|
|
1221
1354
|
const l = 16;
|
|
1222
1355
|
if (args.length !== l) {
|
|
@@ -1241,9 +1374,17 @@
|
|
|
1241
1374
|
m44: args[15]
|
|
1242
1375
|
};
|
|
1243
1376
|
}
|
|
1377
|
+
/**
|
|
1378
|
+
* Returns the transpose of the given matrix (rows and columns swapped).
|
|
1379
|
+
* @param m
|
|
1380
|
+
*/
|
|
1244
1381
|
static transpose(m) {
|
|
1245
1382
|
return Matrix3D.from(m.m11, m.m21, m.m31, m.offsetX, m.m12, m.m22, m.m32, m.offsetY, m.m13, m.m23, m.m33, m.offsetZ, m.m14, m.m24, m.m34, m.m44);
|
|
1246
1383
|
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Flattens the matrix into a 16-number array, in row-major order (the same order accepted by {@link from}).
|
|
1386
|
+
* @param m
|
|
1387
|
+
*/
|
|
1247
1388
|
static toArray(m) {
|
|
1248
1389
|
return [m.m11, m.m12, m.m13, m.m14,
|
|
1249
1390
|
m.m21, m.m22, m.m23, m.m24,
|
|
@@ -1269,6 +1410,11 @@
|
|
|
1269
1410
|
s.m33 *= z;
|
|
1270
1411
|
});
|
|
1271
1412
|
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Returns a copy of the matrix translated (offset) by the given vector.
|
|
1415
|
+
* @param m
|
|
1416
|
+
* @param offset
|
|
1417
|
+
*/
|
|
1272
1418
|
static translate(m, offset) {
|
|
1273
1419
|
return Matrix3DUtils.modify(m, s => {
|
|
1274
1420
|
s.offsetX += offset.x;
|
|
@@ -1276,6 +1422,11 @@
|
|
|
1276
1422
|
s.offsetZ += offset.z;
|
|
1277
1423
|
});
|
|
1278
1424
|
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Parses a string representation of a matrix (16 numbers) into a {@link Matrix3D}.
|
|
1427
|
+
* @param input String to parse.
|
|
1428
|
+
* @throws If the input cannot be parsed as exactly 16 numbers.
|
|
1429
|
+
*/
|
|
1279
1430
|
static parse(input) {
|
|
1280
1431
|
const arr = pacemFoundation.parseAsNumericalArray(input);
|
|
1281
1432
|
if (arr && arr.length === 16) {
|
|
@@ -1283,15 +1434,28 @@
|
|
|
1283
1434
|
}
|
|
1284
1435
|
throw new Error(`Cannot parse "${input}" as a valid Matrix3D.`);
|
|
1285
1436
|
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Checks whether the matrix is the identity matrix.
|
|
1439
|
+
* @param m
|
|
1440
|
+
*/
|
|
1286
1441
|
static isIdentity(m) {
|
|
1287
1442
|
return m.m11 == 1.0 && m.m12 == .0 && m.m13 == .0 && m.m14 == .0
|
|
1288
1443
|
&& m.m21 == .0 && m.m22 == 1.0 && m.m23 == .0 && m.m24 == .0
|
|
1289
1444
|
&& m.m31 == .0 && m.m32 == .0 && m.m33 == 1.0 && m.m34 == .0
|
|
1290
1445
|
&& m.offsetX == .0 && m.offsetY == .0 && m.offsetZ == .0 && m.m44 == 1.0;
|
|
1291
1446
|
}
|
|
1447
|
+
/**
|
|
1448
|
+
* Checks whether the matrix represents a pure affine transform, i.e. it has no perspective
|
|
1449
|
+
* component (its 4th column is `(0, 0, 0, 1)`).
|
|
1450
|
+
* @param m
|
|
1451
|
+
*/
|
|
1292
1452
|
static isAffine(m) {
|
|
1293
1453
|
return m.m14 == .0 && m.m24 == .0 && m.m34 == .0 && m.m44 == 1.0;
|
|
1294
1454
|
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Computes the determinant of the matrix.
|
|
1457
|
+
* @param m
|
|
1458
|
+
*/
|
|
1295
1459
|
static determinant(m) {
|
|
1296
1460
|
if (Matrix3D.isIdentity(m)) {
|
|
1297
1461
|
return 1.0;
|
|
@@ -1332,6 +1496,12 @@
|
|
|
1332
1496
|
}
|
|
1333
1497
|
return m;
|
|
1334
1498
|
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Computes the inverse of the matrix, using the cofactor/adjugate method (with a specialized,
|
|
1501
|
+
* cheaper path for affine matrices).
|
|
1502
|
+
* @param m
|
|
1503
|
+
* @returns The inverse matrix, or `null` if the matrix is singular (determinant is 0).
|
|
1504
|
+
*/
|
|
1335
1505
|
static invert(m) {
|
|
1336
1506
|
if (Matrix3D.isAffine(m)) {
|
|
1337
1507
|
// normalize affine invert
|
|
@@ -1436,10 +1606,17 @@
|
|
|
1436
1606
|
return pt;
|
|
1437
1607
|
}
|
|
1438
1608
|
}
|
|
1609
|
+
/** Utilities to build, combine and convert {@link Quaternion} rotations. */
|
|
1439
1610
|
class Quaternion {
|
|
1611
|
+
/** Returns the identity quaternion (no rotation). */
|
|
1440
1612
|
static identity() {
|
|
1441
1613
|
return Quaternion.from(0, 0, 0, 1);
|
|
1442
1614
|
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Builds a {@link Quaternion} from its x, y, z, w components, in that order.
|
|
1617
|
+
* @param args Exactly 4 numbers: x, y, z, w.
|
|
1618
|
+
* @throws If the number of arguments is not exactly 4.
|
|
1619
|
+
*/
|
|
1443
1620
|
static from(...args) {
|
|
1444
1621
|
const l = 4;
|
|
1445
1622
|
if (args.length !== l) {
|
|
@@ -1452,6 +1629,11 @@
|
|
|
1452
1629
|
w: args[3],
|
|
1453
1630
|
};
|
|
1454
1631
|
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Parses a string representation of a quaternion (4 numbers) into a {@link Quaternion}.
|
|
1634
|
+
* @param input String to parse.
|
|
1635
|
+
* @throws If the input cannot be parsed as exactly 4 numbers.
|
|
1636
|
+
*/
|
|
1455
1637
|
static parse(input) {
|
|
1456
1638
|
const arr = pacemFoundation.parseAsNumericalArray(input);
|
|
1457
1639
|
if (arr && arr.length === 4) {
|
|
@@ -1459,6 +1641,12 @@
|
|
|
1459
1641
|
}
|
|
1460
1642
|
throw new Error(`Cannot parse "${input}" as a valid Quaternion.`);
|
|
1461
1643
|
}
|
|
1644
|
+
/**
|
|
1645
|
+
* Builds the (shortest-arc) quaternion representing the rotation that takes one unit vector onto another.
|
|
1646
|
+
* Normalizes both input vectors in place as a side effect.
|
|
1647
|
+
* @param from Source direction.
|
|
1648
|
+
* @param to Target direction.
|
|
1649
|
+
*/
|
|
1462
1650
|
static fromVectors(from, to) {
|
|
1463
1651
|
// setup
|
|
1464
1652
|
Vector3D.normalize(from);
|
|
@@ -1566,6 +1754,11 @@
|
|
|
1566
1754
|
}
|
|
1567
1755
|
}
|
|
1568
1756
|
}
|
|
1757
|
+
/**
|
|
1758
|
+
* Returns the conjugate of the quaternion (vector part negated). For a unit quaternion, this
|
|
1759
|
+
* represents the inverse rotation.
|
|
1760
|
+
* @param q
|
|
1761
|
+
*/
|
|
1569
1762
|
static conjugate(q) {
|
|
1570
1763
|
return Quaternion.from(-q.x, -q.y, -q.z, q.w);
|
|
1571
1764
|
}
|
|
@@ -1576,15 +1769,29 @@
|
|
|
1576
1769
|
static mag(q) {
|
|
1577
1770
|
return Math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
|
|
1578
1771
|
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Returns the squared magnitude of the vector (imaginary) part of the quaternion.
|
|
1774
|
+
* @param q
|
|
1775
|
+
*/
|
|
1579
1776
|
static norm(q) {
|
|
1580
1777
|
return (q.x * q.x + q.y * q.y + q.z * q.z);
|
|
1581
1778
|
}
|
|
1779
|
+
/**
|
|
1780
|
+
* Returns the (unit) rotation axis represented by the quaternion, defaulting to the y axis
|
|
1781
|
+
* when the quaternion has no vector part (i.e. represents no rotation).
|
|
1782
|
+
* @param q
|
|
1783
|
+
*/
|
|
1582
1784
|
static axis(q) {
|
|
1583
1785
|
if (q.x == .0 && q.y == .0 && q.z == .0) {
|
|
1584
1786
|
return Vector3D.j();
|
|
1585
1787
|
}
|
|
1586
1788
|
return Vector3D.unit(q);
|
|
1587
1789
|
}
|
|
1790
|
+
/**
|
|
1791
|
+
* Rotates a vector by the given quaternion (normalizes the quaternion in place as a side effect).
|
|
1792
|
+
* @param v Vector to rotate.
|
|
1793
|
+
* @param q Rotation quaternion.
|
|
1794
|
+
*/
|
|
1588
1795
|
static transform(v, q) {
|
|
1589
1796
|
Quaternion.normalize(q);
|
|
1590
1797
|
const cross = Vector3D.cross(q, v);
|
|
@@ -1613,6 +1820,10 @@
|
|
|
1613
1820
|
}
|
|
1614
1821
|
return (Math.atan2(y, x) * 114.59155902616465);
|
|
1615
1822
|
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Builds the rotation matrix equivalent to the given quaternion.
|
|
1825
|
+
* @param q
|
|
1826
|
+
*/
|
|
1616
1827
|
static toRotationMatrix(q) {
|
|
1617
1828
|
var m = Matrix3D.identity();
|
|
1618
1829
|
var X = q.x;
|
|
@@ -1630,6 +1841,10 @@
|
|
|
1630
1841
|
m.m33 = 1.0 - 2.0 * X * X - 2.0 * Y * Y;
|
|
1631
1842
|
return m;
|
|
1632
1843
|
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Returns the inverse rotation of the given quaternion (the conjugate of its normalized form).
|
|
1846
|
+
* @param q
|
|
1847
|
+
*/
|
|
1633
1848
|
static invert(q) {
|
|
1634
1849
|
const n = Quaternion.unit(q);
|
|
1635
1850
|
return Quaternion.conjugate(n);
|
|
@@ -1657,6 +1872,7 @@
|
|
|
1657
1872
|
|
|
1658
1873
|
//namespace Pacem.Geometry {
|
|
1659
1874
|
//#region Utils
|
|
1875
|
+
/** Utility tools for 3D geometry: rays, triangles and their interactions. */
|
|
1660
1876
|
class Utils3D {
|
|
1661
1877
|
/**
|
|
1662
1878
|
* Computes the intersection between a ray and a triangle in the 3D space - if any - using the Möller–Trumbore algorithm.
|
|
@@ -1731,6 +1947,7 @@
|
|
|
1731
1947
|
function nac() {
|
|
1732
1948
|
return NOT_A_COMPLEX || (NOT_A_COMPLEX = buildComplex(Number.NaN, Number.NaN));
|
|
1733
1949
|
}
|
|
1950
|
+
/** Utility tools to build and operate on {@link Complex} numbers. */
|
|
1734
1951
|
class Complex {
|
|
1735
1952
|
static build(real, img) {
|
|
1736
1953
|
if (this.isComplex(real)) {
|
|
@@ -1742,20 +1959,41 @@
|
|
|
1742
1959
|
}
|
|
1743
1960
|
return buildComplex(real, img || 0);
|
|
1744
1961
|
}
|
|
1962
|
+
/**
|
|
1963
|
+
* Adds two complex numbers (real numbers are treated as having a zero imaginary part).
|
|
1964
|
+
* @param a First addend.
|
|
1965
|
+
* @param b Second addend.
|
|
1966
|
+
*/
|
|
1745
1967
|
static add(a, b) {
|
|
1746
1968
|
const ac = complex(a), bc = complex(b);
|
|
1747
1969
|
return buildComplex(ac.real + bc.real, ac.img + bc.img);
|
|
1748
1970
|
}
|
|
1971
|
+
/**
|
|
1972
|
+
* Subtracts one complex number from another.
|
|
1973
|
+
* @param from Minuend.
|
|
1974
|
+
* @param what Subtrahend.
|
|
1975
|
+
*/
|
|
1749
1976
|
static subtract(from, what) {
|
|
1750
1977
|
const ac = complex(from), bc = complex(what);
|
|
1751
1978
|
return buildComplex(ac.real - bc.real, ac.img - bc.img);
|
|
1752
1979
|
}
|
|
1980
|
+
/**
|
|
1981
|
+
* Multiplies two complex numbers.
|
|
1982
|
+
* @param a First factor.
|
|
1983
|
+
* @param b Second factor.
|
|
1984
|
+
*/
|
|
1753
1985
|
static multiply(a, b) {
|
|
1754
1986
|
const ac = complex(a), bc = complex(b);
|
|
1755
1987
|
return buildComplex(
|
|
1756
1988
|
/* real*/ ac.real * bc.real - ac.img * bc.img,
|
|
1757
1989
|
/* img */ ac.real * bc.img + ac.img * bc.real);
|
|
1758
1990
|
}
|
|
1991
|
+
/**
|
|
1992
|
+
* Divides one complex number by another.
|
|
1993
|
+
* @param a Dividend.
|
|
1994
|
+
* @param b Divisor.
|
|
1995
|
+
* @returns The quotient, or {@link Complex.NaC} if the divisor is (close to) zero.
|
|
1996
|
+
*/
|
|
1759
1997
|
static divide(a, b) {
|
|
1760
1998
|
const ac = complex(a), bc = complex(b);
|
|
1761
1999
|
const div = this.absSquare(bc).roundoff();
|
|
@@ -1767,20 +2005,42 @@
|
|
|
1767
2005
|
/* real*/ inv_div * (ac.real * bc.real + ac.img * bc.img),
|
|
1768
2006
|
/* img */ inv_div * (ac.img * bc.real - ac.real * bc.img));
|
|
1769
2007
|
}
|
|
2008
|
+
/**
|
|
2009
|
+
* Returns the squared modulus (|z|²) of the complex number, i.e. real² + img².
|
|
2010
|
+
* Cheaper than {@link modulus} since it avoids the square root.
|
|
2011
|
+
* @param c
|
|
2012
|
+
*/
|
|
1770
2013
|
static absSquare(c) {
|
|
1771
2014
|
const ac = complex(c);
|
|
1772
2015
|
return Math.pow(ac.real, 2) + Math.pow(ac.img, 2);
|
|
1773
2016
|
}
|
|
2017
|
+
/**
|
|
2018
|
+
* Returns the modulus (magnitude) of the complex number.
|
|
2019
|
+
* @param c
|
|
2020
|
+
*/
|
|
1774
2021
|
static modulus(c) {
|
|
1775
2022
|
return Math.sqrt(this.absSquare(c));
|
|
1776
2023
|
}
|
|
2024
|
+
/**
|
|
2025
|
+
* Type guard checking whether the provided value is a {@link Complex} number.
|
|
2026
|
+
* @param c
|
|
2027
|
+
*/
|
|
1777
2028
|
static isComplex(c) {
|
|
1778
2029
|
return c != null && typeof c === 'object' && 'real' in c && 'img' in c && typeof c.real === 'number' && typeof c.img === 'number';
|
|
1779
2030
|
}
|
|
2031
|
+
/**
|
|
2032
|
+
* Returns the complex conjugate: same real part, negated imaginary part.
|
|
2033
|
+
* @param a
|
|
2034
|
+
*/
|
|
1780
2035
|
static conjugate(a) {
|
|
1781
2036
|
a = complex(a);
|
|
1782
2037
|
return buildComplex(a.real, Math.abs(a.img) == 0 ? 0 : -a.img);
|
|
1783
2038
|
}
|
|
2039
|
+
/**
|
|
2040
|
+
* Checks whether two complex numbers have the same real and imaginary parts.
|
|
2041
|
+
* @param c1
|
|
2042
|
+
* @param c2
|
|
2043
|
+
*/
|
|
1784
2044
|
static equals(c1, c2) {
|
|
1785
2045
|
const c_1 = this.build(c1), c_2 = this.build(c2);
|
|
1786
2046
|
if (!this.isComplex(c1) || !this.isComplex(c2)) {
|
|
@@ -1800,6 +2060,7 @@
|
|
|
1800
2060
|
// if (!this.isComplex( c)
|
|
1801
2061
|
// }
|
|
1802
2062
|
//}
|
|
2063
|
+
/** The "Not a Complex" sentinel value (`NaN + NaNi`), returned by operations on invalid input. */
|
|
1803
2064
|
static get NaC() { return nac(); }
|
|
1804
2065
|
}
|
|
1805
2066
|
//}
|
|
@@ -1840,6 +2101,7 @@
|
|
|
1840
2101
|
}
|
|
1841
2102
|
return retval;
|
|
1842
2103
|
}
|
|
2104
|
+
/** Discrete Fourier Transform (DFT) utilities, including a Cooley-Tukey FFT fast path for power-of-two inputs. */
|
|
1843
2105
|
class Fourier {
|
|
1844
2106
|
/**
|
|
1845
2107
|
* Checks the input vector and outputs a frequency vector using the best performing algo.
|
|
@@ -1913,12 +2175,19 @@
|
|
|
1913
2175
|
t * (-0.82215223 + t * 0.17087277)))))))));
|
|
1914
2176
|
return x >= 0.0 ? ans : 2.0 - ans;
|
|
1915
2177
|
}
|
|
2178
|
+
/** Represents a Gaussian (normal) distribution and exposes its density and cumulative probability functions. */
|
|
1916
2179
|
class Gaussian {
|
|
2180
|
+
/**
|
|
2181
|
+
* Creates a new {@link Gaussian} distribution with the given mean and standard deviation.
|
|
2182
|
+
* @param mean Mean (μ) of the distribution.
|
|
2183
|
+
* @param stdev Standard deviation (σ) of the distribution (its absolute value is used).
|
|
2184
|
+
*/
|
|
1917
2185
|
constructor(mean, stdev) {
|
|
1918
2186
|
this.mean = mean;
|
|
1919
2187
|
this.stdev = Math.abs(stdev);
|
|
1920
2188
|
this.variance = Math.pow(stdev, 2);
|
|
1921
2189
|
}
|
|
2190
|
+
/** The standard normal distribution (mean 0, standard deviation 1). */
|
|
1922
2191
|
static get normal() {
|
|
1923
2192
|
return _normal;
|
|
1924
2193
|
}
|
|
@@ -1955,7 +2224,13 @@
|
|
|
1955
2224
|
const _normal = new Gaussian(0, 1);
|
|
1956
2225
|
//}
|
|
1957
2226
|
|
|
2227
|
+
/**
|
|
2228
|
+
* Lagrange polynomial interpolation, evaluated using the numerically stable barycentric form.
|
|
2229
|
+
* Given a set of `(x, y)` points, builds the unique polynomial of minimal degree passing through
|
|
2230
|
+
* all of them and evaluates it at arbitrary x values.
|
|
2231
|
+
*/
|
|
1958
2232
|
class Lagrange {
|
|
2233
|
+
/** Use {@link Lagrange.create} to build an instance. */
|
|
1959
2234
|
constructor(points) {
|
|
1960
2235
|
this.#set = points;
|
|
1961
2236
|
}
|
|
@@ -2031,6 +2306,11 @@
|
|
|
2031
2306
|
}
|
|
2032
2307
|
// Iterative/global Lagrange computation removed — barycentric is used
|
|
2033
2308
|
// exclusively for interpolation.
|
|
2309
|
+
/**
|
|
2310
|
+
* Evaluates the Lagrange interpolating polynomial at the given x, using barycentric weights.
|
|
2311
|
+
* @param x Input value.
|
|
2312
|
+
* @returns The interpolated y value (the exact node y if x coincides with a node).
|
|
2313
|
+
*/
|
|
2034
2314
|
interpolate(x) {
|
|
2035
2315
|
return this._computeBarycentric(x);
|
|
2036
2316
|
}
|
|
@@ -2052,7 +2332,14 @@
|
|
|
2052
2332
|
const Lagrangian = {
|
|
2053
2333
|
create: Lagrange.create
|
|
2054
2334
|
};
|
|
2335
|
+
/**
|
|
2336
|
+
* Piecewise Cubic Hermite Interpolating Polynomial (PCHIP).
|
|
2337
|
+
* Builds a shape-preserving (monotonicity-respecting) piecewise-cubic curve through a set of
|
|
2338
|
+
* `(x, y)` points: unlike {@link Lagrange}, it does not overshoot between nodes, at the cost of
|
|
2339
|
+
* only being C¹ (continuous first derivative, not second).
|
|
2340
|
+
*/
|
|
2055
2341
|
class Pchip {
|
|
2342
|
+
/** Use {@link Pchip.create} to build an instance. */
|
|
2056
2343
|
constructor(points) {
|
|
2057
2344
|
if (!points || points.length < 2)
|
|
2058
2345
|
throw new Error('Need at least 2 points');
|
|
@@ -2104,6 +2391,12 @@
|
|
|
2104
2391
|
}
|
|
2105
2392
|
return m;
|
|
2106
2393
|
}
|
|
2394
|
+
/**
|
|
2395
|
+
* Evaluates the PCHIP curve at the given x, using cubic Hermite interpolation on the enclosing segment.
|
|
2396
|
+
* Values outside the node range are linearly extrapolated using the boundary derivative.
|
|
2397
|
+
* @param x Input value.
|
|
2398
|
+
* @returns The interpolated y value.
|
|
2399
|
+
*/
|
|
2107
2400
|
interpolate(x) {
|
|
2108
2401
|
const xs = this.xs, ys = this.ys, ms = this.ms;
|
|
2109
2402
|
const n = xs.length;
|
|
@@ -2148,7 +2441,14 @@
|
|
|
2148
2441
|
return new Pchip(points);
|
|
2149
2442
|
}
|
|
2150
2443
|
}
|
|
2444
|
+
/**
|
|
2445
|
+
* Newton's divided-difference polynomial interpolation.
|
|
2446
|
+
* Builds the same interpolating polynomial as {@link Lagrange} but represents it in Newton form
|
|
2447
|
+
* (a nested/Horner-like product of divided differences), which is efficient to evaluate and to
|
|
2448
|
+
* extend incrementally as new points are added.
|
|
2449
|
+
*/
|
|
2151
2450
|
class Newton {
|
|
2451
|
+
/** Use {@link Newton.create} to build an instance. */
|
|
2152
2452
|
constructor(points) {
|
|
2153
2453
|
if (!points || points.length === 0)
|
|
2154
2454
|
throw new Error('Need at least 1 point');
|
|
@@ -2167,6 +2467,11 @@
|
|
|
2167
2467
|
}
|
|
2168
2468
|
this.coeffs = a;
|
|
2169
2469
|
}
|
|
2470
|
+
/**
|
|
2471
|
+
* Evaluates the Newton form of the interpolating polynomial at the given x, via nested multiplication.
|
|
2472
|
+
* @param x Input value.
|
|
2473
|
+
* @returns The interpolated y value (`NaN` if the interpolator has no points).
|
|
2474
|
+
*/
|
|
2170
2475
|
interpolate(x) {
|
|
2171
2476
|
const xs = this.xs, a = this.coeffs;
|
|
2172
2477
|
const n = a.length;
|
|
@@ -2386,6 +2691,7 @@
|
|
|
2386
2691
|
const fn = searchFnFactory(minAssigner, maxAssigner);
|
|
2387
2692
|
return fn(left, right, weight, tolerance);
|
|
2388
2693
|
};
|
|
2694
|
+
/** Ready-made {@link SearchFunction} implementations for unimodal minimum-search over an interval. */
|
|
2389
2695
|
const SearchFunctions = {
|
|
2390
2696
|
/**
|
|
2391
2697
|
* Creates a linear search function.
|
|
@@ -2416,6 +2722,12 @@
|
|
|
2416
2722
|
const OUT_OF_RANGE = `Radix out of range: possible values go between positive 1 exclusive and ${ALPHABET.length} inclusive`;
|
|
2417
2723
|
/** Utility tools about number theory, 'diophantine' stuff... */
|
|
2418
2724
|
class Utils {
|
|
2725
|
+
/**
|
|
2726
|
+
* Computes the least common multiple (LCM) of the given numbers (each rounded to the nearest integer).
|
|
2727
|
+
* @param args Two or more numbers.
|
|
2728
|
+
* @returns The least common multiple.
|
|
2729
|
+
* @throws If fewer than two numbers are provided.
|
|
2730
|
+
*/
|
|
2419
2731
|
static lcd(...args) {
|
|
2420
2732
|
if (pacemFoundation.NullChecker.isNullOrEmpty(args) || args.length <= 1) {
|
|
2421
2733
|
throw 'Insufficient set of numbers.';
|
|
@@ -2434,6 +2746,12 @@
|
|
|
2434
2746
|
}
|
|
2435
2747
|
return result;
|
|
2436
2748
|
}
|
|
2749
|
+
/**
|
|
2750
|
+
* Computes the greatest common divisor (GCD) of two numbers (each rounded to the nearest integer)
|
|
2751
|
+
* using the Euclidean algorithm.
|
|
2752
|
+
* @param a
|
|
2753
|
+
* @param b
|
|
2754
|
+
*/
|
|
2437
2755
|
static gcd(a, b) {
|
|
2438
2756
|
// force integers
|
|
2439
2757
|
a = Math.round(a),
|
|
@@ -2443,9 +2761,26 @@
|
|
|
2443
2761
|
}
|
|
2444
2762
|
return Utils.gcd(b % a, a);
|
|
2445
2763
|
}
|
|
2764
|
+
/**
|
|
2765
|
+
* Converts an integer value from one numeric base (radix) to another.
|
|
2766
|
+
* @param v Value to convert, in the source radix.
|
|
2767
|
+
* @param from Source radix.
|
|
2768
|
+
* @param to Target radix.
|
|
2769
|
+
* @returns The value re-expressed in the target radix, as a string.
|
|
2770
|
+
*/
|
|
2446
2771
|
static rebaseInt(v, from, to) {
|
|
2447
2772
|
return pacemFoundation.Numbers.rebase(v, from, to);
|
|
2448
2773
|
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Converts a (possibly fractional) numeric value from one radix to another, dispatching to the
|
|
2776
|
+
* appropriate specialized conversion routine depending on whether base-10 is the source, the
|
|
2777
|
+
* target, or neither.
|
|
2778
|
+
* @param v Value to convert, in the source radix.
|
|
2779
|
+
* @param fromRadix Source radix.
|
|
2780
|
+
* @param toRadix Target radix.
|
|
2781
|
+
* @param precision Maximum number of digits to compute for the fractional part (default 12).
|
|
2782
|
+
* @returns The converted value: a number when converting to base 10, otherwise a string.
|
|
2783
|
+
*/
|
|
2449
2784
|
static rebaseFloat(v, fromRadix, toRadix, precision = 12) {
|
|
2450
2785
|
const from10 = Utils.rebaseFloat10ToN, to10 = Utils.rebaseFloatNTo10;
|
|
2451
2786
|
if (fromRadix === 10) {
|
|
@@ -2461,6 +2796,14 @@
|
|
|
2461
2796
|
const _10 = to10(v.toString(), fromRadix, precision + 1);
|
|
2462
2797
|
return from10(_10, toRadix, precision);
|
|
2463
2798
|
}
|
|
2799
|
+
/**
|
|
2800
|
+
* Converts a numeric string expressed in the given radix into its base-10 (decimal) value.
|
|
2801
|
+
* @param v Value to convert, expressed in the source radix (digits from `0-9a-z`).
|
|
2802
|
+
* @param radix Source radix (must be greater than 1 and no larger than the size of the digit alphabet).
|
|
2803
|
+
* @param precision Unused for the integer part; reserved for symmetry with the sibling conversion methods.
|
|
2804
|
+
* @returns The base-10 numeric value, or `NaN` if the input contains invalid digits.
|
|
2805
|
+
* @throws If radix is out of range.
|
|
2806
|
+
*/
|
|
2464
2807
|
static rebaseFloatNTo10(v, radix, precision = 12) {
|
|
2465
2808
|
const alphabet = ALPHABET;
|
|
2466
2809
|
if (radix <= 1 || radix > alphabet.length) {
|
|
@@ -2486,6 +2829,14 @@
|
|
|
2486
2829
|
}
|
|
2487
2830
|
return sign * retval;
|
|
2488
2831
|
}
|
|
2832
|
+
/**
|
|
2833
|
+
* Converts a base-10 number into its representation in another **integer** radix.
|
|
2834
|
+
* @param v Base-10 value to convert.
|
|
2835
|
+
* @param radix Target integer radix (must be greater than 1 and no larger than the size of the digit alphabet).
|
|
2836
|
+
* @param precision Maximum number of digits to compute for the fractional part (default 12).
|
|
2837
|
+
* @returns The value re-expressed in the target radix, as a string.
|
|
2838
|
+
* @throws If radix is out of range.
|
|
2839
|
+
*/
|
|
2489
2840
|
static rebaseFloat10ToNIntBase(v, radix, precision = 12) {
|
|
2490
2841
|
const alphabet = ALPHABET;
|
|
2491
2842
|
if (radix <= 1 || radix > alphabet.length) {
|
|
@@ -2540,6 +2891,14 @@
|
|
|
2540
2891
|
}
|
|
2541
2892
|
return (Math.sign(v) < 0 ? '-' : '') + output + '.' + rebasedFrac.replace(/0+$/, '');
|
|
2542
2893
|
}
|
|
2894
|
+
/**
|
|
2895
|
+
* Converts a base-10 number into its representation in another (possibly non-integer) radix.
|
|
2896
|
+
* @param v Base-10 value to convert.
|
|
2897
|
+
* @param radix Target radix, possibly fractional (must be greater than 1 and no larger than the size of the digit alphabet).
|
|
2898
|
+
* @param precision Maximum number of digits to compute for the fractional part (default 12).
|
|
2899
|
+
* @returns The value re-expressed in the target radix, as a string.
|
|
2900
|
+
* @throws If radix is out of range.
|
|
2901
|
+
*/
|
|
2543
2902
|
static rebaseFloat10ToN(v, radix, precision = 12) {
|
|
2544
2903
|
const alphabet = ALPHABET;
|
|
2545
2904
|
if (radix <= 1 || radix > alphabet.length) {
|