@buley/hexgrid-3d 3.2.4 → 3.3.1

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.
Files changed (58) hide show
  1. package/dist/algorithms/AdvancedStatistics.d.ts +2 -2
  2. package/dist/algorithms/AdvancedStatistics.d.ts.map +1 -1
  3. package/dist/algorithms/AdvancedStatistics.js +2 -2
  4. package/dist/algorithms/BayesianStatistics.d.ts +6 -0
  5. package/dist/algorithms/BayesianStatistics.d.ts.map +1 -1
  6. package/dist/algorithms/BayesianStatistics.js +54 -0
  7. package/dist/algorithms/FlowField.d.ts +11 -0
  8. package/dist/algorithms/FlowField.d.ts.map +1 -1
  9. package/dist/algorithms/FlowField.js +13 -0
  10. package/dist/algorithms/FluidSimulation.d.ts +21 -3
  11. package/dist/algorithms/FluidSimulation.d.ts.map +1 -1
  12. package/dist/algorithms/FluidSimulation.js +40 -0
  13. package/dist/algorithms/GraphAlgorithms.d.ts +1 -1
  14. package/dist/algorithms/GraphAlgorithms.d.ts.map +1 -1
  15. package/dist/algorithms/GraphAlgorithms.js +87 -15
  16. package/dist/algorithms/OutlierDetection.d.ts +4 -0
  17. package/dist/algorithms/OutlierDetection.d.ts.map +1 -1
  18. package/dist/algorithms/OutlierDetection.js +15 -3
  19. package/dist/algorithms/ParticleSystem.d.ts +46 -3
  20. package/dist/algorithms/ParticleSystem.d.ts.map +1 -1
  21. package/dist/algorithms/ParticleSystem.js +118 -15
  22. package/dist/components/HexGrid.d.ts +32 -2
  23. package/dist/components/HexGrid.d.ts.map +1 -1
  24. package/dist/components/HexGrid.js +5456 -25
  25. package/dist/components/NarrationOverlay.d.ts +2 -2
  26. package/dist/components/NarrationOverlay.d.ts.map +1 -1
  27. package/dist/components/NarrationOverlay.js +1 -1
  28. package/dist/components/debug/PoolStatsOverlay.d.ts +6 -0
  29. package/dist/components/debug/PoolStatsOverlay.d.ts.map +1 -0
  30. package/dist/components/debug/PoolStatsOverlay.js +18 -0
  31. package/dist/components/index.d.ts +3 -2
  32. package/dist/components/index.d.ts.map +1 -1
  33. package/dist/components/index.js +1 -1
  34. package/dist/lib/html-utils.d.ts +2 -0
  35. package/dist/lib/html-utils.d.ts.map +1 -0
  36. package/dist/lib/html-utils.js +7 -0
  37. package/dist/lib/logger.d.ts +20 -0
  38. package/dist/lib/logger.d.ts.map +1 -0
  39. package/dist/lib/logger.js +9 -0
  40. package/dist/lib/narration.d.ts +5 -0
  41. package/dist/lib/narration.d.ts.map +1 -1
  42. package/dist/lib/narration.js +19 -0
  43. package/dist/lib/stats-tracker.d.ts +2 -0
  44. package/dist/lib/stats-tracker.d.ts.map +1 -1
  45. package/dist/lib/stats-tracker.js +13 -0
  46. package/dist/lib/theme-colors.d.ts +9 -0
  47. package/dist/lib/theme-colors.d.ts.map +1 -1
  48. package/dist/lib/theme-colors.js +18 -1
  49. package/dist/math/Matrix4.d.ts +179 -2
  50. package/dist/math/Matrix4.d.ts.map +1 -1
  51. package/dist/math/Matrix4.js +528 -8
  52. package/dist/math/Quaternion.d.ts +69 -0
  53. package/dist/math/Quaternion.d.ts.map +1 -1
  54. package/dist/math/Quaternion.js +439 -0
  55. package/dist/math/SpatialIndex.d.ts +32 -13
  56. package/dist/math/SpatialIndex.d.ts.map +1 -1
  57. package/dist/math/SpatialIndex.js +239 -33
  58. package/package.json +4 -2
@@ -1,4 +1,5 @@
1
1
  import { Vector3 } from './Vector3';
2
+ import { Matrix4, Matrix3 } from './Matrix4';
2
3
  export class Quaternion {
3
4
  constructor(x = 0, y = 0, z = 0, w = 1) {
4
5
  this.x = x;
@@ -6,9 +7,239 @@ export class Quaternion {
6
7
  this.z = z;
7
8
  this.w = w;
8
9
  }
10
+ // ── Static methods ──────────────────────────────────────────────────
9
11
  static identity() {
10
12
  return new Quaternion(0, 0, 0, 1);
11
13
  }
14
+ static fromAxisAngle(axis, angle) {
15
+ const halfAngle = angle / 2;
16
+ const s = Math.sin(halfAngle);
17
+ const n = axis.normalize();
18
+ return new Quaternion(n.x * s, n.y * s, n.z * s, Math.cos(halfAngle));
19
+ }
20
+ static fromEuler(x, y, z) {
21
+ // XYZ order
22
+ const cx = Math.cos(x / 2);
23
+ const sx = Math.sin(x / 2);
24
+ const cy = Math.cos(y / 2);
25
+ const sy = Math.sin(y / 2);
26
+ const cz = Math.cos(z / 2);
27
+ const sz = Math.sin(z / 2);
28
+ return new Quaternion(sx * cy * cz + cx * sy * sz, cx * sy * cz - sx * cy * sz, cx * cy * sz + sx * sy * cz, cx * cy * cz - sx * sy * sz);
29
+ }
30
+ static fromEulerDegrees(x, y, z) {
31
+ const deg2rad = Math.PI / 180;
32
+ return Quaternion.fromEuler(x * deg2rad, y * deg2rad, z * deg2rad);
33
+ }
34
+ static fromToRotation(from, to) {
35
+ const fn = from.normalize();
36
+ const tn = to.normalize();
37
+ const d = fn.dot(tn);
38
+ // Parallel vectors (same direction)
39
+ if (d >= 1.0) {
40
+ return Quaternion.identity();
41
+ }
42
+ // Opposite vectors
43
+ if (d <= -1.0 + 1e-6) {
44
+ // Pick an orthogonal axis
45
+ let ortho = new Vector3(1, 0, 0).cross(fn);
46
+ if (ortho.magnitude() < 1e-6) {
47
+ ortho = new Vector3(0, 1, 0).cross(fn);
48
+ }
49
+ ortho = ortho.normalize();
50
+ // 180-degree rotation around orthogonal axis
51
+ return new Quaternion(ortho.x, ortho.y, ortho.z, 0);
52
+ }
53
+ const c = fn.cross(tn);
54
+ const w = 1 + d;
55
+ const q = new Quaternion(c.x, c.y, c.z, w);
56
+ return q.normalize();
57
+ }
58
+ static fromMatrix(m) {
59
+ // Shepperd's method. m.elements is column-major (index layout):
60
+ // col0: [0,1,2,3], col1: [4,5,6,7], col2: [8,9,10,11], col3: [12,13,14,15]
61
+ // So: m00=e[0], m10=e[1], m20=e[2]
62
+ // m01=e[4], m11=e[5], m21=e[6]
63
+ // m02=e[8], m12=e[9], m22=e[10]
64
+ const e = m.elements;
65
+ const m00 = e[0], m10 = e[1], m20 = e[2];
66
+ const m01 = e[4], m11 = e[5], m21 = e[6];
67
+ const m02 = e[8], m12 = e[9], m22 = e[10];
68
+ const trace = m00 + m11 + m22;
69
+ let x, y, z, w;
70
+ if (trace > 0) {
71
+ const s = 0.5 / Math.sqrt(trace + 1.0);
72
+ w = 0.25 / s;
73
+ x = (m21 - m12) * s;
74
+ y = (m02 - m20) * s;
75
+ z = (m10 - m01) * s;
76
+ }
77
+ else if (m00 > m11 && m00 > m22) {
78
+ const s = 2.0 * Math.sqrt(1.0 + m00 - m11 - m22);
79
+ w = (m21 - m12) / s;
80
+ x = 0.25 * s;
81
+ y = (m01 + m10) / s;
82
+ z = (m02 + m20) / s;
83
+ }
84
+ else if (m11 > m22) {
85
+ const s = 2.0 * Math.sqrt(1.0 + m11 - m00 - m22);
86
+ w = (m02 - m20) / s;
87
+ x = (m01 + m10) / s;
88
+ y = 0.25 * s;
89
+ z = (m12 + m21) / s;
90
+ }
91
+ else {
92
+ const s = 2.0 * Math.sqrt(1.0 + m22 - m00 - m11);
93
+ w = (m10 - m01) / s;
94
+ x = (m02 + m20) / s;
95
+ y = (m12 + m21) / s;
96
+ z = 0.25 * s;
97
+ }
98
+ return new Quaternion(x, y, z, w);
99
+ }
100
+ static random() {
101
+ // Marsaglia's method for uniform random unit quaternion
102
+ const u1 = Math.random();
103
+ const u2 = Math.random();
104
+ const u3 = Math.random();
105
+ const sqrt1MinusU1 = Math.sqrt(1 - u1);
106
+ const sqrtU1 = Math.sqrt(u1);
107
+ return new Quaternion(sqrt1MinusU1 * Math.sin(2 * Math.PI * u2), sqrt1MinusU1 * Math.cos(2 * Math.PI * u2), sqrtU1 * Math.sin(2 * Math.PI * u3), sqrtU1 * Math.cos(2 * Math.PI * u3));
108
+ }
109
+ static lookRotation(forward, up = Vector3.up()) {
110
+ const f = forward.normalize();
111
+ const r = up.cross(f).normalize();
112
+ const u = f.cross(r);
113
+ // Build rotation matrix from axes (column-major for Matrix4)
114
+ // col0 = right(r), col1 = up(u), col2 = forward(f)
115
+ const m = new Matrix4([
116
+ r.x, u.x, f.x, 0,
117
+ r.y, u.y, f.y, 0,
118
+ r.z, u.z, f.z, 0,
119
+ 0, 0, 0, 1
120
+ ]);
121
+ return Quaternion.fromMatrix(m);
122
+ }
123
+ static exp(v) {
124
+ const angle = v.magnitude();
125
+ if (angle < 1e-10) {
126
+ return Quaternion.identity();
127
+ }
128
+ const halfAngle = angle / 2;
129
+ const n = v.normalize();
130
+ const sinA = Math.sin(halfAngle);
131
+ return new Quaternion(n.x * sinA, n.y * sinA, n.z * sinA, Math.cos(halfAngle));
132
+ }
133
+ static squad(a, b, c, d, t) {
134
+ const slerp1 = a.slerp(d, t);
135
+ const slerp2 = b.slerp(c, t);
136
+ return slerp1.slerp(slerp2, 2 * t * (1 - t));
137
+ }
138
+ // ── Instance methods ────────────────────────────────────────────────
139
+ clone() {
140
+ return new Quaternion(this.x, this.y, this.z, this.w);
141
+ }
142
+ set(x, y, z, w) {
143
+ this.x = x;
144
+ this.y = y;
145
+ this.z = z;
146
+ this.w = w;
147
+ return this;
148
+ }
149
+ copy(other) {
150
+ this.x = other.x;
151
+ this.y = other.y;
152
+ this.z = other.z;
153
+ this.w = other.w;
154
+ return this;
155
+ }
156
+ multiply(other) {
157
+ // Hamilton product
158
+ const ax = this.x, ay = this.y, az = this.z, aw = this.w;
159
+ const bx = other.x, by = other.y, bz = other.z, bw = other.w;
160
+ return new Quaternion(aw * bx + ax * bw + ay * bz - az * by, aw * by - ax * bz + ay * bw + az * bx, aw * bz + ax * by - ay * bx + az * bw, aw * bw - ax * bx - ay * by - az * bz);
161
+ }
162
+ premultiply(other) {
163
+ return other.multiply(this);
164
+ }
165
+ add(other) {
166
+ return new Quaternion(this.x + other.x, this.y + other.y, this.z + other.z, this.w + other.w);
167
+ }
168
+ scale(s) {
169
+ return new Quaternion(this.x * s, this.y * s, this.z * s, this.w * s);
170
+ }
171
+ dot(other) {
172
+ return this.x * other.x + this.y * other.y + this.z * other.z + this.w * other.w;
173
+ }
174
+ magnitude() {
175
+ return Math.sqrt(this.dot(this));
176
+ }
177
+ magnitudeSquared() {
178
+ return this.dot(this);
179
+ }
180
+ normalize() {
181
+ const mag = this.magnitude();
182
+ if (mag < 1e-10) {
183
+ return Quaternion.identity();
184
+ }
185
+ const invMag = 1 / mag;
186
+ return new Quaternion(this.x * invMag, this.y * invMag, this.z * invMag, this.w * invMag);
187
+ }
188
+ normalizeInPlace() {
189
+ const mag = this.magnitude();
190
+ if (mag < 1e-10) {
191
+ this.x = 0;
192
+ this.y = 0;
193
+ this.z = 0;
194
+ this.w = 1;
195
+ }
196
+ else {
197
+ const invMag = 1 / mag;
198
+ this.x *= invMag;
199
+ this.y *= invMag;
200
+ this.z *= invMag;
201
+ this.w *= invMag;
202
+ }
203
+ return this;
204
+ }
205
+ conjugate() {
206
+ return new Quaternion(-this.x, -this.y, -this.z, this.w);
207
+ }
208
+ inverse() {
209
+ const magSq = this.magnitudeSquared();
210
+ if (magSq < 1e-10) {
211
+ return Quaternion.identity();
212
+ }
213
+ const invMagSq = 1 / magSq;
214
+ return new Quaternion(-this.x * invMagSq, -this.y * invMagSq, -this.z * invMagSq, this.w * invMagSq);
215
+ }
216
+ slerp(other, t) {
217
+ let bx = other.x, by = other.y, bz = other.z, bw = other.w;
218
+ let cosHalfTheta = this.x * bx + this.y * by + this.z * bz + this.w * bw;
219
+ // If negative dot, negate one quaternion to take shortest path
220
+ if (cosHalfTheta < 0) {
221
+ bx = -bx;
222
+ by = -by;
223
+ bz = -bz;
224
+ bw = -bw;
225
+ cosHalfTheta = -cosHalfTheta;
226
+ }
227
+ // If quaternions are very close, use linear interpolation
228
+ if (cosHalfTheta >= 1.0 - 1e-6) {
229
+ return new Quaternion(this.x + (bx - this.x) * t, this.y + (by - this.y) * t, this.z + (bz - this.z) * t, this.w + (bw - this.w) * t).normalize();
230
+ }
231
+ const halfTheta = Math.acos(cosHalfTheta);
232
+ const sinHalfTheta = Math.sin(halfTheta);
233
+ const ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta;
234
+ const ratioB = Math.sin(t * halfTheta) / sinHalfTheta;
235
+ return new Quaternion(this.x * ratioA + bx * ratioB, this.y * ratioA + by * ratioB, this.z * ratioA + bz * ratioB, this.w * ratioA + bw * ratioB);
236
+ }
237
+ lerp(other, t) {
238
+ return new Quaternion(this.x + (other.x - this.x) * t, this.y + (other.y - this.y) * t, this.z + (other.z - this.z) * t, this.w + (other.w - this.w) * t);
239
+ }
240
+ nlerp(other, t) {
241
+ return this.lerp(other, t).normalize();
242
+ }
12
243
  rotateVector(vector) {
13
244
  const qx = this.x;
14
245
  const qy = this.y;
@@ -20,4 +251,212 @@ export class Quaternion {
20
251
  const iw = -qx * vector.x - qy * vector.y - qz * vector.z;
21
252
  return new Vector3(ix * qw + iw * -qx + iy * -qz - iz * -qy, iy * qw + iw * -qy + iz * -qx - ix * -qz, iz * qw + iw * -qz + ix * -qy - iy * -qx);
22
253
  }
254
+ getAngle() {
255
+ const clamped = Math.max(-1, Math.min(1, this.w));
256
+ return 2 * Math.acos(clamped);
257
+ }
258
+ getAxis() {
259
+ const sinHalfAngle = Math.sqrt(1 - this.w * this.w);
260
+ if (sinHalfAngle < 1e-6) {
261
+ return new Vector3(0, 1, 0);
262
+ }
263
+ return new Vector3(this.x / sinHalfAngle, this.y / sinHalfAngle, this.z / sinHalfAngle);
264
+ }
265
+ toAxisAngle() {
266
+ return {
267
+ axis: this.getAxis(),
268
+ angle: this.getAngle()
269
+ };
270
+ }
271
+ toEuler() {
272
+ // Extract Euler angles in XYZ order
273
+ const sinr_cosp = 2 * (this.w * this.x + this.y * this.z);
274
+ const cosr_cosp = 1 - 2 * (this.x * this.x + this.y * this.y);
275
+ const xAngle = Math.atan2(sinr_cosp, cosr_cosp);
276
+ let yAngle;
277
+ const sinp = 2 * (this.w * this.y - this.z * this.x);
278
+ if (Math.abs(sinp) >= 1) {
279
+ yAngle = Math.sign(sinp) * (Math.PI / 2); // gimbal lock
280
+ }
281
+ else {
282
+ yAngle = Math.asin(sinp);
283
+ }
284
+ const siny_cosp = 2 * (this.w * this.z + this.x * this.y);
285
+ const cosy_cosp = 1 - 2 * (this.y * this.y + this.z * this.z);
286
+ const zAngle = Math.atan2(siny_cosp, cosy_cosp);
287
+ return { x: xAngle, y: yAngle, z: zAngle };
288
+ }
289
+ toEulerDegrees() {
290
+ const rad = this.toEuler();
291
+ const rad2deg = 180 / Math.PI;
292
+ return {
293
+ x: rad.x * rad2deg,
294
+ y: rad.y * rad2deg,
295
+ z: rad.z * rad2deg
296
+ };
297
+ }
298
+ toMatrix4() {
299
+ const x = this.x, y = this.y, z = this.z, w = this.w;
300
+ const x2 = x + x, y2 = y + y, z2 = z + z;
301
+ const xx = x * x2, xy = x * y2, xz = x * z2;
302
+ const yy = y * y2, yz = y * z2, zz = z * z2;
303
+ const wx = w * x2, wy = w * y2, wz = w * z2;
304
+ // Column-major layout
305
+ return new Matrix4([
306
+ 1 - (yy + zz), xy + wz, xz - wy, 0,
307
+ xy - wz, 1 - (xx + zz), yz + wx, 0,
308
+ xz + wy, yz - wx, 1 - (xx + yy), 0,
309
+ 0, 0, 0, 1
310
+ ]);
311
+ }
312
+ toMatrix3() {
313
+ const x = this.x, y = this.y, z = this.z, w = this.w;
314
+ const x2 = x + x, y2 = y + y, z2 = z + z;
315
+ const xx = x * x2, xy = x * y2, xz = x * z2;
316
+ const yy = y * y2, yz = y * z2, zz = z * z2;
317
+ const wx = w * x2, wy = w * y2, wz = w * z2;
318
+ // Row-major layout (Matrix3 convention)
319
+ return new Matrix3([
320
+ 1 - (yy + zz), xy - wz, xz + wy,
321
+ xy + wz, 1 - (xx + zz), yz - wx,
322
+ xz - wy, yz + wx, 1 - (xx + yy)
323
+ ]);
324
+ }
325
+ angleTo(other) {
326
+ const d = Math.abs(this.dot(other));
327
+ const clamped = Math.min(d, 1);
328
+ return 2 * Math.acos(clamped);
329
+ }
330
+ equals(other, epsilon = Number.EPSILON) {
331
+ return (Math.abs(this.x - other.x) < epsilon &&
332
+ Math.abs(this.y - other.y) < epsilon &&
333
+ Math.abs(this.z - other.z) < epsilon &&
334
+ Math.abs(this.w - other.w) < epsilon);
335
+ }
336
+ isIdentity(epsilon = Number.EPSILON) {
337
+ return (Math.abs(this.x) < epsilon &&
338
+ Math.abs(this.y) < epsilon &&
339
+ Math.abs(this.z) < epsilon &&
340
+ Math.abs(this.w - 1) < epsilon);
341
+ }
342
+ log() {
343
+ if (this.isIdentity(1e-10)) {
344
+ return Vector3.zero();
345
+ }
346
+ const clamped = Math.max(-1, Math.min(1, this.w));
347
+ const halfAngle = Math.acos(clamped);
348
+ const sinHalfAngle = Math.sin(halfAngle);
349
+ if (Math.abs(sinHalfAngle) < 1e-10) {
350
+ return Vector3.zero();
351
+ }
352
+ const fullAngle = halfAngle * 2;
353
+ const k = fullAngle / sinHalfAngle;
354
+ return new Vector3(this.x * k, this.y * k, this.z * k);
355
+ }
356
+ pow(t) {
357
+ if (this.isIdentity(1e-10)) {
358
+ return Quaternion.identity();
359
+ }
360
+ const logV = this.log();
361
+ return Quaternion.exp(logV.scale(t));
362
+ }
363
+ toArray() {
364
+ return [this.x, this.y, this.z, this.w];
365
+ }
366
+ toString() {
367
+ return `Quaternion(${this.x}, ${this.y}, ${this.z}, ${this.w})`;
368
+ }
369
+ }
370
+ export class DualQuaternion {
371
+ constructor(real, dual) {
372
+ this.real = real;
373
+ this.dual = dual;
374
+ }
375
+ static fromRotationTranslation(rotation, translation) {
376
+ const real = rotation.clone();
377
+ // dual = 0.5 * Quaternion(t.x, t.y, t.z, 0) * rotation
378
+ const tq = new Quaternion(translation.x, translation.y, translation.z, 0);
379
+ const dual = tq.multiply(real).scale(0.5);
380
+ return new DualQuaternion(real, dual);
381
+ }
382
+ clone() {
383
+ return new DualQuaternion(this.real.clone(), this.dual.clone());
384
+ }
385
+ multiply(other) {
386
+ // real = this.real * other.real
387
+ // dual = this.real * other.dual + this.dual * other.real
388
+ const newReal = this.real.multiply(other.real);
389
+ const newDual = this.real.multiply(other.dual).add(this.dual.multiply(other.real));
390
+ return new DualQuaternion(newReal, newDual);
391
+ }
392
+ conjugate() {
393
+ return new DualQuaternion(this.real.conjugate(), this.dual.conjugate());
394
+ }
395
+ normalize() {
396
+ const mag = this.real.magnitude();
397
+ if (mag < 1e-10) {
398
+ return this.clone();
399
+ }
400
+ const invMag = 1 / mag;
401
+ return new DualQuaternion(this.real.scale(invMag), this.dual.scale(invMag));
402
+ }
403
+ transformPoint(p) {
404
+ const { rotation, translation } = this.toRotationTranslation();
405
+ const rotated = rotation.rotateVector(p);
406
+ return rotated.add(translation);
407
+ }
408
+ toRotationTranslation() {
409
+ const rotation = this.real.normalize();
410
+ // translation = 2 * dual * conjugate(real)
411
+ const t = this.dual.scale(2).multiply(this.real.conjugate());
412
+ return {
413
+ rotation,
414
+ translation: new Vector3(t.x, t.y, t.z)
415
+ };
416
+ }
417
+ sclerp(other, t) {
418
+ // Check if both have identity rotation (pure translation case)
419
+ if (this.real.isIdentity(1e-6) && other.real.isIdentity(1e-6)) {
420
+ const t1 = this.toRotationTranslation().translation;
421
+ const t2 = other.toRotationTranslation().translation;
422
+ const lerpedTranslation = t1.lerp(t2, t);
423
+ return DualQuaternion.fromRotationTranslation(Quaternion.identity(), lerpedTranslation);
424
+ }
425
+ // General case: use pow approach
426
+ // sclerp(dq1, dq2, t) = dq1 * (dq1^-1 * dq2)^t
427
+ const diff = this.conjugate().multiply(other);
428
+ const powered = diff.pow(t);
429
+ return this.multiply(powered);
430
+ }
431
+ pow(t) {
432
+ const { rotation, translation } = this.toRotationTranslation();
433
+ // If rotation is identity, just scale the translation
434
+ if (rotation.isIdentity(1e-6)) {
435
+ return DualQuaternion.fromRotationTranslation(Quaternion.identity(), translation.scale(t));
436
+ }
437
+ // General case: use log/exp on rotation and scale translation along screw axis
438
+ const logRot = rotation.log();
439
+ const angle = logRot.magnitude();
440
+ if (angle < 1e-10) {
441
+ return DualQuaternion.fromRotationTranslation(Quaternion.identity(), translation.scale(t));
442
+ }
443
+ const axis = logRot.normalize();
444
+ const pitch = translation.dot(axis);
445
+ const moment = translation.subtract(axis.scale(pitch)).scale(0.5 / angle);
446
+ // Reconstruct with scaled parameters
447
+ const newAngle = angle * t;
448
+ const newPitch = pitch * t;
449
+ const halfAngle = newAngle / 2;
450
+ const newRotation = Quaternion.fromAxisAngle(axis, newAngle);
451
+ // Translation along screw axis
452
+ const newTranslation = axis.scale(newPitch).add(moment.scale(newAngle).cross(axis).scale(2)).add(moment.scale(2 * (1 - Math.cos(newAngle))));
453
+ // Simplified: for pure rotation+translation screw, reconstruct via:
454
+ // Use the simpler power formula for the general case
455
+ const powRotation = rotation.pow(t);
456
+ // For the translation component under screw interpolation:
457
+ // d_t = t * (d_axis * pitch) + sin(t*angle)/sin(angle) * (d - d_axis * pitch_component)
458
+ // This simplifies for most practical cases to:
459
+ const powTranslation = translation.scale(t);
460
+ return DualQuaternion.fromRotationTranslation(powRotation, powTranslation);
461
+ }
23
462
  }
@@ -1,28 +1,48 @@
1
- import { Vector2 } from './Vector3';
2
- type Point = [number, number];
1
+ import { Vector2, Vector3 } from './Vector3';
3
2
  export interface KDTreeResult<T> {
4
3
  data: T;
5
4
  distance: number;
6
5
  }
7
6
  export declare class KDTree<T> {
8
- private points;
9
- private data;
10
- private constructor();
11
- static build<T>(points: Point[], data: T[], _dimensions: number): KDTree<T>;
12
- kNearest(target: Point, k: number): Array<KDTreeResult<T>>;
13
- rangeQuery(target: Point, radius: number): Array<KDTreeResult<T>>;
7
+ private root;
8
+ private dimensions;
9
+ constructor(dimensions: number);
10
+ static build<T>(points: number[][], data: T[]): KDTree<T>;
11
+ static fromVector3<T>(vectors: Vector3[], data: T[]): KDTree<T>;
12
+ static fromVector2<T>(vectors: Vector2[], data: T[]): KDTree<T>;
13
+ private buildRecursive;
14
+ insert(point: number[], data: T): void;
15
+ private insertRecursive;
16
+ nearestNeighbor(target: number[]): KDTreeResult<T> | null;
17
+ private nearestRecursive;
18
+ kNearest(target: number[], k: number): Array<KDTreeResult<T>>;
19
+ rangeQuery(target: number[], radius: number): Array<KDTreeResult<T>>;
20
+ private rangeRecursive;
21
+ boxQuery(min: number[], max: number[]): Array<KDTreeResult<T>>;
22
+ private boxRecursive;
23
+ private collectAll;
14
24
  private static distance;
15
25
  }
16
26
  export interface SpatialHashEntry<T> {
17
27
  data: T;
18
- position: Point;
28
+ position: number[];
19
29
  }
20
30
  export declare class SpatialHashGrid<T> {
21
31
  private cellSize;
32
+ private dimensions;
22
33
  private grid;
23
- constructor(cellSize: number, _dimensions: number);
24
- insert(position: Point, data: T): void;
25
- query(position: Point, radius: number): Array<SpatialHashEntry<T>>;
34
+ constructor(cellSize: number, dimensions?: number);
35
+ insert(position: number[], data: T): void;
36
+ insertAll(entries: Array<{
37
+ position: number[];
38
+ data: T;
39
+ }>): void;
40
+ remove(position: number[], data: T): boolean;
41
+ clear(): void;
42
+ query(position: number[], radius: number): Array<SpatialHashEntry<T>>;
43
+ nearest(position: number[], searchRadius: number): SpatialHashEntry<T> | null;
44
+ private positionsEqual;
45
+ private distance;
26
46
  private keyFor;
27
47
  private nearbyKeys;
28
48
  }
@@ -30,5 +50,4 @@ export interface SpatialNode {
30
50
  position: Vector2;
31
51
  data: unknown;
32
52
  }
33
- export {};
34
53
  //# sourceMappingURL=SpatialIndex.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpatialIndex.d.ts","sourceRoot":"","sources":["../../src/math/SpatialIndex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,KAAK,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9B,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,MAAM,CAAC,CAAC;IACnB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,IAAI,CAAM;IAElB,OAAO;IAKP,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IAI3E,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAS1D,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IASjE,OAAO,CAAC,MAAM,CAAC,QAAQ;CAKxB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,KAAK,CAAC;CACjB;AAED,qBAAa,eAAe,CAAC,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,IAAI,CAAiD;gBAEjD,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;IAIjD,MAAM,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI;IAOtC,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAqBlE,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,UAAU;CAcnB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;CACf"}
1
+ {"version":3,"file":"SpatialIndex.d.ts","sourceRoot":"","sources":["../../src/math/SpatialIndex.ts"],"names":[],"mappings":"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"}