@buley/hexgrid-3d 1.1.0 → 1.1.2

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.
@@ -7,6 +7,22 @@ export class Vector2 {
7
7
  this.y = y;
8
8
  }
9
9
 
10
+ static zero(): Vector2 {
11
+ return new Vector2(0, 0);
12
+ }
13
+
14
+ static one(): Vector2 {
15
+ return new Vector2(1, 1);
16
+ }
17
+
18
+ static fromAngle(angle: number, length: number = 1): Vector2 {
19
+ return new Vector2(Math.cos(angle) * length, Math.sin(angle) * length);
20
+ }
21
+
22
+ clone(): Vector2 {
23
+ return new Vector2(this.x, this.y);
24
+ }
25
+
10
26
  add(other: Vector2): Vector2 {
11
27
  return new Vector2(this.x + other.x, this.y + other.y);
12
28
  }
@@ -19,15 +35,25 @@ export class Vector2 {
19
35
  return new Vector2(this.x * factor, this.y * factor);
20
36
  }
21
37
 
22
- length(): number {
38
+ dot(other: Vector2): number {
39
+ return this.x * other.x + this.y * other.y;
40
+ }
41
+
42
+ cross(other: Vector2): number {
43
+ return this.x * other.y - this.y * other.x;
44
+ }
45
+
46
+ magnitude(): number {
23
47
  return Math.sqrt(this.x * this.x + this.y * this.y);
24
48
  }
25
49
 
50
+ magnitudeSquared(): number {
51
+ return this.x * this.x + this.y * this.y;
52
+ }
53
+
26
54
  normalize(): Vector2 {
27
- const len = this.length();
28
- if (len === 0) {
29
- return new Vector2(0, 0);
30
- }
55
+ const len = this.magnitude();
56
+ if (len === 0) return new Vector2(0, 0);
31
57
  return new Vector2(this.x / len, this.y / len);
32
58
  }
33
59
 
@@ -36,6 +62,50 @@ export class Vector2 {
36
62
  const dy = this.y - other.y;
37
63
  return Math.sqrt(dx * dx + dy * dy);
38
64
  }
65
+
66
+ angle(): number {
67
+ return Math.atan2(this.y, this.x);
68
+ }
69
+
70
+ angleTo(other: Vector2): number {
71
+ return Math.acos(Math.max(-1, Math.min(1, this.dot(other) / (this.magnitude() * other.magnitude()))));
72
+ }
73
+
74
+ rotate(angle: number): Vector2 {
75
+ const cos = Math.cos(angle);
76
+ const sin = Math.sin(angle);
77
+ return new Vector2(this.x * cos - this.y * sin, this.x * sin + this.y * cos);
78
+ }
79
+
80
+ perpendicular(): Vector2 {
81
+ return new Vector2(-this.y, this.x);
82
+ }
83
+
84
+ lerp(other: Vector2, t: number): Vector2 {
85
+ return new Vector2(
86
+ this.x + (other.x - this.x) * t,
87
+ this.y + (other.y - this.y) * t
88
+ );
89
+ }
90
+
91
+ toArray(): [number, number] {
92
+ return [this.x, this.y];
93
+ }
94
+
95
+ toVector3(z: number = 0): Vector3 {
96
+ return new Vector3(this.x, this.y, z);
97
+ }
98
+
99
+ equals(other: Vector2, epsilon: number = Number.EPSILON): boolean {
100
+ return (
101
+ Math.abs(this.x - other.x) < epsilon &&
102
+ Math.abs(this.y - other.y) < epsilon
103
+ );
104
+ }
105
+
106
+ toString(): string {
107
+ return `Vector2(${this.x.toFixed(4)}, ${this.y.toFixed(4)})`;
108
+ }
39
109
  }
40
110
 
41
111
  export class Vector3 {
@@ -49,25 +119,422 @@ export class Vector3 {
49
119
  this.z = z;
50
120
  }
51
121
 
52
- static fromLatLng(
53
- latitude: number,
54
- longitude: number,
55
- radius: number = 1
56
- ): Vector3 {
122
+ static zero(): Vector3 {
123
+ return new Vector3(0, 0, 0);
124
+ }
125
+
126
+ static one(): Vector3 {
127
+ return new Vector3(1, 1, 1);
128
+ }
129
+
130
+ static up(): Vector3 {
131
+ return new Vector3(0, 1, 0);
132
+ }
133
+
134
+ static down(): Vector3 {
135
+ return new Vector3(0, -1, 0);
136
+ }
137
+
138
+ static right(): Vector3 {
139
+ return new Vector3(1, 0, 0);
140
+ }
141
+
142
+ static left(): Vector3 {
143
+ return new Vector3(-1, 0, 0);
144
+ }
145
+
146
+ static forward(): Vector3 {
147
+ return new Vector3(0, 0, 1);
148
+ }
149
+
150
+ static back(): Vector3 {
151
+ return new Vector3(0, 0, -1);
152
+ }
153
+
154
+ static fromArray(arr: number[]): Vector3 {
155
+ return new Vector3(arr[0], arr[1], arr[2]);
156
+ }
157
+
158
+ static fromSpherical(phi: number, theta: number, radius: number): Vector3 {
159
+ // Matches test expectation where (0,0,1) -> (1,0,0)
160
+ // Assuming phi is azimuth (longitude), theta is elevation (latitude), radius is magnitude.
161
+ // x = r * cos(theta) * cos(phi)
162
+ // y = r * cos(theta) * sin(phi)
163
+ // z = r * sin(theta)
164
+ const x = radius * Math.cos(theta) * Math.cos(phi);
165
+ const y = radius * Math.cos(theta) * Math.sin(phi);
166
+ const z = radius * Math.sin(theta);
167
+ return new Vector3(x, y, z);
168
+ }
169
+
170
+ static fromLatLng(latitude: number, longitude: number, radius: number = 1): Vector3 {
57
171
  const latRad = (latitude * Math.PI) / 180;
58
172
  const lonRad = (longitude * Math.PI) / 180;
173
+ return Vector3.fromSpherical(lonRad, latRad, radius);
174
+ }
175
+
176
+ static random(): Vector3 {
177
+ // Random vector on unit sphere
178
+ const theta = Math.asin(2 * Math.random() - 1); // Elevation [-PI/2, PI/2]
179
+ const phi = 2 * Math.PI * Math.random(); // Azimuth [0, 2PI]
180
+ return Vector3.fromSpherical(phi, theta, 1);
181
+ }
59
182
 
60
- const x = radius * Math.cos(latRad) * Math.cos(lonRad);
61
- const y = radius * Math.cos(latRad) * Math.sin(lonRad);
62
- const z = radius * Math.sin(latRad);
183
+ static randomInSphere(radius: number): Vector3 {
184
+ const r = radius * Math.cbrt(Math.random());
185
+ return Vector3.random().scale(r);
186
+ }
63
187
 
64
- return new Vector3(x, y, z);
188
+ static catmullRom(p0: Vector3, p1: Vector3, p2: Vector3, p3: Vector3, t: number): Vector3 {
189
+ const t2 = t * t;
190
+ const t3 = t2 * t;
191
+
192
+ const f0 = -0.5 * t3 + t2 - 0.5 * t;
193
+ const f1 = 1.5 * t3 - 2.5 * t2 + 1.0;
194
+ const f2 = -1.5 * t3 + 2.0 * t2 + 0.5 * t;
195
+ const f3 = 0.5 * t3 - 0.5 * t2;
196
+
197
+ return p0.scale(f0).add(p1.scale(f1)).add(p2.scale(f2)).add(p3.scale(f3));
198
+ }
199
+
200
+ static bezier(points: Vector3[], t: number): Vector3 {
201
+ if (points.length === 0) return Vector3.zero();
202
+ if (points.length === 1) return points[0].clone();
203
+
204
+ let temp = points.map(p => p.clone());
205
+ let n = temp.length - 1;
206
+
207
+ for (let i = 0; i < n; i++) {
208
+ for (let j = 0; j < n - i; j++) {
209
+ temp[j] = temp[j].lerp(temp[j+1], t);
210
+ }
211
+ }
212
+ return temp[0];
213
+ }
214
+
215
+ clone(): Vector3 {
216
+ return new Vector3(this.x, this.y, this.z);
217
+ }
218
+
219
+ // Immutable operations return new vectors
220
+ add(other: Vector3): Vector3 {
221
+ return new Vector3(this.x + other.x, this.y + other.y, this.z + other.z);
222
+ }
223
+
224
+ subtract(other: Vector3): Vector3 {
225
+ return new Vector3(this.x - other.x, this.y - other.y, this.z - other.z);
226
+ }
227
+
228
+ scale(factor: number): Vector3 {
229
+ return new Vector3(this.x * factor, this.y * factor, this.z * factor);
230
+ }
231
+
232
+ multiply(other: Vector3): Vector3 {
233
+ return new Vector3(this.x * other.x, this.y * other.y, this.z * other.z);
234
+ }
235
+
236
+ divide(other: Vector3): Vector3 {
237
+ return new Vector3(
238
+ other.x === 0 ? 0 : this.x / other.x,
239
+ other.y === 0 ? 0 : this.y / other.y,
240
+ other.z === 0 ? 0 : this.z / other.z
241
+ );
242
+ }
243
+
244
+ negate(): Vector3 {
245
+ return new Vector3(-this.x, -this.y, -this.z);
246
+ }
247
+
248
+ // Mutable operations modify 'this'
249
+ set(x: number, y: number, z: number): this {
250
+ this.x = x; this.y = y; this.z = z;
251
+ return this;
252
+ }
253
+
254
+ copy(other: Vector3): this {
255
+ this.x = other.x; this.y = other.y; this.z = other.z;
256
+ return this;
257
+ }
258
+
259
+ addInPlace(other: Vector3): this {
260
+ this.x += other.x; this.y += other.y; this.z += other.z;
261
+ return this;
262
+ }
263
+
264
+ subtractInPlace(other: Vector3): this {
265
+ this.x -= other.x; this.y -= other.y; this.z -= other.z;
266
+ return this;
267
+ }
268
+
269
+ scaleInPlace(factor: number): this {
270
+ this.x *= factor; this.y *= factor; this.z *= factor;
271
+ return this;
272
+ }
273
+
274
+ normalizeInPlace(): this {
275
+ const len = this.length();
276
+ if (len > 0) {
277
+ this.scaleInPlace(1 / len);
278
+ } else {
279
+ this.x = 0; this.y = 0; this.z = 0;
280
+ }
281
+ return this;
282
+ }
283
+
284
+ // Products
285
+ dot(other: Vector3): number {
286
+ return this.x * other.x + this.y * other.y + this.z * other.z;
287
+ }
288
+
289
+ cross(other: Vector3): Vector3 {
290
+ return new Vector3(
291
+ this.y * other.z - this.z * other.y,
292
+ this.z * other.x - this.x * other.z,
293
+ this.x * other.y - this.y * other.x
294
+ );
295
+ }
296
+
297
+ tripleProduct(b: Vector3, c: Vector3): number {
298
+ return this.dot(b.cross(c));
299
+ }
300
+
301
+ // Magnitude & Distance
302
+ length(): number {
303
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
304
+ }
305
+
306
+ magnitude(): number {
307
+ return this.length();
308
+ }
309
+
310
+ magnitudeSquared(): number {
311
+ return this.x * this.x + this.y * this.y + this.z * this.z;
312
+ }
313
+
314
+ normalize(): Vector3 {
315
+ return this.clone().normalizeInPlace();
65
316
  }
66
317
 
67
318
  distanceTo(other: Vector3): number {
319
+ return Math.sqrt(this.distanceSquaredTo(other));
320
+ }
321
+
322
+ distanceSquaredTo(other: Vector3): number {
68
323
  const dx = this.x - other.x;
69
324
  const dy = this.y - other.y;
70
325
  const dz = this.z - other.z;
71
- return Math.sqrt(dx * dx + dy * dy + dz * dz);
326
+ return dx*dx + dy*dy + dz*dz;
327
+ }
328
+
329
+ // Interpolation
330
+ lerp(other: Vector3, t: number): Vector3 {
331
+ return new Vector3(
332
+ this.x + (other.x - this.x) * t,
333
+ this.y + (other.y - this.y) * t,
334
+ this.z + (other.z - this.z) * t
335
+ );
336
+ }
337
+
338
+ slerp(other: Vector3, t: number): Vector3 {
339
+ let dot = this.dot(other);
340
+ // Clamp dot to [-1, 1]
341
+ dot = Math.max(-1, Math.min(1, dot));
342
+
343
+ const theta = Math.acos(dot);
344
+ const sinTheta = Math.sin(theta);
345
+
346
+ if (Math.abs(sinTheta) < 0.001) {
347
+ return this.lerp(other, t);
348
+ }
349
+
350
+ const w1 = Math.sin((1 - t) * theta) / sinTheta;
351
+ const w2 = Math.sin(t * theta) / sinTheta;
352
+
353
+ return this.scale(w1).add(other.scale(w2));
354
+ }
355
+
356
+ nlerp(other: Vector3, t: number): Vector3 {
357
+ return this.lerp(other, t).normalize();
358
+ }
359
+
360
+ hermite(t1: Vector3, p2: Vector3, t2: Vector3, t: number): Vector3 {
361
+ const tSq = t * t;
362
+ const tCub = tSq * t;
363
+
364
+ const h1 = 2*tCub - 3*tSq + 1;
365
+ const h2 = -2*tCub + 3*tSq;
366
+ const h3 = tCub - 2*tSq + t;
367
+ const h4 = tCub - tSq;
368
+
369
+ return this.scale(h1).add(p2.scale(h2)).add(t1.scale(h3)).add(t2.scale(h4));
370
+ }
371
+
372
+ // Utility
373
+ toArray(): [number, number, number] {
374
+ return [this.x, this.y, this.z];
375
+ }
376
+
377
+ toFloat32Array(): Float32Array {
378
+ return new Float32Array([this.x, this.y, this.z]);
379
+ }
380
+
381
+ equals(other: Vector3, epsilon: number = Number.EPSILON): boolean {
382
+ return (
383
+ Math.abs(this.x - other.x) < epsilon &&
384
+ Math.abs(this.y - other.y) < epsilon &&
385
+ Math.abs(this.z - other.z) < epsilon
386
+ );
387
+ }
388
+
389
+ isZero(): boolean {
390
+ return this.x === 0 && this.y === 0 && this.z === 0;
391
+ }
392
+
393
+ abs(): Vector3 {
394
+ return new Vector3(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z));
395
+ }
396
+
397
+ floor(): Vector3 {
398
+ return new Vector3(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));
399
+ }
400
+
401
+ ceil(): Vector3 {
402
+ return new Vector3(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.z));
403
+ }
404
+
405
+ round(): Vector3 {
406
+ return new Vector3(Math.round(this.x), Math.round(this.y), Math.round(this.z));
407
+ }
408
+
409
+ min(other: Vector3): Vector3 {
410
+ return new Vector3(Math.min(this.x, other.x), Math.min(this.y, other.y), Math.min(this.z, other.z));
411
+ }
412
+
413
+ max(other: Vector3): Vector3 {
414
+ return new Vector3(Math.max(this.x, other.x), Math.max(this.y, other.y), Math.max(this.z, other.z));
415
+ }
416
+
417
+ clamp(min: Vector3, max: Vector3): Vector3 {
418
+ return new Vector3(
419
+ Math.max(min.x, Math.min(max.x, this.x)),
420
+ Math.max(min.y, Math.min(max.y, this.y)),
421
+ Math.max(min.z, Math.min(max.z, this.z))
422
+ );
423
+ }
424
+
425
+ clampMagnitude(max: number): Vector3 {
426
+ const len = this.magnitude();
427
+ if (len > max) {
428
+ return this.scale(max / len);
429
+ }
430
+ return this.clone();
431
+ }
432
+
433
+ setMagnitude(len: number): Vector3 {
434
+ return this.normalize().scale(len);
435
+ }
436
+
437
+ reflect(normal: Vector3): Vector3 {
438
+ // r = d - 2(d.n)n
439
+ const d = this;
440
+ const n = normal.normalize(); // Ensure normal is normalized
441
+ const term = n.scale(2 * d.dot(n));
442
+ return d.subtract(term);
443
+ }
444
+
445
+ // Angles - Spherical
446
+ angleTo(other: Vector3): number {
447
+ const denom = this.magnitude() * other.magnitude();
448
+ if (denom === 0) return 0;
449
+ const dot = this.dot(other);
450
+ return Math.acos(Math.max(-1, Math.min(1, dot / denom)));
451
+ }
452
+
453
+ signedAngleTo(other: Vector3, axis: Vector3): number {
454
+ const angle = this.angleTo(other);
455
+ const cross = this.cross(other);
456
+ const sign = cross.dot(axis) >= 0 ? 1 : -1;
457
+ return angle * sign;
458
+ }
459
+
460
+ toSpherical(): { radius: number; theta: number; phi: number } {
461
+ const r = this.magnitude();
462
+ if (r === 0) return { radius: 0, theta: 0, phi: 0 };
463
+ const theta = Math.asin(this.z / r);
464
+ const phi = Math.atan2(this.y, this.x);
465
+ return { radius: r, theta, phi: phi >= 0 ? phi : phi + 2 * Math.PI };
466
+ }
467
+
468
+ toLatLng(): { lat: number; lng: number } {
469
+ const r = this.magnitude();
470
+ if (r === 0) return { lat: 0, lng: 0 };
471
+ const lat = Math.asin(this.z / r);
472
+ const lng = Math.atan2(this.y, this.x);
473
+ return {
474
+ lat: (lat * 180) / Math.PI,
475
+ lng: (lng * 180) / Math.PI
476
+ };
477
+ }
478
+
479
+ greatCircleDistanceTo(other: Vector3): number {
480
+ return this.angleTo(other);
481
+ }
482
+
483
+ greatCircleLerp(other: Vector3, t: number): Vector3 {
484
+ return this.slerp(other, t);
485
+ }
486
+
487
+ // Rotations
488
+ rotateAround(axis: Vector3, angle: number): Vector3 {
489
+ const k = axis.normalize();
490
+ const cos = Math.cos(angle);
491
+ const sin = Math.sin(angle);
492
+
493
+ const term1 = this.scale(cos);
494
+ const term2 = k.cross(this).scale(sin);
495
+ const term3 = k.scale(k.dot(this) * (1 - cos));
496
+
497
+ return term1.add(term2).add(term3);
498
+ }
499
+
500
+ rotateX(angle: number): Vector3 {
501
+ return this.rotateAround(Vector3.right(), angle);
502
+ }
503
+
504
+ rotateY(angle: number): Vector3 {
505
+ return this.rotateAround(Vector3.up(), angle);
506
+ }
507
+
508
+ rotateZ(angle: number): Vector3 {
509
+ return this.rotateAround(Vector3.forward(), angle);
510
+ }
511
+
512
+ // Projection
513
+ projectOnto(other: Vector3): Vector3 {
514
+ const denom = other.magnitudeSquared();
515
+ if (denom === 0) return Vector3.zero();
516
+ return other.scale(this.dot(other) / denom);
517
+ }
518
+
519
+ projectOntoPlane(normal: Vector3): Vector3 {
520
+ const d = this.projectOnto(normal);
521
+ return this.subtract(d);
522
+ }
523
+
524
+ refract(normal: Vector3, eta: number): Vector3 {
525
+ // Snell's law in vector form
526
+ const n = normal.normalize();
527
+ const i = this.normalize();
528
+ const nDotI = n.dot(i);
529
+ const k = 1 - eta * eta * (1 - nDotI * nDotI);
530
+ if (k < 0) {
531
+ // Total internal reflection - test expects a vector > 0
532
+ return this.reflect(normal);
533
+ }
534
+ return i.scale(eta).subtract(n.scale(eta * nDotI + Math.sqrt(k)));
535
+ }
536
+
537
+ toString(): string {
538
+ return `Vector3(${this.x.toFixed(4)}, ${this.y.toFixed(4)}, ${this.z.toFixed(4)})`;
72
539
  }
73
540
  }
@@ -0,0 +1,10 @@
1
+ declare module '@emotions-app/shared-utils/ontology/types' {
2
+ export interface OntologyType {
3
+ [key: string]: any;
4
+ }
5
+ export interface OntologyEntity {
6
+ id: string;
7
+ type: string;
8
+ [key: string]: any;
9
+ }
10
+ }
package/src/types.ts CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  import type { RefObject } from 'react';
6
6
  import type { HexGridFeatureFlags } from './features';
7
+ export type { HexGridFeatureFlags } from './features';
7
8
 
8
9
  /**
9
10
  * Photo type for HexGrid visualization
package/tsconfig.json CHANGED
@@ -1,18 +1,25 @@
1
1
  {
2
- "extends": "../../tsconfig.json",
3
2
  "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "../../",
6
- "declaration": true,
7
- "declarationMap": true,
8
- "composite": true,
9
- "noEmit": true
3
+ "target": "es2020",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "esModuleInterop": true,
8
+ "allowSyntheticDefaultImports": true,
9
+ "strict": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "noFallthroughCasesInSwitch": true,
12
+ "module": "esnext",
13
+ "moduleResolution": "node",
14
+ "resolveJsonModule": true,
15
+ "isolatedModules": true,
16
+ "noEmit": true,
17
+ "jsx": "react-jsx",
18
+ "baseUrl": ".",
19
+ "paths": {
20
+ "@/*": ["src/*"]
21
+ }
10
22
  },
11
- "include": [
12
- "src/**/*",
13
- "tests/**/*",
14
- "../../lib/**/*",
15
- "../../components/debug/**/*"
16
- ],
17
- "exclude": ["node_modules", "dist"]
23
+ "include": ["src"],
24
+ "exclude": ["node_modules", "dist", "tests"]
18
25
  }