@pacem/pacem-numerical 1.0.0-abel
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/LICENSE +191 -0
- package/NOTICE +4 -0
- package/dist/browser/pacem-numerical.js +2622 -0
- package/dist/browser/pacem-numerical.js.map +1 -0
- package/dist/browser/pacem-numerical.min.js +2 -0
- package/dist/browser/pacem-numerical.min.js.map +1 -0
- package/dist/bundle/pacem-numerical.min.mjs +1 -0
- package/dist/bundle/pacem-numerical.mjs +2459 -0
- package/dist/bundle/pacem-numerical.mjs.map +7 -0
- package/dist/esm/index-geometry-linearalgebra.js +2 -0
- package/dist/esm/index-geometry.js +4 -0
- package/dist/esm/index-iife.js +3 -0
- package/dist/esm/index-mathematics-dataanalysis.js +5 -0
- package/dist/esm/index-mathematics.js +3 -0
- package/dist/esm/index.js +2 -0
- package/package.json +41 -0
- package/typings/index.d.ts +763 -0
|
@@ -0,0 +1,2459 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// packages/numerical/dist/esm/index-geometry.js
|
|
8
|
+
var index_geometry_exports = {};
|
|
9
|
+
__export(index_geometry_exports, {
|
|
10
|
+
LinearAlgebra: () => index_geometry_linearalgebra_exports,
|
|
11
|
+
Polygon: () => Polygon,
|
|
12
|
+
Utils: () => Utils,
|
|
13
|
+
Utils3D: () => Utils3D
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// packages/numerical/dist/esm/geom/geom.js
|
|
17
|
+
import { Point as Point2, Rect, NullChecker } from "@pacem/pacem-foundation";
|
|
18
|
+
|
|
19
|
+
// packages/numerical/dist/esm/geom/linear-algebra.js
|
|
20
|
+
import { Point } from "@pacem/pacem-foundation";
|
|
21
|
+
var Vector = class _Vector {
|
|
22
|
+
/**
|
|
23
|
+
* Returns the unit (normalized) vector having the same direction and sense of the provided one.
|
|
24
|
+
* @param v
|
|
25
|
+
*/
|
|
26
|
+
static unit(v) {
|
|
27
|
+
const clone = { x: v.x, y: v.y };
|
|
28
|
+
this.normalize(clone);
|
|
29
|
+
return clone;
|
|
30
|
+
}
|
|
31
|
+
static magSqr(v) {
|
|
32
|
+
return v.x * v.x + v.y * v.y;
|
|
33
|
+
}
|
|
34
|
+
static mag(v) {
|
|
35
|
+
return Math.sqrt(_Vector.magSqr(v));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Normalizes the provided vector in place.
|
|
39
|
+
* @param v
|
|
40
|
+
*/
|
|
41
|
+
static normalize(v) {
|
|
42
|
+
const l = _Vector.mag(v);
|
|
43
|
+
if (l <= 0) {
|
|
44
|
+
throw "Cannot normalize a vector of length 0.";
|
|
45
|
+
}
|
|
46
|
+
const inv = 1 / l;
|
|
47
|
+
v.x *= inv;
|
|
48
|
+
v.y *= inv;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Returns the vector joining two points.
|
|
52
|
+
* @param p1 Start point
|
|
53
|
+
* @param p2 End point
|
|
54
|
+
*/
|
|
55
|
+
static from(p1, p2) {
|
|
56
|
+
return Point.subtract(p1, p2);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns the dot product between two vectors/points.
|
|
60
|
+
* @param v1
|
|
61
|
+
* @param v2
|
|
62
|
+
*/
|
|
63
|
+
static dot(v1, v2) {
|
|
64
|
+
return v1.x * v2.x + v1.y * v2.y;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns the cross product between two vectors/points (magnitude along the z axis).
|
|
68
|
+
* @param v1
|
|
69
|
+
* @param v2
|
|
70
|
+
*/
|
|
71
|
+
static cross(v1, v2) {
|
|
72
|
+
return v1.x * v2.y - v1.y * v2.x;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Checks if the two points are effectively the same location.
|
|
76
|
+
* @param v1 The first point.
|
|
77
|
+
* @param v2 The second point.
|
|
78
|
+
* @returns True if the points are considered coincident within tolerance; otherwise, false.
|
|
79
|
+
*/
|
|
80
|
+
static areClose(v1, v2) {
|
|
81
|
+
return (v2.x - v1.x).isCloseTo(0) && (v2.y - v1.y).isCloseTo(0);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// packages/numerical/dist/esm/geom/algorithms/delaunay.js
|
|
86
|
+
var Vertex = class {
|
|
87
|
+
constructor(x, y, index = -1) {
|
|
88
|
+
this.x = x;
|
|
89
|
+
this.y = y;
|
|
90
|
+
this.index = index;
|
|
91
|
+
}
|
|
92
|
+
equals(other) {
|
|
93
|
+
return this.x === other.x && this.y === other.y;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
function calcCircumCirc(v0, v1, v2) {
|
|
97
|
+
const ax = v0.x, ay = v0.y;
|
|
98
|
+
const bx = v1.x, by = v1.y;
|
|
99
|
+
const cx = v2.x, cy = v2.y;
|
|
100
|
+
const d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
|
|
101
|
+
if (Math.abs(d) < 1e-12) {
|
|
102
|
+
return { c: { x: 0, y: 0 }, r: Infinity };
|
|
103
|
+
}
|
|
104
|
+
const ux = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
|
|
105
|
+
const uy = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
|
|
106
|
+
const r = Math.hypot(ux - ax, uy - ay);
|
|
107
|
+
return { c: { x: ux, y: uy }, r };
|
|
108
|
+
}
|
|
109
|
+
var Triangle = class {
|
|
110
|
+
constructor(v0, v1, v2) {
|
|
111
|
+
this.v0 = v0;
|
|
112
|
+
this.v1 = v1;
|
|
113
|
+
this.v2 = v2;
|
|
114
|
+
this.circumCirc = calcCircumCirc(v0, v1, v2);
|
|
115
|
+
}
|
|
116
|
+
inCircumcircle(v) {
|
|
117
|
+
const dx = this.circumCirc.c.x - v.x;
|
|
118
|
+
const dy = this.circumCirc.c.y - v.y;
|
|
119
|
+
return Math.hypot(dx, dy) <= this.circumCirc.r + 1e-9;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
function superTriangle(vertices) {
|
|
123
|
+
let minX = Infinity, minY = Infinity;
|
|
124
|
+
let maxX = -Infinity, maxY = -Infinity;
|
|
125
|
+
for (const v of vertices) {
|
|
126
|
+
minX = Math.min(minX, v.x);
|
|
127
|
+
minY = Math.min(minY, v.y);
|
|
128
|
+
maxX = Math.max(maxX, v.x);
|
|
129
|
+
maxY = Math.max(maxY, v.y);
|
|
130
|
+
}
|
|
131
|
+
const dx = (maxX - minX) * 10;
|
|
132
|
+
const dy = (maxY - minY) * 10;
|
|
133
|
+
const v0 = new Vertex(minX - dx, minY - dy * 3);
|
|
134
|
+
const v1 = new Vertex(minX - dx, maxY + dy);
|
|
135
|
+
const v2 = new Vertex(maxX + dx * 3, maxY + dy);
|
|
136
|
+
return new Triangle(v0, v1, v2);
|
|
137
|
+
}
|
|
138
|
+
function uniqueEdges(edges) {
|
|
139
|
+
const unique = [];
|
|
140
|
+
for (let i = 0; i < edges.length; ++i) {
|
|
141
|
+
let isUnique = true;
|
|
142
|
+
for (let j = 0; j < edges.length; ++j) {
|
|
143
|
+
if (i !== j) {
|
|
144
|
+
const e1 = edges[i];
|
|
145
|
+
const e2 = edges[j];
|
|
146
|
+
if (e1.v0.equals(e2.v0) && e1.v1.equals(e2.v1) || e1.v0.equals(e2.v1) && e1.v1.equals(e2.v0)) {
|
|
147
|
+
isUnique = false;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (isUnique)
|
|
153
|
+
unique.push(edges[i]);
|
|
154
|
+
}
|
|
155
|
+
return unique;
|
|
156
|
+
}
|
|
157
|
+
function addVertex(vertex, triangles) {
|
|
158
|
+
const edges = [];
|
|
159
|
+
triangles = triangles.filter((triangle) => {
|
|
160
|
+
if (triangle.inCircumcircle(vertex)) {
|
|
161
|
+
edges.push({ v0: triangle.v0, v1: triangle.v1 });
|
|
162
|
+
edges.push({ v0: triangle.v1, v1: triangle.v2 });
|
|
163
|
+
edges.push({ v0: triangle.v2, v1: triangle.v0 });
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
});
|
|
168
|
+
const unique = uniqueEdges(edges);
|
|
169
|
+
for (const edge of unique) {
|
|
170
|
+
triangles.push(new Triangle(edge.v0, edge.v1, vertex));
|
|
171
|
+
}
|
|
172
|
+
return triangles;
|
|
173
|
+
}
|
|
174
|
+
function triangulate(vertices) {
|
|
175
|
+
if (vertices.length < 3)
|
|
176
|
+
return [];
|
|
177
|
+
const st = superTriangle(vertices);
|
|
178
|
+
let triangles = [st];
|
|
179
|
+
for (const vertex of vertices) {
|
|
180
|
+
triangles = addVertex(vertex, triangles);
|
|
181
|
+
}
|
|
182
|
+
triangles = triangles.filter((t) => !(t.v0 === st.v0 || t.v0 === st.v1 || t.v0 === st.v2 || t.v1 === st.v0 || t.v1 === st.v1 || t.v1 === st.v2 || t.v2 === st.v0 || t.v2 === st.v1 || t.v2 === st.v2));
|
|
183
|
+
return triangles;
|
|
184
|
+
}
|
|
185
|
+
function delaunayTriangulation(vertices) {
|
|
186
|
+
if (vertices.length < 3)
|
|
187
|
+
return [];
|
|
188
|
+
const vertList = vertices.map((p, idx) => new Vertex(p.x, p.y, idx));
|
|
189
|
+
const triangles = triangulate(vertList);
|
|
190
|
+
const indices = [];
|
|
191
|
+
for (const t of triangles) {
|
|
192
|
+
indices.push(t.v0.index, t.v1.index, t.v2.index);
|
|
193
|
+
}
|
|
194
|
+
return indices;
|
|
195
|
+
}
|
|
196
|
+
var Delaunay = class {
|
|
197
|
+
static triangulate(vertices) {
|
|
198
|
+
return delaunayTriangulation(vertices);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// packages/numerical/dist/esm/geom/geom.js
|
|
203
|
+
var RAD2DEG = 180 / Math.PI;
|
|
204
|
+
var RAD_ROUND = 2 * Math.PI;
|
|
205
|
+
var PRECISION = 12;
|
|
206
|
+
function isPoint(p) {
|
|
207
|
+
return Point2.isPoint(p);
|
|
208
|
+
}
|
|
209
|
+
function isSegment(s) {
|
|
210
|
+
return s != null && Array.isArray(s) && s.length === 2 && isPoint(s[0]) && isPoint(s[1]);
|
|
211
|
+
}
|
|
212
|
+
var Utils = class _Utils {
|
|
213
|
+
/**
|
|
214
|
+
* Computes the slope (in radians) of the segment joining two points.
|
|
215
|
+
* @param p1 Point 1
|
|
216
|
+
* @param p2 Point 2
|
|
217
|
+
*/
|
|
218
|
+
static slopeRad(p1, p2) {
|
|
219
|
+
return Math.atan2(p2.y - p1.y, p2.x - p1.x);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Computes the slope (in radians) of the segment joining two points and returns a strictly positive value (0 to 2*pi).
|
|
223
|
+
* @param p1 Point 1
|
|
224
|
+
* @param p2 Point 2
|
|
225
|
+
*/
|
|
226
|
+
static slopeRad2(p1, p2) {
|
|
227
|
+
return (RAD_ROUND + _Utils.slopeRad(p1, p2)) % RAD_ROUND;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Computes the slope (in degrees) of the segment joining two points.
|
|
231
|
+
* @param p1 Point 1
|
|
232
|
+
* @param p2 Point 2
|
|
233
|
+
*/
|
|
234
|
+
static slopeDeg(p1, p2) {
|
|
235
|
+
return _Utils.slopeRad(p1, p2) * RAD2DEG;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Computes the slope (in degrees) of the segment joining two points and returns a strictly positive value (0 to 360).
|
|
239
|
+
* @param p1 Point 1
|
|
240
|
+
* @param p2 Point 2
|
|
241
|
+
*/
|
|
242
|
+
static slopeDeg2(p1, p2) {
|
|
243
|
+
return (360 + _Utils.slopeDeg(p1, p2)) % 360;
|
|
244
|
+
}
|
|
245
|
+
static intersect(arg1, arg2, ...args) {
|
|
246
|
+
if (Array.isArray(arg1) && Array.isArray(arg2)) {
|
|
247
|
+
return _Utils._intersectSegments(arg1, arg2, true);
|
|
248
|
+
} else if (Rect.isRect(arg1)) {
|
|
249
|
+
return _Utils._intersectRects(...arguments);
|
|
250
|
+
} else if ("vertices" in arg1) {
|
|
251
|
+
return _Utils._mergePolygons(arg1, arg2, "intersect")?.polygons ?? null;
|
|
252
|
+
} else {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
static boundingBox(vertices) {
|
|
257
|
+
let xmin = Number.MAX_VALUE, ymin = Number.MAX_VALUE, xmax = -Number.MAX_VALUE, ymax = -Number.MAX_VALUE;
|
|
258
|
+
const l = vertices.length;
|
|
259
|
+
for (let v of vertices) {
|
|
260
|
+
xmin = Math.min(xmin, v.x);
|
|
261
|
+
ymin = Math.min(ymin, v.y);
|
|
262
|
+
xmax = Math.max(xmax, v.x);
|
|
263
|
+
ymax = Math.max(ymax, v.y);
|
|
264
|
+
}
|
|
265
|
+
return { x: xmin, y: ymin, width: xmax - xmin, height: ymax - ymin };
|
|
266
|
+
}
|
|
267
|
+
static combinePolygons(polygon1, polygon2, operator) {
|
|
268
|
+
return _Utils._mergePolygons(polygon1, polygon2, operator);
|
|
269
|
+
}
|
|
270
|
+
static _mergePolygons(polygon1, polygon2, operator) {
|
|
271
|
+
if (NullChecker.isNullOrEmpty(polygon1?.vertices) || NullChecker.isNullOrEmpty(polygon2?.vertices)) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
const contained1 = polygon1.vertices.filter((v) => _Utils.inPolygon(v, polygon2.vertices));
|
|
275
|
+
if (polygon1.vertices.length == contained1.length) {
|
|
276
|
+
return {
|
|
277
|
+
polygons: operator === "intersect" ? [polygon1] : (
|
|
278
|
+
/* hole in difference */
|
|
279
|
+
[polygon2]
|
|
280
|
+
)
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
const contained2 = polygon2.vertices.filter((v) => _Utils.inPolygon(v, polygon1.vertices));
|
|
284
|
+
if (polygon2.vertices.length === contained2.length) {
|
|
285
|
+
return {
|
|
286
|
+
polygons: operator === "intersect" ? [polygon2] : (
|
|
287
|
+
/* hole in difference */
|
|
288
|
+
[polygon1]
|
|
289
|
+
)
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const bbox1 = _Utils.boundingBox(polygon1.vertices), bbox2 = _Utils.boundingBox(polygon2.vertices);
|
|
293
|
+
const rect = _Utils.intersect(bbox1, bbox2);
|
|
294
|
+
if (operator !== "union" && (!Rect.isRect(rect) || !(rect.width > 0) || !(rect.height > 0))) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const { vertices: vertices1 } = polygon1, { vertices: vertices2 } = polygon2;
|
|
298
|
+
if (vertices1.length < 3 || vertices2.length < 3) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
const sideBuilderCallback = (v, j, arr) => [v, arr[(j + 1) % arr.length]];
|
|
302
|
+
const verticesSortCallback = (a, b) => a.x === b.x ? a.y - b.y : a.x - b.x;
|
|
303
|
+
const edges1 = vertices1.map(sideBuilderCallback), edges2 = vertices2.map(sideBuilderCallback);
|
|
304
|
+
const outputVertices = vertices1.concat(vertices2);
|
|
305
|
+
const memoizer = /* @__PURE__ */ new Map();
|
|
306
|
+
const ensureMemoizer = (e) => {
|
|
307
|
+
if (!memoizer.has(e)) {
|
|
308
|
+
const retval = [];
|
|
309
|
+
memoizer.set(e, retval);
|
|
310
|
+
}
|
|
311
|
+
return memoizer.get(e);
|
|
312
|
+
};
|
|
313
|
+
let thereAreIntersections = false;
|
|
314
|
+
for (let edge1 of edges1) {
|
|
315
|
+
const additionalVertices1 = ensureMemoizer(edge1);
|
|
316
|
+
for (let edge2 of edges2) {
|
|
317
|
+
const additionalVertices2 = ensureMemoizer(edge2);
|
|
318
|
+
const intersection = _Utils.intersect(edge1, edge2);
|
|
319
|
+
if (!NullChecker.isNull(intersection)) {
|
|
320
|
+
thereAreIntersections = true;
|
|
321
|
+
outputVertices.push(intersection);
|
|
322
|
+
additionalVertices1.push(intersection);
|
|
323
|
+
additionalVertices2.push(intersection);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (contained1.length === 0 && contained2.length === 0 && !thereAreIntersections) {
|
|
328
|
+
switch (operator) {
|
|
329
|
+
case "union":
|
|
330
|
+
return { polygons: [polygon1, polygon2] };
|
|
331
|
+
default:
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const constraints = [];
|
|
336
|
+
for (let edge of edges1.concat(edges2)) {
|
|
337
|
+
const additionalVertices = ensureMemoizer(edge);
|
|
338
|
+
if (NullChecker.isNullOrEmpty(additionalVertices)) {
|
|
339
|
+
constraints.push(edge);
|
|
340
|
+
} else {
|
|
341
|
+
const totalVertices = edge.concat(additionalVertices).sort(verticesSortCallback);
|
|
342
|
+
for (let j = 0; j < totalVertices.length - 1; j++) {
|
|
343
|
+
constraints.push([totalVertices[j], totalVertices[j + 1]]);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
var dirty = false;
|
|
348
|
+
var mesh;
|
|
349
|
+
do {
|
|
350
|
+
outputVertices.sort(verticesSortCallback);
|
|
351
|
+
outputVertices.filter((v, i, arr) => {
|
|
352
|
+
return i === 0 || !_Utils.areClose(v, arr[i - 1]);
|
|
353
|
+
});
|
|
354
|
+
mesh = _Utils.mesh(outputVertices);
|
|
355
|
+
const { vertices: vertices3, triangleIndices: triangleIndices2 } = mesh;
|
|
356
|
+
dirty = false;
|
|
357
|
+
for (let j = 0; j < triangleIndices2.length; j += 3) {
|
|
358
|
+
const indexA = triangleIndices2[j], indexB = triangleIndices2[j + 1], indexC = triangleIndices2[j + 2];
|
|
359
|
+
const A = vertices3[indexA], B = vertices3[indexB], C = vertices3[indexC];
|
|
360
|
+
const sides = [[A, B], [B, C], [C, A]];
|
|
361
|
+
for (let constraint of constraints) {
|
|
362
|
+
for (let side of sides) {
|
|
363
|
+
const consecutive = _Utils.areClose(side[0], constraint[0]) || _Utils.areClose(side[1], constraint[1]) || _Utils.areClose(side[0], constraint[1]) || _Utils.areClose(side[1], constraint[0]);
|
|
364
|
+
const intersection = consecutive ? null : _Utils.intersect(side, constraint);
|
|
365
|
+
if (!NullChecker.isNull(intersection) && outputVertices.findIndex((p) => _Utils.areClose(p, intersection, 4)) === -1) {
|
|
366
|
+
dirty = true;
|
|
367
|
+
outputVertices.push(intersection);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
} while (dirty);
|
|
373
|
+
const { vertices, triangleIndices } = mesh;
|
|
374
|
+
const triangles = [];
|
|
375
|
+
for (let j = 0; j < triangleIndices.length; j += 3) {
|
|
376
|
+
const jA = j, jB = j + 1, jC = j + 2;
|
|
377
|
+
const A = vertices[triangleIndices[jA]], B = vertices[triangleIndices[jB]], C = vertices[triangleIndices[jC]];
|
|
378
|
+
const O = { x: (A.x + B.x + C.x) / 3, y: (A.y + B.y + C.y) / 3 };
|
|
379
|
+
const in1 = _Utils.inPolygon(O, vertices1), in2 = _Utils.inPolygon(O, vertices2);
|
|
380
|
+
if (in1 && in2 && operator === "intersect" || (in1 || in2) && operator === "union" || (in1 && !in2 || !in1 && in2) && operator === "difference") {
|
|
381
|
+
triangles.push([A, B, C]);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const edges = triangles.flatMap(([A, B, C]) => [[A, B], [B, C], [C, A]]);
|
|
385
|
+
const uniqueEdges2 = [];
|
|
386
|
+
for (let j = 0; j < edges.length; j++) {
|
|
387
|
+
const edge = edges[j];
|
|
388
|
+
if (j == 0) {
|
|
389
|
+
uniqueEdges2.push(edge);
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
const alreadyIn = uniqueEdges2.findIndex((e) => _Utils.areClose(e[0], edge[0]) && _Utils.areClose(e[1], edge[1]) || _Utils.areClose(e[0], edge[1]) && _Utils.areClose(e[1], edge[0]));
|
|
393
|
+
if (alreadyIn === -1) {
|
|
394
|
+
uniqueEdges2.push(edge);
|
|
395
|
+
} else {
|
|
396
|
+
uniqueEdges2.splice(alreadyIn, 1);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (uniqueEdges2.length < 3) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
const polygons = [];
|
|
403
|
+
const cleanupPolygon = (vertices3) => {
|
|
404
|
+
const { length } = vertices3;
|
|
405
|
+
const repeated = _Utils.areClose(vertices3[0], vertices3[length - 1]);
|
|
406
|
+
const retval = vertices3.slice(0, repeated ? length - 1 : length);
|
|
407
|
+
if (retval.length > 3 && _Utils.inSegment(retval[0], [retval[1], retval[retval.length - 1]])) {
|
|
408
|
+
retval.shift();
|
|
409
|
+
}
|
|
410
|
+
return retval;
|
|
411
|
+
};
|
|
412
|
+
const [P0, P1] = uniqueEdges2.shift();
|
|
413
|
+
const boundaryVertices = [P0, P1];
|
|
414
|
+
while (uniqueEdges2.length > 1) {
|
|
415
|
+
const lastVertex = boundaryVertices[boundaryVertices.length - 1];
|
|
416
|
+
const nextEdgeIndex = uniqueEdges2.findIndex((e) => _Utils.areClose(e[0], lastVertex) || _Utils.areClose(e[1], lastVertex));
|
|
417
|
+
if (nextEdgeIndex === -1) {
|
|
418
|
+
polygons.push({ vertices: cleanupPolygon(boundaryVertices.splice(0)) });
|
|
419
|
+
const [P2, P3] = uniqueEdges2.shift();
|
|
420
|
+
boundaryVertices.push(P2, P3);
|
|
421
|
+
} else {
|
|
422
|
+
const [nextEdge] = uniqueEdges2.splice(nextEdgeIndex, 1);
|
|
423
|
+
const nextVertex = _Utils.areClose(nextEdge[0], lastVertex) ? nextEdge[1] : _Utils.areClose(nextEdge[1], lastVertex) ? nextEdge[0] : null;
|
|
424
|
+
if (boundaryVertices.length > 1 && _Utils.inSegment(boundaryVertices[boundaryVertices.length - 1], [boundaryVertices[boundaryVertices.length - 2], nextVertex])) {
|
|
425
|
+
boundaryVertices.splice(boundaryVertices.length - 1, 1, nextVertex);
|
|
426
|
+
} else {
|
|
427
|
+
boundaryVertices.push(nextVertex);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
polygons.push({ vertices: cleanupPolygon(boundaryVertices) });
|
|
432
|
+
return {
|
|
433
|
+
polygons,
|
|
434
|
+
mesh,
|
|
435
|
+
triangles: triangles.map((vertices3) => {
|
|
436
|
+
return { vertices: vertices3 };
|
|
437
|
+
})
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
static mesh(point, ...points) {
|
|
441
|
+
const vertices = [].concat(point).concat(points), triangleIndices = Delaunay.triangulate(vertices);
|
|
442
|
+
return { vertices, triangleIndices };
|
|
443
|
+
}
|
|
444
|
+
static intersectLines(segment1, segment2, m2, q2) {
|
|
445
|
+
let s1, s2;
|
|
446
|
+
if (Array.isArray(segment1) && Array.isArray(segment2) && segment1.length === 2 && segment2.length === 2 && typeof segment1[0] === "number" && typeof segment2[0] === "number" && typeof segment1[1] === "number" && typeof segment2[1] === "number") {
|
|
447
|
+
m2 = segment2[0];
|
|
448
|
+
q2 = segment2[1];
|
|
449
|
+
segment2 = segment1[1];
|
|
450
|
+
segment1 = segment1[0];
|
|
451
|
+
}
|
|
452
|
+
if (typeof segment1 === "number" && typeof segment2 === "number") {
|
|
453
|
+
const m1 = segment1, q1 = segment2;
|
|
454
|
+
s1 = [
|
|
455
|
+
{ x: 0, y: q1 },
|
|
456
|
+
{ x: 1, y: m1 + q1 }
|
|
457
|
+
];
|
|
458
|
+
s2 = [
|
|
459
|
+
{ x: 0, y: q2 },
|
|
460
|
+
{ x: 1, y: m2 + q2 }
|
|
461
|
+
];
|
|
462
|
+
} else {
|
|
463
|
+
s1 = segment1;
|
|
464
|
+
s2 = segment2;
|
|
465
|
+
}
|
|
466
|
+
return _Utils._intersectSegments(s1, s2, false);
|
|
467
|
+
}
|
|
468
|
+
static cramer(l1, l2) {
|
|
469
|
+
if (Array.isArray(l1)) {
|
|
470
|
+
l1 = { a: l1[0], b: l1[1], c: l1[2] };
|
|
471
|
+
}
|
|
472
|
+
if (Array.isArray(l2)) {
|
|
473
|
+
l2 = { a: l2[0], b: l2[1], c: l2[2] };
|
|
474
|
+
}
|
|
475
|
+
let xP = (l1.b * l2.c - l1.c * l2.b) / (l1.b * l2.a - l1.a * l2.b), yP = (l1.c * l2.a - l1.a * l2.c) / (l1.b * l2.a - l1.a * l2.b);
|
|
476
|
+
return { x: -xP.roundoff(), y: -yP.roundoff() };
|
|
477
|
+
}
|
|
478
|
+
static mq(arg0, p2) {
|
|
479
|
+
let p1;
|
|
480
|
+
if (isPoint(arg0)) {
|
|
481
|
+
p1 = arg0;
|
|
482
|
+
} else {
|
|
483
|
+
p1 = arg0[0];
|
|
484
|
+
p2 = arg0[1];
|
|
485
|
+
}
|
|
486
|
+
if (p1.x === p2.x) {
|
|
487
|
+
return [Number.NaN, Number.NaN];
|
|
488
|
+
}
|
|
489
|
+
const m = (p2.y - p1.y) / (p2.x - p1.x);
|
|
490
|
+
const q = p1.y - m * p1.x;
|
|
491
|
+
return [m, q];
|
|
492
|
+
}
|
|
493
|
+
static _intersectSegments(segment1, segment2, excludeProjection) {
|
|
494
|
+
const A = segment1[0], B = segment1[1], C = segment2[0], D = segment2[1], xAB = Math.min(A.x, B.x), yAB = Math.min(A.y, B.y), XAB = Math.max(A.x, B.x), wAB = XAB - xAB, YAB = Math.max(A.y, B.y), hAB = YAB - yAB, xCD = Math.min(C.x, D.x), yCD = Math.min(C.y, D.y), XCD = Math.max(C.x, D.x), wCD = XCD - xCD, YCD = Math.max(C.y, D.y), hCD = YCD - yCD;
|
|
495
|
+
if (wAB === 0 && wCD === 0) {
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
if (hAB === 0 && hCD === 0) {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
const outOfXBounds = (p) => {
|
|
502
|
+
return p.x < xCD || p.x > XCD || p.x < xAB || p.x > XAB;
|
|
503
|
+
};
|
|
504
|
+
const outOfYBounds = (p) => {
|
|
505
|
+
return p.y < yCD || p.y > YCD || p.y < yAB || p.y > YAB;
|
|
506
|
+
};
|
|
507
|
+
var retval = null;
|
|
508
|
+
if (wAB === 0) {
|
|
509
|
+
if (excludeProjection && (xCD > xAB || XCD < XAB)) {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
const mqCD = _Utils.mq(C, D);
|
|
513
|
+
retval = _Utils.cramer([1, 0, -xAB], [mqCD[0], -1, mqCD[1]]);
|
|
514
|
+
if (excludeProjection && outOfYBounds(retval)) {
|
|
515
|
+
retval = null;
|
|
516
|
+
}
|
|
517
|
+
} else if (wCD === 0) {
|
|
518
|
+
if (excludeProjection && (xAB > xCD || XAB < XCD)) {
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
const mqAB = _Utils.mq(A, B);
|
|
522
|
+
retval = _Utils.cramer([mqAB[0], -1, mqAB[1]], [1, 0, -xCD]);
|
|
523
|
+
if (excludeProjection && outOfYBounds(retval)) {
|
|
524
|
+
retval = null;
|
|
525
|
+
}
|
|
526
|
+
} else if (hAB === 0) {
|
|
527
|
+
if (excludeProjection && (yCD > yAB || YCD < YAB)) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
const mqCD = _Utils.mq(C, D);
|
|
531
|
+
retval = _Utils.cramer([0, 1, -yAB], [mqCD[0], -1, mqCD[1]]);
|
|
532
|
+
if (excludeProjection && outOfXBounds(retval)) {
|
|
533
|
+
retval = null;
|
|
534
|
+
}
|
|
535
|
+
} else if (hCD === 0) {
|
|
536
|
+
if (excludeProjection && (yAB > yCD || YAB < YCD)) {
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
const mqAB = _Utils.mq(A, B);
|
|
540
|
+
retval = _Utils.cramer([mqAB[0], -1, mqAB[1]], [0, 1, -yCD]);
|
|
541
|
+
if (excludeProjection && outOfXBounds(retval)) {
|
|
542
|
+
retval = null;
|
|
543
|
+
}
|
|
544
|
+
} else {
|
|
545
|
+
let intersection;
|
|
546
|
+
if (!excludeProjection || (intersection = _Utils._intersectRects({ x: xAB, y: yAB, width: wAB, height: hAB }, { x: xCD, y: yCD, width: wCD, height: hCD })) && intersection.width > 0 && intersection.height > 0) {
|
|
547
|
+
const mqAB = _Utils.mq(A, B), mqCD = _Utils.mq(C, D);
|
|
548
|
+
const mAB = mqAB[0], mCD = mqCD[0], qAB = mqAB[1], qCD = mqCD[1];
|
|
549
|
+
if (mAB !== mCD) {
|
|
550
|
+
retval = _Utils.cramer([mAB, -1, qAB], [mCD, -1, qCD]);
|
|
551
|
+
let xR = retval.x, yR = retval.y;
|
|
552
|
+
if (excludeProjection && (xR < intersection.x || xR > intersection.x + intersection.width || yR < intersection.y || yR > intersection.y + intersection.height)) {
|
|
553
|
+
retval = null;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (retval === null) {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
return { x: retval.x, y: retval.y };
|
|
562
|
+
}
|
|
563
|
+
static _intersectRects(...args) {
|
|
564
|
+
return Rect.intersect(...args);
|
|
565
|
+
}
|
|
566
|
+
static _areClose(p1, p2, precision = PRECISION) {
|
|
567
|
+
return Math.abs(p1.x - p2.x).isCloseTo(0, precision) && Math.abs(p1.y - p2.y).isCloseTo(0, precision);
|
|
568
|
+
}
|
|
569
|
+
static areClose(p1, p2, precision = PRECISION) {
|
|
570
|
+
return _Utils._areClose(p1, p2, precision);
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Returns the dot product between two vectors/points.
|
|
574
|
+
* @param v1
|
|
575
|
+
* @param v2
|
|
576
|
+
*/
|
|
577
|
+
static dot(v1, v2) {
|
|
578
|
+
return Vector.dot(v1, v2);
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Returns the cross product between two vectors/points (magnitude along the z axis).
|
|
582
|
+
* @param v1
|
|
583
|
+
* @param v2
|
|
584
|
+
*/
|
|
585
|
+
static cross(v1, v2) {
|
|
586
|
+
return Vector.cross(v1, v2);
|
|
587
|
+
}
|
|
588
|
+
static distance(p1, arg2) {
|
|
589
|
+
if (isSegment(p1)) {
|
|
590
|
+
return Point2.distance(p1[0], p1[1]);
|
|
591
|
+
} else if (isPoint(arg2)) {
|
|
592
|
+
return Point2.distance(p1, arg2);
|
|
593
|
+
} else {
|
|
594
|
+
const m = arg2[0], q = arg2[1];
|
|
595
|
+
return Math.abs(m * p1.x - p1.y + q) / Math.sqrt(Math.pow(m, 2) + 1);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
static _pointSegmentDistance(p, segment) {
|
|
599
|
+
const v1 = Vector.from(segment[0], segment[1]), v2 = Vector.from(segment[0], p);
|
|
600
|
+
return _Utils.cross(v1, v2);
|
|
601
|
+
}
|
|
602
|
+
static isWithin(p, segment, clockwise, precision = PRECISION) {
|
|
603
|
+
const cross = _Utils._pointSegmentDistance(p, segment);
|
|
604
|
+
if (cross.isCloseTo(0, precision)) {
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
return clockwise ? cross < 0 : cross > 0;
|
|
608
|
+
}
|
|
609
|
+
static isBeyond(p, segment, clockwise, precision = PRECISION) {
|
|
610
|
+
return _Utils.isWithin(p, segment, !clockwise, precision);
|
|
611
|
+
}
|
|
612
|
+
static inLine(p, segment, precision = PRECISION) {
|
|
613
|
+
const cross = _Utils._pointSegmentDistance(p, segment);
|
|
614
|
+
return cross.isCloseTo(0, precision);
|
|
615
|
+
}
|
|
616
|
+
static inSegment(p, segment, precision = PRECISION) {
|
|
617
|
+
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);
|
|
618
|
+
return p.x >= minx && p.x <= maxx && p.y >= miny && p.y <= maxy && _Utils.inLine(p, segment, precision);
|
|
619
|
+
}
|
|
620
|
+
static inTriangle(p, triangle) {
|
|
621
|
+
let last;
|
|
622
|
+
for (let j = 0; j < 3; j++) {
|
|
623
|
+
const p1 = triangle[j], p2 = triangle[(j + 1) % 3], v1 = Point2.subtract(p2, p), v2 = Point2.subtract(p1, p), current = _Utils.cross(v1, v2);
|
|
624
|
+
if (j > 0 && current * last <= 0) {
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
last = current;
|
|
628
|
+
}
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
static inPolygon(p, vertices, precision = PRECISION) {
|
|
632
|
+
if (!(vertices?.length >= 3)) {
|
|
633
|
+
throw `Not enough vertices`;
|
|
634
|
+
}
|
|
635
|
+
const length = vertices.length;
|
|
636
|
+
if (length === 3) {
|
|
637
|
+
return _Utils.inTriangle(p, [vertices[0], vertices[1], vertices[2]]);
|
|
638
|
+
}
|
|
639
|
+
if (precision > 0) {
|
|
640
|
+
vertices = vertices.map((v) => {
|
|
641
|
+
return { x: v.x.roundoff(precision), y: v.y.roundoff(precision) };
|
|
642
|
+
});
|
|
643
|
+
p = { x: p.x.roundoff(precision), y: p.y.roundoff(precision) };
|
|
644
|
+
}
|
|
645
|
+
let minx = Number.MAX_VALUE, miny = Number.MAX_VALUE, maxx = Number.MIN_VALUE, maxy = Number.MIN_VALUE;
|
|
646
|
+
for (let j = 0; j < length; j++) {
|
|
647
|
+
const v = vertices[j];
|
|
648
|
+
minx = Math.min(minx, v.x);
|
|
649
|
+
miny = Math.min(miny, v.y);
|
|
650
|
+
maxx = Math.max(maxx, v.x);
|
|
651
|
+
maxy = Math.max(maxy, v.y);
|
|
652
|
+
}
|
|
653
|
+
if (p.x < minx || p.x > maxx || p.y < miny || p.y > maxy) {
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
const outerPoint = { x: minx - 1, y: p.y }, test = [outerPoint, p];
|
|
657
|
+
let intersections = 0;
|
|
658
|
+
for (let j = 0; j < length; j++) {
|
|
659
|
+
const p1 = vertices[j], p2 = vertices[(j + 1) % length], side = [p1, p2];
|
|
660
|
+
if (p1.y === p.y) {
|
|
661
|
+
const p0 = vertices[(j - 1 + length) % length];
|
|
662
|
+
if (p.x > p1.x && (p0.y - p.y) * (p2.y - p.y) < 0) {
|
|
663
|
+
intersections++;
|
|
664
|
+
}
|
|
665
|
+
} else if (p2.y === p.y) {
|
|
666
|
+
continue;
|
|
667
|
+
} else {
|
|
668
|
+
if (_Utils.intersect(side, test) != null) {
|
|
669
|
+
intersections++;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return intersections % 2 === 1;
|
|
674
|
+
}
|
|
675
|
+
static area(point, ...points) {
|
|
676
|
+
const pts = [].concat(point).concat(points);
|
|
677
|
+
let retval = 0;
|
|
678
|
+
const length = pts.length;
|
|
679
|
+
if (length >= 3) {
|
|
680
|
+
for (let j = 0; j < pts.length; j++) {
|
|
681
|
+
const { x: xj, y: yj } = pts[j], next = j === length - 1 ? 0 : j + 1, { x: xnext, y: ynext } = pts[next];
|
|
682
|
+
retval += xj * ynext - yj * xnext;
|
|
683
|
+
}
|
|
684
|
+
retval *= 0.5;
|
|
685
|
+
}
|
|
686
|
+
return Math.abs(retval.roundoff());
|
|
687
|
+
}
|
|
688
|
+
static convexHull(point, ...points) {
|
|
689
|
+
const pts = [].concat(point).concat(points);
|
|
690
|
+
if (!(pts?.length > 1)) {
|
|
691
|
+
return pts?.slice() ?? [];
|
|
692
|
+
}
|
|
693
|
+
const sorted = pts.map((p) => ({ x: p.x, y: p.y })).sort((a, b) => a.x === b.x ? a.y - b.y : a.x - b.x);
|
|
694
|
+
const unique = [];
|
|
695
|
+
for (const p of sorted) {
|
|
696
|
+
const last = unique[unique.length - 1];
|
|
697
|
+
if (!last || last.x !== p.x || last.y !== p.y) {
|
|
698
|
+
unique.push(p);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
if (unique.length <= 2) {
|
|
702
|
+
return unique;
|
|
703
|
+
}
|
|
704
|
+
const cross = (o, a, b) => {
|
|
705
|
+
return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
706
|
+
};
|
|
707
|
+
const lower = [];
|
|
708
|
+
for (const p of unique) {
|
|
709
|
+
while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) {
|
|
710
|
+
lower.pop();
|
|
711
|
+
}
|
|
712
|
+
lower.push(p);
|
|
713
|
+
}
|
|
714
|
+
const upper = [];
|
|
715
|
+
for (let j = unique.length - 1; j >= 0; j--) {
|
|
716
|
+
const p = unique[j];
|
|
717
|
+
while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) {
|
|
718
|
+
upper.pop();
|
|
719
|
+
}
|
|
720
|
+
upper.push(p);
|
|
721
|
+
}
|
|
722
|
+
lower.pop();
|
|
723
|
+
upper.pop();
|
|
724
|
+
return lower.concat(upper);
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
// packages/numerical/dist/esm/geom/polygon.js
|
|
729
|
+
import { Point as Point3, Matrix2D, Angle, NullChecker as NullChecker2 } from "@pacem/pacem-foundation";
|
|
730
|
+
function isPolygon(obj) {
|
|
731
|
+
let vertices;
|
|
732
|
+
return "vertices" in obj && Array.isArray(obj.vertices) && (vertices = obj.vertices).length >= 3 && vertices.every((i) => Point3.isPoint(i));
|
|
733
|
+
}
|
|
734
|
+
var Polygon = class _Polygon {
|
|
735
|
+
static {
|
|
736
|
+
this._eps = 1e-12;
|
|
737
|
+
}
|
|
738
|
+
static isPolygon(obj) {
|
|
739
|
+
return isPolygon(obj);
|
|
740
|
+
}
|
|
741
|
+
static from(...points) {
|
|
742
|
+
return { vertices: Array.from(points) };
|
|
743
|
+
}
|
|
744
|
+
static contains(polygon, p) {
|
|
745
|
+
return Utils.inPolygon(p, polygon.vertices, 12);
|
|
746
|
+
}
|
|
747
|
+
static centroid(polygon) {
|
|
748
|
+
const bbox = _Polygon.boundingBox(polygon);
|
|
749
|
+
return { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 };
|
|
750
|
+
}
|
|
751
|
+
static boundingBox(polygon) {
|
|
752
|
+
return Utils.boundingBox(polygon.vertices);
|
|
753
|
+
}
|
|
754
|
+
static sides(polygon) {
|
|
755
|
+
const { vertices } = polygon;
|
|
756
|
+
const retval = [];
|
|
757
|
+
for (let j = 1; j <= vertices.length; j++) {
|
|
758
|
+
const v = vertices[j % vertices.length], v0 = vertices[j - 1];
|
|
759
|
+
retval.push([v0, v]);
|
|
760
|
+
}
|
|
761
|
+
return retval;
|
|
762
|
+
}
|
|
763
|
+
static isConvex(polygon) {
|
|
764
|
+
const { vertices } = polygon, l = vertices.length;
|
|
765
|
+
if (l <= 3) {
|
|
766
|
+
return true;
|
|
767
|
+
}
|
|
768
|
+
let sign = 0;
|
|
769
|
+
for (let j = 0; j < vertices.length; j++) {
|
|
770
|
+
const p0 = vertices[(j - 1 + l) % l], p = vertices[j], p1 = vertices[(j + 1) % l];
|
|
771
|
+
const theta = Angle.angleBetween(p, p0, p1);
|
|
772
|
+
if (sign === 0) {
|
|
773
|
+
sign = Math.sign(theta);
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
if (sign != Math.sign(theta)) {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return true;
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Check whether a polygon is a self-intersecting one.
|
|
784
|
+
* @param polygon
|
|
785
|
+
* @returns
|
|
786
|
+
*/
|
|
787
|
+
static isSelfIntersecting(polygon) {
|
|
788
|
+
const { vertices } = polygon;
|
|
789
|
+
if (vertices.length <= 3) {
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
const sides = _Polygon.sides(polygon);
|
|
793
|
+
for (let side of sides) {
|
|
794
|
+
for (let check of sides) {
|
|
795
|
+
if (
|
|
796
|
+
/* same */
|
|
797
|
+
check === side || /* contiguous */
|
|
798
|
+
check[0] === side[1] || side[0] === check[1]
|
|
799
|
+
) {
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
const intersection = Utils.intersect(check, side);
|
|
803
|
+
if (!NullChecker2.isNull(intersection)) {
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Returns whether the vertices of the spcified polygon have been provided in counter-clockwise order.
|
|
812
|
+
* Meaningless in case of self-intersecting polygons.
|
|
813
|
+
* @param polygon
|
|
814
|
+
* @returns
|
|
815
|
+
*/
|
|
816
|
+
static isCounterClockwise(polygon) {
|
|
817
|
+
const { vertices: [p0, p1, p2] } = polygon;
|
|
818
|
+
return Angle.angleBetween(p1, p0, p2) > 0;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Expands (or contracts) a polygon by a provided offset.
|
|
822
|
+
* @param polygon
|
|
823
|
+
* @param offset
|
|
824
|
+
*
|
|
825
|
+
*/
|
|
826
|
+
static expand(polygon, offset) {
|
|
827
|
+
const { vertices } = polygon, l = vertices.length, retval = [];
|
|
828
|
+
for (let j = 0; j < vertices.length; j++) {
|
|
829
|
+
const p0 = vertices[(j - 1 + l) % l], p = vertices[j], p1 = vertices[(j + 1) % l];
|
|
830
|
+
const theta = Angle.angleBetween(p, p0, p1), scale = -offset / Math.sin(Math.abs(theta) / 2);
|
|
831
|
+
const matrix = Matrix2D.scale(Matrix2D.identity, scale);
|
|
832
|
+
const v0 = Point3.subtract(p, p0);
|
|
833
|
+
Vector.normalize(v0);
|
|
834
|
+
const v1 = Point3.subtract(p, p1);
|
|
835
|
+
Vector.normalize(v1);
|
|
836
|
+
const vector = Point3.add(v0, v1);
|
|
837
|
+
Vector.normalize(vector);
|
|
838
|
+
const pOffset = Matrix2D.multiply(vector, matrix);
|
|
839
|
+
retval.push(Point3.add(p, { x: pOffset.x.roundoff(), y: pOffset.y.roundoff() }));
|
|
840
|
+
}
|
|
841
|
+
return { vertices: retval };
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Returns the area of a polygon using the shoelace algorithm. It's meaningless in case of self-intersecting polygons.
|
|
845
|
+
* @param polygon
|
|
846
|
+
* @returns
|
|
847
|
+
*/
|
|
848
|
+
static area(polygon) {
|
|
849
|
+
return Utils.area(polygon.vertices);
|
|
850
|
+
}
|
|
851
|
+
static intersect(polygon1, polygon2) {
|
|
852
|
+
return Utils.intersect(polygon1, polygon2);
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Returns the convex hull of a polygon.
|
|
856
|
+
* @param polygon
|
|
857
|
+
* @returns
|
|
858
|
+
*/
|
|
859
|
+
static convexHull(polygon) {
|
|
860
|
+
const vertices = Utils.convexHull(polygon.vertices);
|
|
861
|
+
return { vertices };
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
// packages/numerical/dist/esm/geom/geom3d.js
|
|
866
|
+
import { parseAsNumericalArray } from "@pacem/pacem-foundation";
|
|
867
|
+
var RAD2DEG2 = 180 / Math.PI;
|
|
868
|
+
var DEG2RAD = 1 / RAD2DEG2;
|
|
869
|
+
var Vector3D = class _Vector3D {
|
|
870
|
+
static from(...args) {
|
|
871
|
+
const l = 3;
|
|
872
|
+
if (args.length !== l) {
|
|
873
|
+
throw new RangeError(`Must provide exactly ${l} numbers`);
|
|
874
|
+
}
|
|
875
|
+
return { x: args[0], y: args[1], z: args[2] };
|
|
876
|
+
}
|
|
877
|
+
static parse(input) {
|
|
878
|
+
const arr = parseAsNumericalArray(input);
|
|
879
|
+
if (arr && arr.length === 3) {
|
|
880
|
+
return _Vector3D.from.apply(null, arr);
|
|
881
|
+
}
|
|
882
|
+
throw new Error(`Cannot parse "${input}" as a valid Vector3D.`);
|
|
883
|
+
}
|
|
884
|
+
// notable vectors
|
|
885
|
+
static i() {
|
|
886
|
+
return { x: 1, y: 0, z: 0 };
|
|
887
|
+
}
|
|
888
|
+
static j() {
|
|
889
|
+
return { x: 0, y: 1, z: 0 };
|
|
890
|
+
}
|
|
891
|
+
static k() {
|
|
892
|
+
return { x: 0, y: 0, z: 1 };
|
|
893
|
+
}
|
|
894
|
+
static zero() {
|
|
895
|
+
return { x: 0, y: 0, z: 0 };
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Subtracts a point p from another and returns the resulting vector.
|
|
899
|
+
* @param p Point to subtract
|
|
900
|
+
* @param from Point to be subtracted from
|
|
901
|
+
*/
|
|
902
|
+
static subtract(p, from) {
|
|
903
|
+
return { x: from.x - p.x, y: from.y - p.y, z: from.z - p.z };
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Adds a set of points together and returns the resulting vector.
|
|
907
|
+
* @param points Points to add
|
|
908
|
+
*/
|
|
909
|
+
static add(...points) {
|
|
910
|
+
var point = { x: 0, y: 0, z: 0 };
|
|
911
|
+
for (var p of points) {
|
|
912
|
+
point.x += p.x;
|
|
913
|
+
point.y += p.y;
|
|
914
|
+
point.z += p.z;
|
|
915
|
+
}
|
|
916
|
+
return point;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Returns the dot product between two vectors/points.
|
|
920
|
+
* @param v1
|
|
921
|
+
* @param v2
|
|
922
|
+
*/
|
|
923
|
+
static dot(v1, v2) {
|
|
924
|
+
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* Returns the cross product between two vectors/points (as a vector itself).
|
|
928
|
+
* @param v1
|
|
929
|
+
* @param v2
|
|
930
|
+
*/
|
|
931
|
+
static cross(v1, v2) {
|
|
932
|
+
return {
|
|
933
|
+
x: v1.y * v2.z - v1.z * v2.y,
|
|
934
|
+
y: v1.z * v2.x - v1.x * v2.z,
|
|
935
|
+
z: v1.x * v2.y - v1.y * v2.x
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
static scale(v, fx, fy = fx, fz = fx) {
|
|
939
|
+
return { x: v.x * fx, y: v.y * fy, z: v.z * fz };
|
|
940
|
+
}
|
|
941
|
+
static magSqr(v) {
|
|
942
|
+
return v.x * v.x + v.y * v.y + v.z * v.z;
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Checks if the two points are effectively the same location.
|
|
946
|
+
* @param v1 The first point.
|
|
947
|
+
* @param v2 The second point.
|
|
948
|
+
* @returns True if the points are considered coincident within tolerance; otherwise, false.
|
|
949
|
+
*/
|
|
950
|
+
static areClose(v1, v2) {
|
|
951
|
+
return (v2.x - v1.x).isCloseTo(0) && (v2.y - v1.y).isCloseTo(0) && (v2.z - v1.z).isCloseTo(0);
|
|
952
|
+
}
|
|
953
|
+
static mag(v) {
|
|
954
|
+
return Math.sqrt(_Vector3D.magSqr(v));
|
|
955
|
+
}
|
|
956
|
+
static negate(v) {
|
|
957
|
+
return { x: -v.x, y: -v.y, z: -v.z };
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Returns the unit (normalized) vector having the same direction and sense of the provided one.
|
|
961
|
+
* @param v
|
|
962
|
+
*/
|
|
963
|
+
static unit(v) {
|
|
964
|
+
const clone = { x: v.x, y: v.y, z: v.z };
|
|
965
|
+
this.normalize(clone);
|
|
966
|
+
return clone;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Normalizes the provided vector in place.
|
|
970
|
+
* @param v
|
|
971
|
+
*/
|
|
972
|
+
static normalize(v) {
|
|
973
|
+
const l = _Vector3D.mag(v);
|
|
974
|
+
if (l <= 0) {
|
|
975
|
+
throw "Cannot normalize a vector of length 0.";
|
|
976
|
+
}
|
|
977
|
+
const inv = 1 / l;
|
|
978
|
+
v.x *= inv;
|
|
979
|
+
v.y *= inv;
|
|
980
|
+
v.z *= inv;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Returns the angle (in degrees) between two vectors.
|
|
984
|
+
* @param vector1
|
|
985
|
+
* @param vector2
|
|
986
|
+
*/
|
|
987
|
+
static angleBetween(vector1, vector2) {
|
|
988
|
+
let num;
|
|
989
|
+
const v1 = _Vector3D.unit(vector1);
|
|
990
|
+
const v2 = _Vector3D.unit(vector2);
|
|
991
|
+
const dot = _Vector3D.dot(v1, v2);
|
|
992
|
+
if (dot < 0) {
|
|
993
|
+
const vectord = { x: -v1.x - v2.x, y: -v1.y - v2.y, z: -v1.z - v2.z };
|
|
994
|
+
const length = _Vector3D.mag(vectord);
|
|
995
|
+
num = Math.PI - 2 * Math.asin(length / 2);
|
|
996
|
+
} else {
|
|
997
|
+
const vectord = { x: v1.x - v2.x, y: v1.y - v2.y, z: v1.z - v2.z };
|
|
998
|
+
const length = _Vector3D.mag(vectord);
|
|
999
|
+
num = 2 * Math.asin(length / 2);
|
|
1000
|
+
}
|
|
1001
|
+
return RAD2DEG2 * num;
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
var Spherical = class _Spherical {
|
|
1005
|
+
/**
|
|
1006
|
+
* Creates a new {@link Spherical} object.
|
|
1007
|
+
* @param rho radius.
|
|
1008
|
+
* @param theta Azimuth angle in degrees.
|
|
1009
|
+
* @param phi Polar angle in degrees.
|
|
1010
|
+
* @returns
|
|
1011
|
+
*/
|
|
1012
|
+
static from(rho, theta, phi) {
|
|
1013
|
+
return { rho: rho.clamp(0, Infinity), theta: theta.clamp(-180, 180), phi: phi.clamp(0, 360) };
|
|
1014
|
+
}
|
|
1015
|
+
static fromVector(v, y, z) {
|
|
1016
|
+
if (typeof v === "number") {
|
|
1017
|
+
v = Vector3D.from(v, y, z);
|
|
1018
|
+
}
|
|
1019
|
+
const rho = Vector3D.mag(v);
|
|
1020
|
+
if (rho.isCloseTo(0)) {
|
|
1021
|
+
return _Spherical.from(0, 0, 0);
|
|
1022
|
+
}
|
|
1023
|
+
const theta = Math.atan2(v.x, v.z), phi = Math.acos((v.y / rho).clamp(-1, 1));
|
|
1024
|
+
return _Spherical.from(rho, theta * RAD2DEG2, phi * RAD2DEG2);
|
|
1025
|
+
}
|
|
1026
|
+
static toVector(coords) {
|
|
1027
|
+
const { rho, theta: thetaDeg, phi: phiDeg } = coords;
|
|
1028
|
+
const phi = phiDeg * DEG2RAD;
|
|
1029
|
+
const theta = thetaDeg * DEG2RAD;
|
|
1030
|
+
const sinPhiRho = Math.sin(phi) * rho;
|
|
1031
|
+
return {
|
|
1032
|
+
x: sinPhiRho * Math.sin(theta),
|
|
1033
|
+
y: Math.cos(phi) * rho,
|
|
1034
|
+
z: sinPhiRho * Math.cos(theta)
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
static toRotationMatrix(coords) {
|
|
1038
|
+
const { theta, phi } = coords;
|
|
1039
|
+
const t = DEG2RAD * theta, sinTheta = Math.sin(t), cosTheta = Math.cos(t), p = DEG2RAD * phi, sinPhi = Math.sin(p), cosPhi = Math.cos(p);
|
|
1040
|
+
return Matrix3D.from(sinTheta * cosPhi, sinTheta * sinPhi, cosTheta, 0, cosTheta * cosPhi, cosTheta * sinPhi, -sinTheta, 0, -sinPhi, cosPhi, 0, 0, 0, 0, 0, 1);
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
var Matrix3DUtils = class _Matrix3DUtils {
|
|
1044
|
+
/**
|
|
1045
|
+
* Modifies the content of a provided matrix and returns it as a new matrix instance.
|
|
1046
|
+
* @param m Input matrix
|
|
1047
|
+
* @param action Content change callback
|
|
1048
|
+
* @returns New matrix instance.
|
|
1049
|
+
*/
|
|
1050
|
+
static modify(m, action) {
|
|
1051
|
+
const { m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, offsetX, offsetY, offsetZ, m44 } = m;
|
|
1052
|
+
const retval = {
|
|
1053
|
+
m11,
|
|
1054
|
+
m12,
|
|
1055
|
+
m13,
|
|
1056
|
+
m14,
|
|
1057
|
+
m21,
|
|
1058
|
+
m22,
|
|
1059
|
+
m23,
|
|
1060
|
+
m24,
|
|
1061
|
+
m31,
|
|
1062
|
+
m32,
|
|
1063
|
+
m33,
|
|
1064
|
+
m34,
|
|
1065
|
+
offsetX,
|
|
1066
|
+
offsetY,
|
|
1067
|
+
offsetZ,
|
|
1068
|
+
m44
|
|
1069
|
+
};
|
|
1070
|
+
action(retval);
|
|
1071
|
+
return retval;
|
|
1072
|
+
}
|
|
1073
|
+
static clone(m) {
|
|
1074
|
+
return _Matrix3DUtils.modify(m, (_) => {
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
static multiply(m1, m2) {
|
|
1078
|
+
if (Matrix3D.isIdentity(m1))
|
|
1079
|
+
return m2;
|
|
1080
|
+
if (Matrix3D.isIdentity(m2))
|
|
1081
|
+
return m1;
|
|
1082
|
+
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);
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
var Matrix3D = class _Matrix3D {
|
|
1086
|
+
//#region notable matrices
|
|
1087
|
+
/**
|
|
1088
|
+
* Identity 4x4 matrix.
|
|
1089
|
+
* @returns
|
|
1090
|
+
*/
|
|
1091
|
+
static identity() {
|
|
1092
|
+
return {
|
|
1093
|
+
m11: 1,
|
|
1094
|
+
m12: 0,
|
|
1095
|
+
m13: 0,
|
|
1096
|
+
m14: 0,
|
|
1097
|
+
m21: 0,
|
|
1098
|
+
m22: 1,
|
|
1099
|
+
m23: 0,
|
|
1100
|
+
m24: 0,
|
|
1101
|
+
m31: 0,
|
|
1102
|
+
m32: 0,
|
|
1103
|
+
m33: 1,
|
|
1104
|
+
m34: 0,
|
|
1105
|
+
offsetX: 0,
|
|
1106
|
+
offsetY: 0,
|
|
1107
|
+
offsetZ: 0,
|
|
1108
|
+
m44: 1
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
//#endregion
|
|
1112
|
+
static from(...args) {
|
|
1113
|
+
const l = 16;
|
|
1114
|
+
if (args.length !== l) {
|
|
1115
|
+
throw new RangeError(`Must provide exactly ${l} numbers`);
|
|
1116
|
+
}
|
|
1117
|
+
return {
|
|
1118
|
+
m11: args[0],
|
|
1119
|
+
m12: args[1],
|
|
1120
|
+
m13: args[2],
|
|
1121
|
+
m14: args[3],
|
|
1122
|
+
m21: args[4],
|
|
1123
|
+
m22: args[5],
|
|
1124
|
+
m23: args[6],
|
|
1125
|
+
m24: args[7],
|
|
1126
|
+
m31: args[8],
|
|
1127
|
+
m32: args[9],
|
|
1128
|
+
m33: args[10],
|
|
1129
|
+
m34: args[11],
|
|
1130
|
+
offsetX: args[12],
|
|
1131
|
+
offsetY: args[13],
|
|
1132
|
+
offsetZ: args[14],
|
|
1133
|
+
m44: args[15]
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
static transpose(m) {
|
|
1137
|
+
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);
|
|
1138
|
+
}
|
|
1139
|
+
static toArray(m) {
|
|
1140
|
+
return [
|
|
1141
|
+
m.m11,
|
|
1142
|
+
m.m12,
|
|
1143
|
+
m.m13,
|
|
1144
|
+
m.m14,
|
|
1145
|
+
m.m21,
|
|
1146
|
+
m.m22,
|
|
1147
|
+
m.m23,
|
|
1148
|
+
m.m24,
|
|
1149
|
+
m.m31,
|
|
1150
|
+
m.m32,
|
|
1151
|
+
m.m33,
|
|
1152
|
+
m.m34,
|
|
1153
|
+
m.offsetX,
|
|
1154
|
+
m.offsetY,
|
|
1155
|
+
m.offsetZ,
|
|
1156
|
+
m.m44
|
|
1157
|
+
];
|
|
1158
|
+
}
|
|
1159
|
+
static clone(m, modifier) {
|
|
1160
|
+
return typeof modifier === "function" ? Matrix3DUtils.modify(m, modifier) : Matrix3DUtils.clone(m);
|
|
1161
|
+
}
|
|
1162
|
+
static scale(m, x, y, z) {
|
|
1163
|
+
if (typeof x === "number") {
|
|
1164
|
+
y ??= x;
|
|
1165
|
+
z ??= x;
|
|
1166
|
+
} else {
|
|
1167
|
+
z = x.z;
|
|
1168
|
+
y = x.y;
|
|
1169
|
+
x = x.x;
|
|
1170
|
+
}
|
|
1171
|
+
return Matrix3DUtils.modify(m, (s) => {
|
|
1172
|
+
s.m11 *= x;
|
|
1173
|
+
s.m22 *= y;
|
|
1174
|
+
s.m33 *= z;
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
static translate(m, offset) {
|
|
1178
|
+
return Matrix3DUtils.modify(m, (s) => {
|
|
1179
|
+
s.offsetX += offset.x;
|
|
1180
|
+
s.offsetY += offset.y;
|
|
1181
|
+
s.offsetZ += offset.z;
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
static parse(input) {
|
|
1185
|
+
const arr = parseAsNumericalArray(input);
|
|
1186
|
+
if (arr && arr.length === 16) {
|
|
1187
|
+
return _Matrix3D.from.apply(null, arr);
|
|
1188
|
+
}
|
|
1189
|
+
throw new Error(`Cannot parse "${input}" as a valid Matrix3D.`);
|
|
1190
|
+
}
|
|
1191
|
+
static isIdentity(m) {
|
|
1192
|
+
return m.m11 == 1 && m.m12 == 0 && m.m13 == 0 && m.m14 == 0 && m.m21 == 0 && m.m22 == 1 && m.m23 == 0 && m.m24 == 0 && m.m31 == 0 && m.m32 == 0 && m.m33 == 1 && m.m34 == 0 && m.offsetX == 0 && m.offsetY == 0 && m.offsetZ == 0 && m.m44 == 1;
|
|
1193
|
+
}
|
|
1194
|
+
static isAffine(m) {
|
|
1195
|
+
return m.m14 == 0 && m.m24 == 0 && m.m34 == 0 && m.m44 == 1;
|
|
1196
|
+
}
|
|
1197
|
+
static determinant(m) {
|
|
1198
|
+
if (_Matrix3D.isIdentity(m)) {
|
|
1199
|
+
return 1;
|
|
1200
|
+
} else if (_Matrix3D.isAffine(m)) {
|
|
1201
|
+
return m.m11 * (m.m22 * m.m33 - m.m32 * m.m23) - m.m12 * (m.m21 * m.m33 - m.m31 * m.m23) + m.m13 * (m.m21 * m.m32 - m.m31 * m.m22);
|
|
1202
|
+
} else {
|
|
1203
|
+
const num6 = m.m13 * m.m24 - m.m23 * m.m14;
|
|
1204
|
+
const num5 = m.m13 * m.m34 - m.m33 * m.m14;
|
|
1205
|
+
const num4 = m.m13 * m.m44 - m.offsetZ * m.m14;
|
|
1206
|
+
const num3 = m.m23 * m.m34 - m.m33 * m.m24;
|
|
1207
|
+
const num2 = m.m23 * m.m44 - m.offsetZ * m.m24;
|
|
1208
|
+
const num = m.m33 * m.m44 - m.offsetZ * m.m34;
|
|
1209
|
+
const num10 = m.m22 * num5 - m.m32 * num6 - m.m12 * num3;
|
|
1210
|
+
const num9 = m.m12 * num2 - m.m22 * num4 + m.offsetY * num6;
|
|
1211
|
+
const num8 = m.m32 * num4 - m.offsetY * num5 - m.m12 * num;
|
|
1212
|
+
const num7 = m.m22 * num - m.m32 * num2 + m.offsetY * num3;
|
|
1213
|
+
const det = m.offsetX * num10 + m.m31 * num9 + m.m21 * num8 + m.m11 * num7;
|
|
1214
|
+
return det;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Multiplies two matrices in the provided order (i.e.: applies {@link m2} to {@link m1}).
|
|
1219
|
+
* @param matrices Matrices to apply one to the other, sequentially.
|
|
1220
|
+
* @returns Combination matrix.
|
|
1221
|
+
*/
|
|
1222
|
+
static multiply(...matrices) {
|
|
1223
|
+
if (matrices.length === 0) {
|
|
1224
|
+
throw new ReferenceError("Matrix not provided.");
|
|
1225
|
+
}
|
|
1226
|
+
const arr = Array.from(matrices);
|
|
1227
|
+
let m = arr[0];
|
|
1228
|
+
for (let i = 1; i < arr.length; i++) {
|
|
1229
|
+
m = Matrix3DUtils.multiply(m, arr[i]);
|
|
1230
|
+
}
|
|
1231
|
+
return m;
|
|
1232
|
+
}
|
|
1233
|
+
static invert(m) {
|
|
1234
|
+
if (_Matrix3D.isAffine(m)) {
|
|
1235
|
+
const determinant = _Matrix3D.determinant(m);
|
|
1236
|
+
if (determinant == 0) {
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
const cofactor31 = m.m12 * m.m23 - m.m22 * m.m13;
|
|
1240
|
+
const cofactor21 = m.m32 * m.m13 - m.m12 * m.m33;
|
|
1241
|
+
const cofactor11 = m.m22 * m.m33 - m.m32 * m.m23;
|
|
1242
|
+
const num20 = m.m21 * m.m13 - m.m11 * m.m23;
|
|
1243
|
+
const num19 = m.m11 * m.m33 - m.m31 * m.m13;
|
|
1244
|
+
const num18 = m.m31 * m.m23 - m.m21 * m.m33;
|
|
1245
|
+
const num7 = m.m11 * m.m22 - m.m21 * m.m12;
|
|
1246
|
+
const num6 = m.m11 * m.m32 - m.m31 * m.m12;
|
|
1247
|
+
const num5 = m.m11 * m.offsetY - m.offsetX * m.m12;
|
|
1248
|
+
const num4 = m.m21 * m.m32 - m.m31 * m.m22;
|
|
1249
|
+
const num3 = m.m21 * m.offsetY - m.offsetX * m.m22;
|
|
1250
|
+
const num2 = m.m31 * m.offsetY - m.offsetX * m.m32;
|
|
1251
|
+
const num17 = m.m23 * num5 - m.offsetZ * num7 - m.m13 * num3;
|
|
1252
|
+
const num16 = m.m13 * num2 - m.m33 * num5 + m.offsetZ * num6;
|
|
1253
|
+
const num15 = m.m33 * num3 - m.offsetZ * num4 - m.m23 * num2;
|
|
1254
|
+
const num14 = num7;
|
|
1255
|
+
const num13 = -num6;
|
|
1256
|
+
const num12 = num4;
|
|
1257
|
+
const invdet = 1 / determinant;
|
|
1258
|
+
return _Matrix3D.from(cofactor11 * invdet, cofactor21 * invdet, cofactor31 * invdet, 0, num18 * invdet, num19 * invdet, num20 * invdet, 0, num12 * invdet, num13 * invdet, num14 * invdet, 0, num15 * invdet, num16 * invdet, num17 * invdet, 1);
|
|
1259
|
+
} else {
|
|
1260
|
+
const determinant = _Matrix3D.determinant(m);
|
|
1261
|
+
if (determinant == 0) {
|
|
1262
|
+
return null;
|
|
1263
|
+
}
|
|
1264
|
+
const num1 = m.m33 * m.m44 - m.m34 * m.offsetZ;
|
|
1265
|
+
const num2 = m.m32 * m.m44 - m.m34 * m.offsetY;
|
|
1266
|
+
const num3 = m.m31 * m.m44 - m.m34 * m.offsetX;
|
|
1267
|
+
const num4 = m.m32 * m.offsetZ - m.m33 * m.offsetY;
|
|
1268
|
+
const num5 = m.m31 * m.offsetZ - m.m33 * m.offsetX;
|
|
1269
|
+
const num6 = m.m31 * m.offsetY - m.m32 * m.offsetX;
|
|
1270
|
+
const num7 = m.m33 * m.m44 - m.m34 * m.offsetZ;
|
|
1271
|
+
const num8 = m.m32 * m.m44 - m.m34 * m.offsetY;
|
|
1272
|
+
const num9 = m.m31 * m.m44 - m.m34 * m.offsetX;
|
|
1273
|
+
const num10 = m.m32 * m.offsetZ - m.m33 * m.offsetY;
|
|
1274
|
+
const num11 = m.m31 * m.offsetZ - m.m33 * m.offsetX;
|
|
1275
|
+
const num12 = m.m31 * m.offsetY - m.m32 * m.offsetX;
|
|
1276
|
+
const num13 = m.m23 * m.m44 - m.m24 * m.offsetZ;
|
|
1277
|
+
const num14 = m.m22 * m.m44 - m.m24 * m.offsetY;
|
|
1278
|
+
const num15 = m.m21 * m.m44 - m.m24 * m.offsetX;
|
|
1279
|
+
const num16 = m.m22 * m.offsetZ - m.m23 * m.offsetY;
|
|
1280
|
+
const num17 = m.m21 * m.offsetZ - m.m23 * m.offsetX;
|
|
1281
|
+
const num18 = m.m21 * m.offsetY - m.m22 * m.offsetX;
|
|
1282
|
+
const num19 = m.m23 * m.m34 - m.m24 * m.m33;
|
|
1283
|
+
const num20 = m.m22 * m.m34 - m.m24 * m.m32;
|
|
1284
|
+
const num21 = m.m21 * m.m34 - m.m24 * m.m31;
|
|
1285
|
+
const num22 = m.m22 * m.m33 - m.m23 * m.m32;
|
|
1286
|
+
const num23 = m.m21 * m.m33 - m.m23 * m.m31;
|
|
1287
|
+
const num24 = m.m21 * m.m32 - m.m22 * m.m31;
|
|
1288
|
+
const cofactor11 = m.m22 * num1 - m.m23 * num2 + m.m24 * num4;
|
|
1289
|
+
const cofactor12 = -(m.m21 * num1 - m.m23 * num3 + m.m24 * num5);
|
|
1290
|
+
const cofactor13 = m.m21 * num2 - m.m22 * num3 + m.m24 * num6;
|
|
1291
|
+
const cofactor14 = -(m.m21 * num4 - m.m22 * num5 + m.m23 * num6);
|
|
1292
|
+
const cofactor21 = -(m.m12 * num7 - m.m13 * num8 + m.m14 * num10);
|
|
1293
|
+
const cofactor22 = m.m11 * num7 - m.m13 * num9 + m.m14 * num11;
|
|
1294
|
+
const cofactor23 = -(m.m11 * num8 - m.m12 * num9 + m.m14 * num12);
|
|
1295
|
+
const cofactor24 = m.m11 * num10 - m.m12 * num11 + m.m13 * num12;
|
|
1296
|
+
const cofactor31 = m.m12 * num13 - m.m13 * num14 + m.m14 * num16;
|
|
1297
|
+
const cofactor32 = -(m.m11 * num13 - m.m13 * num15 + m.m14 * num17);
|
|
1298
|
+
const cofactor33 = m.m11 * num14 - m.m12 * num15 + m.m14 * num18;
|
|
1299
|
+
const cofactor34 = -(m.m11 * num16 - m.m12 * num17 + m.m13 * num18);
|
|
1300
|
+
const cofactor41 = -(m.m12 * num19 - m.m13 * num20 + m.m14 * num22);
|
|
1301
|
+
const cofactor42 = m.m11 * num19 - m.m13 * num21 + m.m14 * num23;
|
|
1302
|
+
const cofactor43 = -(m.m11 * num20 - m.m12 * num21 + m.m14 * num24);
|
|
1303
|
+
const cofactor44 = m.m11 * num22 - m.m12 * num23 + m.m13 * num24;
|
|
1304
|
+
const inverseDet = 1 / determinant;
|
|
1305
|
+
return _Matrix3D.from(cofactor11 * inverseDet, cofactor21 * inverseDet, cofactor31 * inverseDet, cofactor41 * inverseDet, cofactor12 * inverseDet, cofactor22 * inverseDet, cofactor32 * inverseDet, cofactor42 * inverseDet, cofactor13 * inverseDet, cofactor23 * inverseDet, cofactor33 * inverseDet, cofactor43 * inverseDet, cofactor14 * inverseDet, cofactor24 * inverseDet, cofactor34 * inverseDet, cofactor44 * inverseDet);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Moves a given point in 3D space given the transformation matrix.
|
|
1310
|
+
* @param point
|
|
1311
|
+
* @param matrix
|
|
1312
|
+
*/
|
|
1313
|
+
static transform(point, matrix) {
|
|
1314
|
+
var pt = { x: point.x, y: point.y, z: point.z };
|
|
1315
|
+
if (!_Matrix3D.isIdentity(matrix)) {
|
|
1316
|
+
var x = pt.x;
|
|
1317
|
+
var y = pt.y;
|
|
1318
|
+
var z = pt.z;
|
|
1319
|
+
pt.x = x * matrix.m11 + y * matrix.m21 + z * matrix.m31 + matrix.offsetX;
|
|
1320
|
+
pt.y = x * matrix.m12 + y * matrix.m22 + z * matrix.m32 + matrix.offsetY;
|
|
1321
|
+
pt.z = x * matrix.m13 + y * matrix.m23 + z * matrix.m33 + matrix.offsetZ;
|
|
1322
|
+
if (!_Matrix3D.isAffine(matrix)) {
|
|
1323
|
+
var num4 = x * matrix.m14 + y * matrix.m24 + z * matrix.m34 + matrix.m44;
|
|
1324
|
+
if (num4 != 0) {
|
|
1325
|
+
pt.x /= num4;
|
|
1326
|
+
pt.y /= num4;
|
|
1327
|
+
pt.z /= num4;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
return pt;
|
|
1332
|
+
}
|
|
1333
|
+
};
|
|
1334
|
+
var Quaternion = class _Quaternion {
|
|
1335
|
+
static identity() {
|
|
1336
|
+
return _Quaternion.from(0, 0, 0, 1);
|
|
1337
|
+
}
|
|
1338
|
+
static from(...args) {
|
|
1339
|
+
const l = 4;
|
|
1340
|
+
if (args.length !== l) {
|
|
1341
|
+
throw new RangeError(`Must provide exactly ${l} numbers`);
|
|
1342
|
+
}
|
|
1343
|
+
return {
|
|
1344
|
+
x: args[0],
|
|
1345
|
+
y: args[1],
|
|
1346
|
+
z: args[2],
|
|
1347
|
+
w: args[3]
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
static parse(input) {
|
|
1351
|
+
const arr = parseAsNumericalArray(input);
|
|
1352
|
+
if (arr && arr.length === 4) {
|
|
1353
|
+
return _Quaternion.from.apply(null, arr);
|
|
1354
|
+
}
|
|
1355
|
+
throw new Error(`Cannot parse "${input}" as a valid Quaternion.`);
|
|
1356
|
+
}
|
|
1357
|
+
static fromVectors(from, to) {
|
|
1358
|
+
Vector3D.normalize(from);
|
|
1359
|
+
Vector3D.normalize(to);
|
|
1360
|
+
const yieldQ = (x, y, z, w) => {
|
|
1361
|
+
const retval = _Quaternion.from(x, y, z, w);
|
|
1362
|
+
_Quaternion.normalize(retval);
|
|
1363
|
+
return retval;
|
|
1364
|
+
};
|
|
1365
|
+
let r = Vector3D.dot(from, to) + 1;
|
|
1366
|
+
if (r.isCloseTo(0)) {
|
|
1367
|
+
if (Math.abs(from.x) > Math.abs(from.z)) {
|
|
1368
|
+
return yieldQ(
|
|
1369
|
+
-from.y,
|
|
1370
|
+
from.x,
|
|
1371
|
+
0,
|
|
1372
|
+
/* r */
|
|
1373
|
+
0
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
return yieldQ(
|
|
1377
|
+
0,
|
|
1378
|
+
-from.z,
|
|
1379
|
+
from.y,
|
|
1380
|
+
/* r */
|
|
1381
|
+
0
|
|
1382
|
+
);
|
|
1383
|
+
} else {
|
|
1384
|
+
return yieldQ(from.y * to.z - from.z * to.y, from.z * to.x - from.x * to.z, from.x * to.y - from.y * to.x, r);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* In-place normalizarion of the provided quaternion
|
|
1389
|
+
* @param q {Quaternion}
|
|
1390
|
+
*/
|
|
1391
|
+
static normalize(q) {
|
|
1392
|
+
const l = _Quaternion.mag(q);
|
|
1393
|
+
if (l.isCloseTo(0)) {
|
|
1394
|
+
q.x = q.y = q.z = 0;
|
|
1395
|
+
q.w = 1;
|
|
1396
|
+
} else {
|
|
1397
|
+
const f = 1 / l;
|
|
1398
|
+
q.x *= f;
|
|
1399
|
+
q.y *= f;
|
|
1400
|
+
q.z *= f;
|
|
1401
|
+
q.w *= f;
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Returns a normalized copy of the source quaternion.
|
|
1406
|
+
* @param q {Quaternion}
|
|
1407
|
+
*/
|
|
1408
|
+
static unit(q) {
|
|
1409
|
+
const clone = _Quaternion.from(q.x, q.y, q.z, q.w);
|
|
1410
|
+
_Quaternion.normalize(clone);
|
|
1411
|
+
return clone;
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Creates a quaternion instance given the rotation axis and an angle (in degrees).
|
|
1415
|
+
* @param axis
|
|
1416
|
+
* @param angleDeg
|
|
1417
|
+
*/
|
|
1418
|
+
static fromAxisAngle(axis, angleDeg) {
|
|
1419
|
+
angleDeg %= 360;
|
|
1420
|
+
var angleInRadians = DEG2RAD * angleDeg;
|
|
1421
|
+
var length = Vector3D.mag(axis);
|
|
1422
|
+
if (length == 0) {
|
|
1423
|
+
throw new RangeError("Invalid argument");
|
|
1424
|
+
}
|
|
1425
|
+
var factor = Math.sin(0.5 * angleInRadians) / length;
|
|
1426
|
+
var x = axis.x * factor;
|
|
1427
|
+
var y = axis.y * factor;
|
|
1428
|
+
var z = axis.z * factor;
|
|
1429
|
+
return _Quaternion.from(x, y, z, Math.cos(0.5 * angleInRadians));
|
|
1430
|
+
}
|
|
1431
|
+
/**
|
|
1432
|
+
* Creates a quaternion instance given the rotation matrix.
|
|
1433
|
+
* @param rotationMatrix
|
|
1434
|
+
*/
|
|
1435
|
+
static fromRotationMatrix(rotationMatrix) {
|
|
1436
|
+
const trace = rotationMatrix.m11 + rotationMatrix.m22 + rotationMatrix.m33 + rotationMatrix.m44;
|
|
1437
|
+
if (trace > 0) {
|
|
1438
|
+
const sq = 0.5 / Math.sqrt(trace);
|
|
1439
|
+
const w = 0.25 / sq;
|
|
1440
|
+
const x = (rotationMatrix.m23 - rotationMatrix.m32) * sq;
|
|
1441
|
+
const y = (rotationMatrix.m31 - rotationMatrix.m13) * sq;
|
|
1442
|
+
const z = (rotationMatrix.m12 - rotationMatrix.m21) * sq;
|
|
1443
|
+
return _Quaternion.from(x, y, z, w);
|
|
1444
|
+
} else {
|
|
1445
|
+
if (rotationMatrix.m11 > rotationMatrix.m22 && rotationMatrix.m11 > rotationMatrix.m22) {
|
|
1446
|
+
const sq = 0.5 / Math.sqrt(rotationMatrix.m44 + rotationMatrix.m11 - rotationMatrix.m22 - rotationMatrix.m33);
|
|
1447
|
+
const w = (rotationMatrix.m23 - rotationMatrix.m32) * sq;
|
|
1448
|
+
const x = 0.25 / sq;
|
|
1449
|
+
const y = (rotationMatrix.m12 + rotationMatrix.m21) * sq;
|
|
1450
|
+
const z = (rotationMatrix.m31 + rotationMatrix.m13) * sq;
|
|
1451
|
+
return _Quaternion.from(x, y, z, w);
|
|
1452
|
+
} else if (rotationMatrix.m22 > rotationMatrix.m33) {
|
|
1453
|
+
const sq = 0.5 / Math.sqrt(rotationMatrix.m44 + rotationMatrix.m22 - rotationMatrix.m11 - rotationMatrix.m33);
|
|
1454
|
+
const z = (rotationMatrix.m23 + rotationMatrix.m32) * sq;
|
|
1455
|
+
const y = 0.25 / sq;
|
|
1456
|
+
const x = (rotationMatrix.m12 + rotationMatrix.m21) * sq;
|
|
1457
|
+
const w = (rotationMatrix.m31 - rotationMatrix.m13) * sq;
|
|
1458
|
+
return _Quaternion.from(x, y, z, w);
|
|
1459
|
+
} else {
|
|
1460
|
+
const sq = 0.5 / Math.sqrt(rotationMatrix.m44 + rotationMatrix.m33 - rotationMatrix.m11 - rotationMatrix.m22);
|
|
1461
|
+
const y = (rotationMatrix.m23 + rotationMatrix.m32) * sq;
|
|
1462
|
+
const z = 0.25 / sq;
|
|
1463
|
+
const w = (rotationMatrix.m12 - rotationMatrix.m21) * sq;
|
|
1464
|
+
const x = (rotationMatrix.m31 - rotationMatrix.m13) * sq;
|
|
1465
|
+
return _Quaternion.from(x, y, z, w);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
static conjugate(q) {
|
|
1470
|
+
return _Quaternion.from(-q.x, -q.y, -q.z, q.w);
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Returns the magnitude/length of the provided quaternion.
|
|
1474
|
+
* @param q {Quaternion}
|
|
1475
|
+
*/
|
|
1476
|
+
static mag(q) {
|
|
1477
|
+
return Math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
|
|
1478
|
+
}
|
|
1479
|
+
static norm(q) {
|
|
1480
|
+
return q.x * q.x + q.y * q.y + q.z * q.z;
|
|
1481
|
+
}
|
|
1482
|
+
static axis(q) {
|
|
1483
|
+
if (q.x == 0 && q.y == 0 && q.z == 0) {
|
|
1484
|
+
return Vector3D.j();
|
|
1485
|
+
}
|
|
1486
|
+
return Vector3D.unit(q);
|
|
1487
|
+
}
|
|
1488
|
+
static transform(v, q) {
|
|
1489
|
+
_Quaternion.normalize(q);
|
|
1490
|
+
const cross = Vector3D.cross(q, v);
|
|
1491
|
+
const t = Vector3D.scale(cross, 2);
|
|
1492
|
+
const tCross = Vector3D.cross(q, t);
|
|
1493
|
+
return {
|
|
1494
|
+
x: v.x + q.w * t.x + tCross.x,
|
|
1495
|
+
y: v.y + q.w * t.y + tCross.y,
|
|
1496
|
+
z: v.z + q.w * t.z + tCross.z
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Returns the rotation angle (in degrees) of the provided quaternion.
|
|
1501
|
+
* @param q
|
|
1502
|
+
*/
|
|
1503
|
+
static angle(q) {
|
|
1504
|
+
let y = Math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z);
|
|
1505
|
+
let x = q.w;
|
|
1506
|
+
if (y > Number.MAX_VALUE) {
|
|
1507
|
+
const num = Math.max(Math.abs(q.x), Math.max(Math.abs(q.y), Math.abs(q.z)));
|
|
1508
|
+
const num5 = q.x / num;
|
|
1509
|
+
const num4 = q.y / num;
|
|
1510
|
+
const num3 = q.z / num;
|
|
1511
|
+
y = Math.sqrt(num5 * num5 + num4 * num4 + num3 * num3);
|
|
1512
|
+
x /= num;
|
|
1513
|
+
}
|
|
1514
|
+
return Math.atan2(y, x) * 114.59155902616465;
|
|
1515
|
+
}
|
|
1516
|
+
static toRotationMatrix(q) {
|
|
1517
|
+
var m = Matrix3D.identity();
|
|
1518
|
+
var X = q.x;
|
|
1519
|
+
var Y = q.y;
|
|
1520
|
+
var Z = q.z;
|
|
1521
|
+
var W = q.w;
|
|
1522
|
+
m.m11 = 1 - 2 * Y * Y - 2 * Z * Z;
|
|
1523
|
+
m.m12 = 2 * X * Y + 2 * W * Z;
|
|
1524
|
+
m.m13 = 2 * X * Z - 2 * W * Y;
|
|
1525
|
+
m.m21 = 2 * X * Y - 2 * W * Z;
|
|
1526
|
+
m.m22 = 1 - 2 * X * X - 2 * Z * Z;
|
|
1527
|
+
m.m23 = 2 * Y * Z + 2 * W * X;
|
|
1528
|
+
m.m31 = 2 * W * Y + 2 * X * Z;
|
|
1529
|
+
m.m32 = 2 * Y * Z - 2 * W * X;
|
|
1530
|
+
m.m33 = 1 - 2 * X * X - 2 * Y * Y;
|
|
1531
|
+
return m;
|
|
1532
|
+
}
|
|
1533
|
+
static invert(q) {
|
|
1534
|
+
const n = _Quaternion.unit(q);
|
|
1535
|
+
return _Quaternion.conjugate(n);
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* Combines two quaternions.
|
|
1539
|
+
* @param q1
|
|
1540
|
+
* @param q2
|
|
1541
|
+
*/
|
|
1542
|
+
static multiply(q1, q2) {
|
|
1543
|
+
return _Quaternion.from(q1.w * q2.x + q1.x + q2.w + q1.y * q2.z - q1.z * q2.y, q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x, q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w, q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z);
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Returns the dot product of two quaternions.
|
|
1547
|
+
* @param q1
|
|
1548
|
+
* @param q2
|
|
1549
|
+
*/
|
|
1550
|
+
static dot(q1, q2) {
|
|
1551
|
+
var q = _Quaternion.multiply(q1, _Quaternion.conjugate(q2));
|
|
1552
|
+
return q.w;
|
|
1553
|
+
}
|
|
1554
|
+
};
|
|
1555
|
+
|
|
1556
|
+
// packages/numerical/dist/esm/geom/util3d.js
|
|
1557
|
+
var Utils3D = class {
|
|
1558
|
+
/**
|
|
1559
|
+
* Computes the intersection between a ray and a triangle in the 3D space - if any - using the Möller–Trumbore algorithm.
|
|
1560
|
+
* @param ray Array of two points identifying a ray.
|
|
1561
|
+
* @param triangle Array of three points identifying a triangle.
|
|
1562
|
+
* @returns Intersection point if any, otherwise null.
|
|
1563
|
+
*/
|
|
1564
|
+
static intersect(ray, triangle) {
|
|
1565
|
+
const [start, end] = ray;
|
|
1566
|
+
const vector = Vector3D.subtract(start, end);
|
|
1567
|
+
const [a, b, c] = triangle;
|
|
1568
|
+
const ab = Vector3D.subtract(a, b);
|
|
1569
|
+
const ac = Vector3D.subtract(a, c);
|
|
1570
|
+
const cross = Vector3D.cross(vector, ac);
|
|
1571
|
+
const det = Vector3D.dot(ab, cross);
|
|
1572
|
+
if (det.isCloseTo(0)) {
|
|
1573
|
+
return null;
|
|
1574
|
+
}
|
|
1575
|
+
const invDet = 1 / det;
|
|
1576
|
+
const s = Vector3D.subtract(start, a);
|
|
1577
|
+
const u = invDet * Vector3D.dot(s, cross);
|
|
1578
|
+
if (u < 0 || u > 1) {
|
|
1579
|
+
return null;
|
|
1580
|
+
}
|
|
1581
|
+
const scross = Vector3D.cross(s, ab);
|
|
1582
|
+
const v = invDet * Vector3D.dot(vector, scross);
|
|
1583
|
+
if (v < 0 || u + v > 1) {
|
|
1584
|
+
return null;
|
|
1585
|
+
}
|
|
1586
|
+
const t = invDet * Vector3D.dot(ac, scross);
|
|
1587
|
+
if (t < 0 || t.isCloseTo(0)) {
|
|
1588
|
+
return null;
|
|
1589
|
+
}
|
|
1590
|
+
return Vector3D.add(start, Vector3D.scale(vector, t));
|
|
1591
|
+
}
|
|
1592
|
+
};
|
|
1593
|
+
|
|
1594
|
+
// packages/numerical/dist/esm/index-geometry-linearalgebra.js
|
|
1595
|
+
var index_geometry_linearalgebra_exports = {};
|
|
1596
|
+
__export(index_geometry_linearalgebra_exports, {
|
|
1597
|
+
Matrix3D: () => Matrix3D,
|
|
1598
|
+
Quaternion: () => Quaternion,
|
|
1599
|
+
Spherical: () => Spherical,
|
|
1600
|
+
Vector: () => Vector,
|
|
1601
|
+
Vector3D: () => Vector3D
|
|
1602
|
+
});
|
|
1603
|
+
|
|
1604
|
+
// packages/numerical/dist/esm/index-mathematics.js
|
|
1605
|
+
var index_mathematics_exports = {};
|
|
1606
|
+
__export(index_mathematics_exports, {
|
|
1607
|
+
Complex: () => Complex,
|
|
1608
|
+
DataAnalysis: () => index_mathematics_dataanalysis_exports,
|
|
1609
|
+
NumberTheory: () => math_exports
|
|
1610
|
+
});
|
|
1611
|
+
|
|
1612
|
+
// packages/numerical/dist/esm/math/complex.js
|
|
1613
|
+
function complex(c) {
|
|
1614
|
+
if (typeof c === "number") {
|
|
1615
|
+
c = { real: c, img: 0 };
|
|
1616
|
+
}
|
|
1617
|
+
return c;
|
|
1618
|
+
}
|
|
1619
|
+
var NOT_A_COMPLEX;
|
|
1620
|
+
function buildComplex(real, img) {
|
|
1621
|
+
const c = {};
|
|
1622
|
+
Object.defineProperty(c, "real", { value: real, writable: false });
|
|
1623
|
+
Object.defineProperty(c, "img", { value: img, writable: false });
|
|
1624
|
+
return c;
|
|
1625
|
+
}
|
|
1626
|
+
function nac() {
|
|
1627
|
+
return NOT_A_COMPLEX || (NOT_A_COMPLEX = buildComplex(Number.NaN, Number.NaN));
|
|
1628
|
+
}
|
|
1629
|
+
var Complex = class {
|
|
1630
|
+
static build(real, img) {
|
|
1631
|
+
if (this.isComplex(real)) {
|
|
1632
|
+
return real;
|
|
1633
|
+
}
|
|
1634
|
+
img ??= 0;
|
|
1635
|
+
if (typeof real != "number" || typeof img != "number") {
|
|
1636
|
+
return nac();
|
|
1637
|
+
}
|
|
1638
|
+
return buildComplex(real, img || 0);
|
|
1639
|
+
}
|
|
1640
|
+
static add(a, b) {
|
|
1641
|
+
const ac = complex(a), bc = complex(b);
|
|
1642
|
+
return buildComplex(ac.real + bc.real, ac.img + bc.img);
|
|
1643
|
+
}
|
|
1644
|
+
static subtract(from, what) {
|
|
1645
|
+
const ac = complex(from), bc = complex(what);
|
|
1646
|
+
return buildComplex(ac.real - bc.real, ac.img - bc.img);
|
|
1647
|
+
}
|
|
1648
|
+
static multiply(a, b) {
|
|
1649
|
+
const ac = complex(a), bc = complex(b);
|
|
1650
|
+
return buildComplex(
|
|
1651
|
+
/* real*/
|
|
1652
|
+
ac.real * bc.real - ac.img * bc.img,
|
|
1653
|
+
/* img */
|
|
1654
|
+
ac.real * bc.img + ac.img * bc.real
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
static divide(a, b) {
|
|
1658
|
+
const ac = complex(a), bc = complex(b);
|
|
1659
|
+
const div = this.absSquare(bc).roundoff();
|
|
1660
|
+
if (div === 0) {
|
|
1661
|
+
return nac();
|
|
1662
|
+
}
|
|
1663
|
+
const inv_div = 1 / div;
|
|
1664
|
+
return buildComplex(
|
|
1665
|
+
/* real*/
|
|
1666
|
+
inv_div * (ac.real * bc.real + ac.img * bc.img),
|
|
1667
|
+
/* img */
|
|
1668
|
+
inv_div * (ac.img * bc.real - ac.real * bc.img)
|
|
1669
|
+
);
|
|
1670
|
+
}
|
|
1671
|
+
static absSquare(c) {
|
|
1672
|
+
const ac = complex(c);
|
|
1673
|
+
return Math.pow(ac.real, 2) + Math.pow(ac.img, 2);
|
|
1674
|
+
}
|
|
1675
|
+
static modulus(c) {
|
|
1676
|
+
return Math.sqrt(this.absSquare(c));
|
|
1677
|
+
}
|
|
1678
|
+
static isComplex(c) {
|
|
1679
|
+
return c != null && typeof c === "object" && "real" in c && "img" in c && typeof c.real === "number" && typeof c.img === "number";
|
|
1680
|
+
}
|
|
1681
|
+
static conjugate(a) {
|
|
1682
|
+
a = complex(a);
|
|
1683
|
+
return buildComplex(a.real, Math.abs(a.img) == 0 ? 0 : -a.img);
|
|
1684
|
+
}
|
|
1685
|
+
static equals(c1, c2) {
|
|
1686
|
+
const c_1 = this.build(c1), c_2 = this.build(c2);
|
|
1687
|
+
if (!this.isComplex(c1) || !this.isComplex(c2)) {
|
|
1688
|
+
return false;
|
|
1689
|
+
}
|
|
1690
|
+
return c_1.real === c_2.real && c_1.img === c_2.img;
|
|
1691
|
+
}
|
|
1692
|
+
//static isNaN(c: Complex | number): boolean {
|
|
1693
|
+
// switch (c) {
|
|
1694
|
+
// case undefined:
|
|
1695
|
+
// // doh!
|
|
1696
|
+
// return true;
|
|
1697
|
+
// case null:
|
|
1698
|
+
// // doh!
|
|
1699
|
+
// return false;
|
|
1700
|
+
// default:
|
|
1701
|
+
// if (!this.isComplex( c)
|
|
1702
|
+
// }
|
|
1703
|
+
//}
|
|
1704
|
+
static get NaC() {
|
|
1705
|
+
return nac();
|
|
1706
|
+
}
|
|
1707
|
+
};
|
|
1708
|
+
|
|
1709
|
+
// packages/numerical/dist/esm/index-mathematics-dataanalysis.js
|
|
1710
|
+
var index_mathematics_dataanalysis_exports = {};
|
|
1711
|
+
__export(index_mathematics_dataanalysis_exports, {
|
|
1712
|
+
Fourier: () => Fourier,
|
|
1713
|
+
Gaussian: () => Gaussian,
|
|
1714
|
+
Lagrange: () => Lagrange,
|
|
1715
|
+
Lagrangian: () => Lagrangian,
|
|
1716
|
+
Newton: () => Newton,
|
|
1717
|
+
Pchip: () => Pchip,
|
|
1718
|
+
SearchFunctions: () => SearchFunctions,
|
|
1719
|
+
Utils: () => Utils2
|
|
1720
|
+
});
|
|
1721
|
+
|
|
1722
|
+
// packages/numerical/dist/esm/math/fourier.js
|
|
1723
|
+
function euler(k, N) {
|
|
1724
|
+
const x = 2 * Math.PI * k / N;
|
|
1725
|
+
return Complex.build(Math.cos(x), Math.sin(x));
|
|
1726
|
+
}
|
|
1727
|
+
var unitCircle = {};
|
|
1728
|
+
function exp(k, N) {
|
|
1729
|
+
const memN = unitCircle[N] = unitCircle[N] || {};
|
|
1730
|
+
return memN[k] = memN[k] || euler(k, N);
|
|
1731
|
+
}
|
|
1732
|
+
function isPowerOfTwo(length) {
|
|
1733
|
+
return length > 0 && (length & length - 1) === 0;
|
|
1734
|
+
}
|
|
1735
|
+
function fftRec(data) {
|
|
1736
|
+
const retval = [], N = data.length;
|
|
1737
|
+
if (N === 1) {
|
|
1738
|
+
return [Complex.build(data[0])];
|
|
1739
|
+
}
|
|
1740
|
+
const retval_2n = fftRec(data.filter((_, i) => i % 2 === 0)), retval_2n1 = fftRec(data.filter((_, i) => i % 2 === 1));
|
|
1741
|
+
for (var k = 0; k < N / 2; k++) {
|
|
1742
|
+
const t = retval_2n[k], e = Complex.multiply(exp(k, N), retval_2n1[k]);
|
|
1743
|
+
retval[k] = Complex.add(t, e);
|
|
1744
|
+
retval[k + N / 2] = Complex.subtract(t, e);
|
|
1745
|
+
}
|
|
1746
|
+
return retval;
|
|
1747
|
+
}
|
|
1748
|
+
var Fourier = class {
|
|
1749
|
+
/**
|
|
1750
|
+
* Checks the input vector and outputs a frequency vector using the best performing algo.
|
|
1751
|
+
* @param data Input vector of any size
|
|
1752
|
+
* @param normalize Whether to normalize the signal or not (default true)
|
|
1753
|
+
*/
|
|
1754
|
+
static transform(data, normalize = true) {
|
|
1755
|
+
data = data || [];
|
|
1756
|
+
if (isPowerOfTwo(data.length)) {
|
|
1757
|
+
return this.fft(data, normalize);
|
|
1758
|
+
}
|
|
1759
|
+
return this.dft(data, normalize);
|
|
1760
|
+
}
|
|
1761
|
+
/**
|
|
1762
|
+
* Checks the input vector and applies inverted fourier transform's best performing algo.
|
|
1763
|
+
* @param data Input frequency vector
|
|
1764
|
+
*/
|
|
1765
|
+
static invert(data, normalize = true) {
|
|
1766
|
+
return this.idft(data || [], normalize);
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Naive Discrete Fourier Transform implementation (O(n^2) algo).
|
|
1770
|
+
* @param data Input vector of any size
|
|
1771
|
+
* @param normalize Whether to normalize the signal or not (default true)
|
|
1772
|
+
*/
|
|
1773
|
+
static dft(data, normalize = true) {
|
|
1774
|
+
const N = (data || [])?.length, DEN = normalize ? 1 / Math.sqrt(N) : 1, retval = [];
|
|
1775
|
+
for (let k = 0; k < N; k++) {
|
|
1776
|
+
retval.push({ real: 0, img: 0 });
|
|
1777
|
+
for (let j = 0; j < N; j++) {
|
|
1778
|
+
const e = exp(k * j, N), item = Complex.multiply(data[j], e), itemN = Complex.multiply(item, DEN);
|
|
1779
|
+
retval[k] = Complex.add(retval[k], itemN);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
return retval;
|
|
1783
|
+
}
|
|
1784
|
+
/**
|
|
1785
|
+
* Inverse Discrete Fourier Transform.
|
|
1786
|
+
* @param data Input vector data of any size
|
|
1787
|
+
*/
|
|
1788
|
+
static idft(data, normalize = true) {
|
|
1789
|
+
const reversed = data.map((i) => Complex.build(i.img, i.real)), temp = this.transform(reversed, normalize);
|
|
1790
|
+
return temp.map((c) => Complex.build(c.img, c.real));
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* FFT processing using Cooley-Tukey.
|
|
1794
|
+
* @param data Input vector (size MUST be power of 2)
|
|
1795
|
+
*/
|
|
1796
|
+
static fft(data, normalize = true) {
|
|
1797
|
+
const retval = fftRec(data);
|
|
1798
|
+
if (!normalize) {
|
|
1799
|
+
return retval;
|
|
1800
|
+
}
|
|
1801
|
+
const DEN = 1 / Math.sqrt(data.length);
|
|
1802
|
+
return retval.map((i) => Complex.multiply(i, DEN));
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
|
|
1806
|
+
// packages/numerical/dist/esm/math/gaussian.js
|
|
1807
|
+
var SQRT_PI = Math.sqrt(Math.PI);
|
|
1808
|
+
function erfc(x) {
|
|
1809
|
+
const z = Math.abs(x);
|
|
1810
|
+
const t = 1 / (1 + 0.5 * z);
|
|
1811
|
+
const ans = t * Math.exp(-z * z - 1.26551223 + t * (1.00002368 + t * (0.37409196 + t * (0.09678418 + t * (-0.18628806 + t * (0.27886807 + t * (-1.13520398 + t * (1.48851587 + t * (-0.82215223 + t * 0.17087277)))))))));
|
|
1812
|
+
return x >= 0 ? ans : 2 - ans;
|
|
1813
|
+
}
|
|
1814
|
+
var Gaussian = class {
|
|
1815
|
+
constructor(mean2, stdev2) {
|
|
1816
|
+
this.mean = mean2;
|
|
1817
|
+
this.stdev = Math.abs(stdev2);
|
|
1818
|
+
this.variance = Math.pow(stdev2, 2);
|
|
1819
|
+
}
|
|
1820
|
+
static get normal() {
|
|
1821
|
+
return _normal;
|
|
1822
|
+
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Un-normalized probability density function.
|
|
1825
|
+
* Can be useful as a weight function without requiring the integral w(x)dx to be 1.
|
|
1826
|
+
* @param x Input value
|
|
1827
|
+
*/
|
|
1828
|
+
weight(x) {
|
|
1829
|
+
return Math.exp(-0.5 * Math.pow(this._z(x), 2));
|
|
1830
|
+
}
|
|
1831
|
+
/**
|
|
1832
|
+
* Probability density function (PDF).
|
|
1833
|
+
* @param x Input value
|
|
1834
|
+
*/
|
|
1835
|
+
probabilityDensity(x) {
|
|
1836
|
+
const inv_coeff = this.stdev * Math.SQRT2 * SQRT_PI, exp_part = this.weight(x);
|
|
1837
|
+
return exp_part / inv_coeff;
|
|
1838
|
+
}
|
|
1839
|
+
_z(x) {
|
|
1840
|
+
return (x - this.mean) / this.stdev;
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1843
|
+
* Returns the area under the PDF from -∞ to x
|
|
1844
|
+
* @param x Input value
|
|
1845
|
+
*/
|
|
1846
|
+
probability(x) {
|
|
1847
|
+
return 0.5 * erfc(-this._z(x) / Math.SQRT2);
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
var _normal = new Gaussian(0, 1);
|
|
1851
|
+
|
|
1852
|
+
// packages/numerical/dist/esm/math/interpolation.js
|
|
1853
|
+
var Lagrange = class _Lagrange {
|
|
1854
|
+
constructor(points) {
|
|
1855
|
+
this.#set = points;
|
|
1856
|
+
}
|
|
1857
|
+
#set;
|
|
1858
|
+
#weights = null;
|
|
1859
|
+
_prepareBarycentric() {
|
|
1860
|
+
if (this.#weights) {
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
const points = this.#set || [];
|
|
1864
|
+
const n = points.length;
|
|
1865
|
+
if (n === 0) {
|
|
1866
|
+
this.#weights = [];
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
const logs = new Array(n);
|
|
1870
|
+
const signs = new Array(n);
|
|
1871
|
+
for (let j = 0; j < n; j++) {
|
|
1872
|
+
let sumLog = 0;
|
|
1873
|
+
let sign = 1;
|
|
1874
|
+
const xj = points[j].x;
|
|
1875
|
+
for (let m = 0; m < n; m++) {
|
|
1876
|
+
if (m === j)
|
|
1877
|
+
continue;
|
|
1878
|
+
const diff = xj - points[m].x;
|
|
1879
|
+
if (diff === 0) {
|
|
1880
|
+
throw new Error("Invalid function provided.");
|
|
1881
|
+
}
|
|
1882
|
+
if (diff < 0)
|
|
1883
|
+
sign = -sign;
|
|
1884
|
+
sumLog += Math.log(Math.abs(diff));
|
|
1885
|
+
}
|
|
1886
|
+
logs[j] = -sumLog;
|
|
1887
|
+
signs[j] = sign;
|
|
1888
|
+
}
|
|
1889
|
+
let maxLog = logs[0];
|
|
1890
|
+
for (let j = 1; j < n; j++) {
|
|
1891
|
+
if (logs[j] > maxLog)
|
|
1892
|
+
maxLog = logs[j];
|
|
1893
|
+
}
|
|
1894
|
+
const w = new Array(n);
|
|
1895
|
+
for (let j = 0; j < n; j++) {
|
|
1896
|
+
const scaled = Math.exp(logs[j] - maxLog);
|
|
1897
|
+
w[j] = signs[j] * scaled;
|
|
1898
|
+
}
|
|
1899
|
+
this.#weights = w;
|
|
1900
|
+
}
|
|
1901
|
+
_computeBarycentric(x) {
|
|
1902
|
+
const points = this.#set || [];
|
|
1903
|
+
if (!this.#weights) {
|
|
1904
|
+
this._prepareBarycentric();
|
|
1905
|
+
}
|
|
1906
|
+
const w = this.#weights || [];
|
|
1907
|
+
let numerator = 0;
|
|
1908
|
+
let denominator = 0;
|
|
1909
|
+
for (let j = 0; j < points.length; j++) {
|
|
1910
|
+
const xj = points[j].x;
|
|
1911
|
+
if (x === xj) {
|
|
1912
|
+
return points[j].y;
|
|
1913
|
+
}
|
|
1914
|
+
const temp = w[j] / (x - xj);
|
|
1915
|
+
numerator += temp * points[j].y;
|
|
1916
|
+
denominator += temp;
|
|
1917
|
+
}
|
|
1918
|
+
return numerator / denominator;
|
|
1919
|
+
}
|
|
1920
|
+
// Iterative/global Lagrange computation removed — barycentric is used
|
|
1921
|
+
// exclusively for interpolation.
|
|
1922
|
+
interpolate(x) {
|
|
1923
|
+
return this._computeBarycentric(x);
|
|
1924
|
+
}
|
|
1925
|
+
static create(...values) {
|
|
1926
|
+
var array = [];
|
|
1927
|
+
if (arguments.length > 0 && Array.isArray(arguments[0])) {
|
|
1928
|
+
array = arguments[0];
|
|
1929
|
+
} else {
|
|
1930
|
+
array = Array.from(values);
|
|
1931
|
+
}
|
|
1932
|
+
const points = array.map((y, x) => {
|
|
1933
|
+
return typeof y === "number" ? { x, y } : y;
|
|
1934
|
+
});
|
|
1935
|
+
return new _Lagrange(points);
|
|
1936
|
+
}
|
|
1937
|
+
};
|
|
1938
|
+
var Lagrangian = {
|
|
1939
|
+
create: Lagrange.create
|
|
1940
|
+
};
|
|
1941
|
+
var Pchip = class _Pchip {
|
|
1942
|
+
constructor(points) {
|
|
1943
|
+
if (!points || points.length < 2)
|
|
1944
|
+
throw new Error("Need at least 2 points");
|
|
1945
|
+
const pts = points.slice().sort((a, b) => a.x - b.x);
|
|
1946
|
+
this.xs = pts.map((p) => p.x);
|
|
1947
|
+
this.ys = pts.map((p) => p.y);
|
|
1948
|
+
this.ms = this.computeDerivatives();
|
|
1949
|
+
}
|
|
1950
|
+
computeDerivatives() {
|
|
1951
|
+
const n = this.xs.length;
|
|
1952
|
+
const h = new Array(n - 1);
|
|
1953
|
+
const delta = new Array(n - 1);
|
|
1954
|
+
for (let i = 0; i < n - 1; i++) {
|
|
1955
|
+
h[i] = this.xs[i + 1] - this.xs[i];
|
|
1956
|
+
delta[i] = (this.ys[i + 1] - this.ys[i]) / h[i];
|
|
1957
|
+
}
|
|
1958
|
+
const m = new Array(n).fill(0);
|
|
1959
|
+
m[0] = delta[0];
|
|
1960
|
+
m[n - 1] = delta[n - 2];
|
|
1961
|
+
for (let i = 1; i < n - 1; i++) {
|
|
1962
|
+
const d1 = delta[i - 1], d2 = delta[i];
|
|
1963
|
+
if (d1 === 0 || d2 === 0 || d1 * d2 <= 0) {
|
|
1964
|
+
m[i] = 0;
|
|
1965
|
+
} else {
|
|
1966
|
+
const w1 = 2 * h[i] + h[i - 1];
|
|
1967
|
+
const w2 = h[i] + 2 * h[i - 1];
|
|
1968
|
+
m[i] = (w1 + w2) / (w1 / d1 + w2 / d2);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
for (let i = 0; i < n - 1; i++) {
|
|
1972
|
+
if (delta[i] === 0) {
|
|
1973
|
+
m[i] = 0;
|
|
1974
|
+
m[i + 1] = 0;
|
|
1975
|
+
} else {
|
|
1976
|
+
const a = m[i] / delta[i];
|
|
1977
|
+
const b = m[i + 1] / delta[i];
|
|
1978
|
+
const s = a * a + b * b;
|
|
1979
|
+
if (s > 9) {
|
|
1980
|
+
const tau = 3 / Math.sqrt(s);
|
|
1981
|
+
m[i] = tau * a * delta[i];
|
|
1982
|
+
m[i + 1] = tau * b * delta[i];
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
return m;
|
|
1987
|
+
}
|
|
1988
|
+
interpolate(x) {
|
|
1989
|
+
const xs = this.xs, ys = this.ys, ms = this.ms;
|
|
1990
|
+
const n = xs.length;
|
|
1991
|
+
if (x <= xs[0]) {
|
|
1992
|
+
const slope = ms[0];
|
|
1993
|
+
return ys[0] + slope * (x - xs[0]);
|
|
1994
|
+
}
|
|
1995
|
+
if (x >= xs[n - 1]) {
|
|
1996
|
+
const slope = ms[n - 1];
|
|
1997
|
+
return ys[n - 1] + slope * (x - xs[n - 1]);
|
|
1998
|
+
}
|
|
1999
|
+
let i = 0, j = n - 1;
|
|
2000
|
+
while (j - i > 1) {
|
|
2001
|
+
const mid = i + j >> 1;
|
|
2002
|
+
if (xs[mid] <= x)
|
|
2003
|
+
i = mid;
|
|
2004
|
+
else
|
|
2005
|
+
j = mid;
|
|
2006
|
+
}
|
|
2007
|
+
const h = xs[i + 1] - xs[i];
|
|
2008
|
+
const t = (x - xs[i]) / h;
|
|
2009
|
+
const t2 = t * t, t3 = t2 * t;
|
|
2010
|
+
const h00 = 2 * t3 - 3 * t2 + 1;
|
|
2011
|
+
const h10 = t3 - 2 * t2 + t;
|
|
2012
|
+
const h01 = -2 * t3 + 3 * t2;
|
|
2013
|
+
const h11 = t3 - t2;
|
|
2014
|
+
return h00 * ys[i] + h10 * (ms[i] * h) + h01 * ys[i + 1] + h11 * (ms[i + 1] * h);
|
|
2015
|
+
}
|
|
2016
|
+
static create(...values) {
|
|
2017
|
+
var array = [];
|
|
2018
|
+
if (arguments.length > 0 && Array.isArray(arguments[0])) {
|
|
2019
|
+
array = arguments[0];
|
|
2020
|
+
} else {
|
|
2021
|
+
array = Array.from(values);
|
|
2022
|
+
}
|
|
2023
|
+
const points = array.map((y, x) => {
|
|
2024
|
+
return typeof y === "number" ? { x, y } : y;
|
|
2025
|
+
});
|
|
2026
|
+
return new _Pchip(points);
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2029
|
+
var Newton = class _Newton {
|
|
2030
|
+
constructor(points) {
|
|
2031
|
+
if (!points || points.length === 0)
|
|
2032
|
+
throw new Error("Need at least 1 point");
|
|
2033
|
+
const pts = points.slice().sort((a2, b) => a2.x - b.x);
|
|
2034
|
+
this.xs = pts.map((p) => p.x);
|
|
2035
|
+
const n = pts.length;
|
|
2036
|
+
const a = pts.map((p) => p.y);
|
|
2037
|
+
for (let j = 1; j < n; j++) {
|
|
2038
|
+
for (let i = n - 1; i >= j; i--) {
|
|
2039
|
+
const denom = this.xs[i] - this.xs[i - j];
|
|
2040
|
+
if (denom === 0)
|
|
2041
|
+
throw new Error("Invalid function provided.");
|
|
2042
|
+
a[i] = (a[i] - a[i - 1]) / denom;
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
this.coeffs = a;
|
|
2046
|
+
}
|
|
2047
|
+
interpolate(x) {
|
|
2048
|
+
const xs = this.xs, a = this.coeffs;
|
|
2049
|
+
const n = a.length;
|
|
2050
|
+
if (n === 0)
|
|
2051
|
+
return NaN;
|
|
2052
|
+
let result = a[n - 1];
|
|
2053
|
+
for (let i = n - 2; i >= 0; i--) {
|
|
2054
|
+
result = result * (x - xs[i]) + a[i];
|
|
2055
|
+
}
|
|
2056
|
+
return result;
|
|
2057
|
+
}
|
|
2058
|
+
static create(...values) {
|
|
2059
|
+
var array = [];
|
|
2060
|
+
if (arguments.length > 0 && Array.isArray(arguments[0])) {
|
|
2061
|
+
array = arguments[0];
|
|
2062
|
+
} else {
|
|
2063
|
+
array = Array.from(values);
|
|
2064
|
+
}
|
|
2065
|
+
const points = array.map((y, x) => {
|
|
2066
|
+
return typeof y === "number" ? { x, y } : y;
|
|
2067
|
+
});
|
|
2068
|
+
return new _Newton(points);
|
|
2069
|
+
}
|
|
2070
|
+
};
|
|
2071
|
+
|
|
2072
|
+
// packages/numerical/dist/esm/math/statistics.js
|
|
2073
|
+
var selfSelector = (i, _) => i;
|
|
2074
|
+
function mode(set, selector = selfSelector) {
|
|
2075
|
+
const groups = Object.groupBy(set, selector);
|
|
2076
|
+
let key, max = 0;
|
|
2077
|
+
for (let group in groups) {
|
|
2078
|
+
const l = groups[group].length;
|
|
2079
|
+
if (l > max) {
|
|
2080
|
+
key = parseInt(group);
|
|
2081
|
+
max = l;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
return key;
|
|
2085
|
+
}
|
|
2086
|
+
function median(set, selector = selfSelector) {
|
|
2087
|
+
const setCopy = set.slice(0), length = setCopy.length;
|
|
2088
|
+
setCopy.sort((a, b) => {
|
|
2089
|
+
const i = selector(a), j = selector(b);
|
|
2090
|
+
return i - j;
|
|
2091
|
+
});
|
|
2092
|
+
const index = Math.ceil(setCopy.length / 2) - 1;
|
|
2093
|
+
if (length % 2 == 1) {
|
|
2094
|
+
return setCopy[index];
|
|
2095
|
+
}
|
|
2096
|
+
return 0.5 * (setCopy[index + 1] + setCopy[index]);
|
|
2097
|
+
}
|
|
2098
|
+
function sum(set, selector = selfSelector) {
|
|
2099
|
+
return set.reduce((p, c, i) => p + selector(c, i), 0);
|
|
2100
|
+
}
|
|
2101
|
+
function mean(set, selector = selfSelector) {
|
|
2102
|
+
return sum(set, selector) / set.length;
|
|
2103
|
+
}
|
|
2104
|
+
function variance(set, selector = selfSelector, bessel = false) {
|
|
2105
|
+
const avg = mean(set, selector);
|
|
2106
|
+
return set.reduce((p, c) => Math.pow(c - avg, 2) + p, 0) / (set.length - (bessel ? 1 : 0));
|
|
2107
|
+
}
|
|
2108
|
+
function stdev(set, selector = selfSelector, bessel = false) {
|
|
2109
|
+
const v = variance(set, selector, bessel);
|
|
2110
|
+
return Math.sqrt(v);
|
|
2111
|
+
}
|
|
2112
|
+
function correlation(set, arg2, selector1 = selfSelector, selector2 = selfSelector) {
|
|
2113
|
+
const twoArrays = Array.isArray(arg2);
|
|
2114
|
+
if (twoArrays && arg2.length !== set.length) {
|
|
2115
|
+
throw new Error("Sets must be of the same length.");
|
|
2116
|
+
}
|
|
2117
|
+
const length = set.length;
|
|
2118
|
+
const setX = set, setY = twoArrays ? arg2 : set;
|
|
2119
|
+
const selectorX = twoArrays ? selector1 : arg2;
|
|
2120
|
+
const selectorY = twoArrays ? selector2 : selector1;
|
|
2121
|
+
const meanX = mean(setX, selectorX);
|
|
2122
|
+
const meanY = mean(setY, selectorY);
|
|
2123
|
+
let accNum = 0;
|
|
2124
|
+
let accDenXSq = 0;
|
|
2125
|
+
let accDenYSq = 0;
|
|
2126
|
+
for (let i = 0; i < length; i++) {
|
|
2127
|
+
const itemX = setX[i], itemY = setY[i];
|
|
2128
|
+
const a = selectorX(itemX, i) - meanX;
|
|
2129
|
+
const b = selectorY(itemY, i) - meanY;
|
|
2130
|
+
accNum += a * b;
|
|
2131
|
+
accDenXSq += a * a;
|
|
2132
|
+
accDenYSq += b * b;
|
|
2133
|
+
}
|
|
2134
|
+
return accNum / Math.sqrt(accDenXSq * accDenYSq);
|
|
2135
|
+
}
|
|
2136
|
+
function linearRegression(set, arg2, selector1 = selfSelector, selector2 = selfSelector) {
|
|
2137
|
+
const twoArrays = Array.isArray(arg2);
|
|
2138
|
+
if (twoArrays && arg2.length !== set.length) {
|
|
2139
|
+
throw new Error("Sets must be of the same length.");
|
|
2140
|
+
}
|
|
2141
|
+
const setX = set, setY = twoArrays ? arg2 : set;
|
|
2142
|
+
const selectorX0 = twoArrays ? selector1 : arg2;
|
|
2143
|
+
const selectorY0 = twoArrays ? selector2 : selectorX0;
|
|
2144
|
+
const selectorX = selectorX0 ?? ((p) => p.x);
|
|
2145
|
+
const selectorY = selectorY0 ?? ((p) => p.y);
|
|
2146
|
+
const length = set.length;
|
|
2147
|
+
let xyAcc = 0, xAcc = 0, yAcc = 0, x2Acc = 0, y2Acc = 0;
|
|
2148
|
+
for (let i = 0; i < length; i++) {
|
|
2149
|
+
const itemX = setX[i], itemY = setY[i];
|
|
2150
|
+
const x = selectorX(itemX, i);
|
|
2151
|
+
const y = selectorY(itemY, i);
|
|
2152
|
+
xyAcc += x * y;
|
|
2153
|
+
xAcc += x;
|
|
2154
|
+
yAcc += y;
|
|
2155
|
+
x2Acc += x * x;
|
|
2156
|
+
y2Acc += y * y;
|
|
2157
|
+
}
|
|
2158
|
+
const m = (length * xyAcc - xAcc * yAcc) / (length * x2Acc - Math.pow(xAcc, 2));
|
|
2159
|
+
const q = (yAcc - m * xAcc) / length;
|
|
2160
|
+
return [m, q];
|
|
2161
|
+
}
|
|
2162
|
+
var Utils2 = class {
|
|
2163
|
+
static sum(set, selector) {
|
|
2164
|
+
return sum(set, selector);
|
|
2165
|
+
}
|
|
2166
|
+
static mean(set, selector) {
|
|
2167
|
+
return mean(set, selector);
|
|
2168
|
+
}
|
|
2169
|
+
static median(set, selector) {
|
|
2170
|
+
return median(set, selector);
|
|
2171
|
+
}
|
|
2172
|
+
static mode(set, selector) {
|
|
2173
|
+
return mode(set, selector);
|
|
2174
|
+
}
|
|
2175
|
+
static var(set, selector) {
|
|
2176
|
+
return variance(set, selector, true);
|
|
2177
|
+
}
|
|
2178
|
+
static varp(set, selector) {
|
|
2179
|
+
return variance(set, selector, false);
|
|
2180
|
+
}
|
|
2181
|
+
static stdevp(set, selector) {
|
|
2182
|
+
return stdev(set, selector, false);
|
|
2183
|
+
}
|
|
2184
|
+
static stdev(set, selector) {
|
|
2185
|
+
return stdev(set, selector, true);
|
|
2186
|
+
}
|
|
2187
|
+
static correlation(set, arg2, selector1, selector2) {
|
|
2188
|
+
return correlation(set, arg2, selector1, selector2);
|
|
2189
|
+
}
|
|
2190
|
+
static linearRegression(set, arg2, selector1, selector2) {
|
|
2191
|
+
return linearRegression(set, arg2, selector1, selector2);
|
|
2192
|
+
}
|
|
2193
|
+
// restart from here: https://en.wikipedia.org/wiki/Multivariate_statistics#Multivariate_analysis
|
|
2194
|
+
/**
|
|
2195
|
+
* Returns a set of utilities for gaussian distribution computations.
|
|
2196
|
+
* @param mean
|
|
2197
|
+
* @param stdev
|
|
2198
|
+
*/
|
|
2199
|
+
static gaussian(mean2, stdev2) {
|
|
2200
|
+
return new Gaussian(mean2, stdev2);
|
|
2201
|
+
}
|
|
2202
|
+
};
|
|
2203
|
+
function searchFnFactory(minAssigner, maxAssigner) {
|
|
2204
|
+
const fn = (left, right, f, tolerance) => {
|
|
2205
|
+
let min = left;
|
|
2206
|
+
let max = right;
|
|
2207
|
+
let x1 = minAssigner(min, max);
|
|
2208
|
+
let x2 = maxAssigner(min, max);
|
|
2209
|
+
let f1 = f(x1);
|
|
2210
|
+
let f2 = f(x2);
|
|
2211
|
+
while (max - min > tolerance) {
|
|
2212
|
+
if (f1 < f2) {
|
|
2213
|
+
max = x2;
|
|
2214
|
+
x2 = x1;
|
|
2215
|
+
f2 = f1;
|
|
2216
|
+
x1 = minAssigner(min, max);
|
|
2217
|
+
f1 = f(x1);
|
|
2218
|
+
} else {
|
|
2219
|
+
min = x1;
|
|
2220
|
+
x1 = x2;
|
|
2221
|
+
f1 = f2;
|
|
2222
|
+
x2 = maxAssigner(min, max);
|
|
2223
|
+
f2 = f(x2);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
return 0.5 * (x1 + x2);
|
|
2227
|
+
};
|
|
2228
|
+
return fn;
|
|
2229
|
+
}
|
|
2230
|
+
var DEFAULT_TOLERANCE = 1e-3;
|
|
2231
|
+
var goldenRatioSearch = (left, right, weight, tolerance = DEFAULT_TOLERANCE) => {
|
|
2232
|
+
const phi = 1.618;
|
|
2233
|
+
const minAssigner = (min, max) => max - (max - min) / phi;
|
|
2234
|
+
const maxAssigner = (min, max) => min + (max - min) / phi;
|
|
2235
|
+
const fn = searchFnFactory(minAssigner, maxAssigner);
|
|
2236
|
+
return fn(left, right, weight, tolerance);
|
|
2237
|
+
};
|
|
2238
|
+
function linearSearch(segments) {
|
|
2239
|
+
if (segments <= 1) {
|
|
2240
|
+
throw new RangeError("segments must be a number grater than 1.");
|
|
2241
|
+
}
|
|
2242
|
+
return (left, right, weight, tolerance = DEFAULT_TOLERANCE) => {
|
|
2243
|
+
const minAssigner = (min, max) => min + (max - min) / segments;
|
|
2244
|
+
const maxAssigner = (min, max) => max - (max - min) / segments;
|
|
2245
|
+
const fn = searchFnFactory(minAssigner, maxAssigner);
|
|
2246
|
+
return fn(left, right, weight, tolerance);
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
var gaussianSearch = (left, right, weight, tolerance = DEFAULT_TOLERANCE) => {
|
|
2250
|
+
var sigma0 = null;
|
|
2251
|
+
var length0 = null;
|
|
2252
|
+
const computeSigma = (min, max) => {
|
|
2253
|
+
length0 ??= max - min;
|
|
2254
|
+
sigma0 ??= length0 / Math.sqrt(12);
|
|
2255
|
+
return sigma0 * (max - min) / length0;
|
|
2256
|
+
};
|
|
2257
|
+
const minAssigner = (min, max) => Math.max(min, (max + min) / 2 - computeSigma(min, max));
|
|
2258
|
+
const maxAssigner = (min, max) => Math.min(max, (max + min) / 2 + computeSigma(min, max));
|
|
2259
|
+
const fn = searchFnFactory(minAssigner, maxAssigner);
|
|
2260
|
+
return fn(left, right, weight, tolerance);
|
|
2261
|
+
};
|
|
2262
|
+
var SearchFunctions = {
|
|
2263
|
+
/**
|
|
2264
|
+
* Creates a linear search function.
|
|
2265
|
+
* @param segments How many segments to split the range into.
|
|
2266
|
+
*/
|
|
2267
|
+
linear: (segments = 2) => linearSearch(segments),
|
|
2268
|
+
/** Golden section search function. */
|
|
2269
|
+
goldenRatio: goldenRatioSearch,
|
|
2270
|
+
/** Gaussian search function. */
|
|
2271
|
+
gaussian: gaussianSearch
|
|
2272
|
+
};
|
|
2273
|
+
|
|
2274
|
+
// packages/numerical/dist/esm/math/math.js
|
|
2275
|
+
var math_exports = {};
|
|
2276
|
+
__export(math_exports, {
|
|
2277
|
+
Utils: () => Utils3
|
|
2278
|
+
});
|
|
2279
|
+
import { Numbers, NullChecker as NullChecker3 } from "@pacem/pacem-foundation";
|
|
2280
|
+
var ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
2281
|
+
var OUT_OF_RANGE = `Radix out of range: possible values go between positive 1 exclusive and ${ALPHABET.length} inclusive`;
|
|
2282
|
+
var Utils3 = class _Utils {
|
|
2283
|
+
static lcd(...args) {
|
|
2284
|
+
if (NullChecker3.isNullOrEmpty(args) || args.length <= 1) {
|
|
2285
|
+
throw "Insufficient set of numbers.";
|
|
2286
|
+
}
|
|
2287
|
+
function ex9(x, y) {
|
|
2288
|
+
if (!y)
|
|
2289
|
+
return y === 0 ? x : NaN;
|
|
2290
|
+
return ex9(y, x % y);
|
|
2291
|
+
}
|
|
2292
|
+
function ex10(x, y) {
|
|
2293
|
+
return x * y / ex9(x, y);
|
|
2294
|
+
}
|
|
2295
|
+
let result = Math.round(args[0]);
|
|
2296
|
+
for (let j = 1; j < args.length; j++) {
|
|
2297
|
+
result = ex10(result, Math.round(args[j]));
|
|
2298
|
+
}
|
|
2299
|
+
return result;
|
|
2300
|
+
}
|
|
2301
|
+
static gcd(a, b) {
|
|
2302
|
+
a = Math.round(a), b = Math.round(b);
|
|
2303
|
+
if (a === 0) {
|
|
2304
|
+
return b;
|
|
2305
|
+
}
|
|
2306
|
+
return _Utils.gcd(b % a, a);
|
|
2307
|
+
}
|
|
2308
|
+
static rebaseInt(v, from, to) {
|
|
2309
|
+
return Numbers.rebase(v, from, to);
|
|
2310
|
+
}
|
|
2311
|
+
static rebaseFloat(v, fromRadix, toRadix, precision = 12) {
|
|
2312
|
+
const from10 = _Utils.rebaseFloat10ToN, to10 = _Utils.rebaseFloatNTo10;
|
|
2313
|
+
if (fromRadix === 10) {
|
|
2314
|
+
const vFloat = parseFloat(v.toString());
|
|
2315
|
+
if (toRadix % 1 === 0) {
|
|
2316
|
+
return _Utils.rebaseFloat10ToNIntBase(vFloat, toRadix, precision);
|
|
2317
|
+
}
|
|
2318
|
+
return from10(vFloat, toRadix, precision);
|
|
2319
|
+
}
|
|
2320
|
+
if (toRadix === 10) {
|
|
2321
|
+
return to10(v.toString(), fromRadix, precision);
|
|
2322
|
+
}
|
|
2323
|
+
const _10 = to10(v.toString(), fromRadix, precision + 1);
|
|
2324
|
+
return from10(_10, toRadix, precision);
|
|
2325
|
+
}
|
|
2326
|
+
static rebaseFloatNTo10(v, radix, precision = 12) {
|
|
2327
|
+
const alphabet = ALPHABET;
|
|
2328
|
+
if (radix <= 1 || radix > alphabet.length) {
|
|
2329
|
+
throw new Error(OUT_OF_RANGE);
|
|
2330
|
+
}
|
|
2331
|
+
const vs = (v ?? "0").toString().toLowerCase(), nan = Number.NaN;
|
|
2332
|
+
if (vs === "0") {
|
|
2333
|
+
return 0;
|
|
2334
|
+
}
|
|
2335
|
+
const arr = /^([-+]?) *([\da-z]+)?(.[\da-z]+)?$/.exec(vs);
|
|
2336
|
+
if (arr.length < 2) {
|
|
2337
|
+
return nan;
|
|
2338
|
+
}
|
|
2339
|
+
const sign = arr[1] === "-" ? -1 : 1, int = arr[2] || "0", frac = arr[3] || ".0";
|
|
2340
|
+
let retval = 0, cursor = 0;
|
|
2341
|
+
for (let digit of int + frac.substr(1)) {
|
|
2342
|
+
const num = alphabet.indexOf(digit);
|
|
2343
|
+
if (num === -1 || num >= radix) {
|
|
2344
|
+
return nan;
|
|
2345
|
+
}
|
|
2346
|
+
retval += num * Math.pow(radix, int.length - cursor - 1);
|
|
2347
|
+
cursor++;
|
|
2348
|
+
}
|
|
2349
|
+
return sign * retval;
|
|
2350
|
+
}
|
|
2351
|
+
static rebaseFloat10ToNIntBase(v, radix, precision = 12) {
|
|
2352
|
+
const alphabet = ALPHABET;
|
|
2353
|
+
if (radix <= 1 || radix > alphabet.length) {
|
|
2354
|
+
throw new Error(OUT_OF_RANGE);
|
|
2355
|
+
}
|
|
2356
|
+
const to = radix;
|
|
2357
|
+
let value = Math.abs(v ?? 0);
|
|
2358
|
+
if (value === 0 || Number.isNaN(value)) {
|
|
2359
|
+
return v.toString();
|
|
2360
|
+
}
|
|
2361
|
+
let decimals = (value % 1).roundoff();
|
|
2362
|
+
let output = "";
|
|
2363
|
+
do {
|
|
2364
|
+
let div = value / to;
|
|
2365
|
+
let divFloor = Math.floor(div);
|
|
2366
|
+
let mod = (value - divFloor * to).roundoff();
|
|
2367
|
+
let intPart = Math.floor(mod);
|
|
2368
|
+
if (intPart >= alphabet.length) {
|
|
2369
|
+
output = `[${intPart}]` + output;
|
|
2370
|
+
} else {
|
|
2371
|
+
output = alphabet[intPart] + output;
|
|
2372
|
+
}
|
|
2373
|
+
if (divFloor <= 0) {
|
|
2374
|
+
break;
|
|
2375
|
+
}
|
|
2376
|
+
value = divFloor;
|
|
2377
|
+
} while (true);
|
|
2378
|
+
output = output.replace(/^0+/, "");
|
|
2379
|
+
output ||= "0";
|
|
2380
|
+
if (decimals === 0) {
|
|
2381
|
+
return output;
|
|
2382
|
+
}
|
|
2383
|
+
let rebasedFrac = "";
|
|
2384
|
+
do {
|
|
2385
|
+
value = (decimals * to).roundoff();
|
|
2386
|
+
const intPart = Math.floor(value), remainder = (value - intPart).roundoff();
|
|
2387
|
+
if (intPart >= alphabet.length) {
|
|
2388
|
+
rebasedFrac = `[${intPart}]`;
|
|
2389
|
+
} else {
|
|
2390
|
+
rebasedFrac += alphabet[intPart];
|
|
2391
|
+
}
|
|
2392
|
+
decimals = remainder;
|
|
2393
|
+
} while (!decimals.isCloseTo(0) && rebasedFrac.length < precision);
|
|
2394
|
+
if (/^0*$/.test(rebasedFrac)) {
|
|
2395
|
+
return output;
|
|
2396
|
+
}
|
|
2397
|
+
return (Math.sign(v) < 0 ? "-" : "") + output + "." + rebasedFrac.replace(/0+$/, "");
|
|
2398
|
+
}
|
|
2399
|
+
static rebaseFloat10ToN(v, radix, precision = 12) {
|
|
2400
|
+
const alphabet = ALPHABET;
|
|
2401
|
+
if (radix <= 1 || radix > alphabet.length) {
|
|
2402
|
+
throw new Error(OUT_OF_RANGE);
|
|
2403
|
+
}
|
|
2404
|
+
const to = radix;
|
|
2405
|
+
let value = Math.abs(v ?? 0);
|
|
2406
|
+
if (value === 0 || Number.isNaN(value)) {
|
|
2407
|
+
return v.toString();
|
|
2408
|
+
}
|
|
2409
|
+
let sign = Math.sign(v) < 0 ? "-" : "";
|
|
2410
|
+
let int = [];
|
|
2411
|
+
let dec = [];
|
|
2412
|
+
for (let j = 0; j < 20; j++) {
|
|
2413
|
+
int.push("0");
|
|
2414
|
+
dec.push("0");
|
|
2415
|
+
}
|
|
2416
|
+
const maxIndex = to % 1 === 0 ? to : Math.ceil(to);
|
|
2417
|
+
function add(integers, j, digitIndex) {
|
|
2418
|
+
const output = integers ? int : dec;
|
|
2419
|
+
const current = output[j] = output[j] || "0";
|
|
2420
|
+
const index = alphabet.indexOf(current) + digitIndex, tgetIndex = index % maxIndex, carryIndex = Math.floor(index / maxIndex);
|
|
2421
|
+
output[j] = alphabet[tgetIndex];
|
|
2422
|
+
if (carryIndex > 0) {
|
|
2423
|
+
if (integers) {
|
|
2424
|
+
add(integers, j + 1, carryIndex);
|
|
2425
|
+
} else {
|
|
2426
|
+
if (j === 0) {
|
|
2427
|
+
add(true, 0, carryIndex);
|
|
2428
|
+
} else {
|
|
2429
|
+
add(false, j - 1, carryIndex);
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
return carryIndex;
|
|
2434
|
+
}
|
|
2435
|
+
let step = 0;
|
|
2436
|
+
do {
|
|
2437
|
+
const magnitude = Math.floor(Numbers.log(value, to));
|
|
2438
|
+
const isInteger = magnitude >= 0;
|
|
2439
|
+
const absMagnitude = isInteger ? magnitude : Math.abs(1 + magnitude);
|
|
2440
|
+
add(isInteger, absMagnitude, 1);
|
|
2441
|
+
value = (value - Math.pow(to, magnitude).roundoff()).roundoff();
|
|
2442
|
+
if (!isInteger) {
|
|
2443
|
+
step++;
|
|
2444
|
+
}
|
|
2445
|
+
if (value === 0 || step >= 100) {
|
|
2446
|
+
const integer = sign + (int.reverse().map((i) => i || "0").join("").replace(/^0+/, "") || "0"), fractional = dec.map((i) => i || "0").join("").substr(0, precision).replace(/0+$/, "");
|
|
2447
|
+
if (/^0*$/.test(fractional)) {
|
|
2448
|
+
return integer;
|
|
2449
|
+
}
|
|
2450
|
+
return integer + "." + fractional;
|
|
2451
|
+
}
|
|
2452
|
+
} while (true);
|
|
2453
|
+
}
|
|
2454
|
+
};
|
|
2455
|
+
export {
|
|
2456
|
+
index_geometry_exports as Geometry,
|
|
2457
|
+
index_mathematics_exports as Mathematics
|
|
2458
|
+
};
|
|
2459
|
+
//# sourceMappingURL=pacem-numerical.mjs.map
|