@buley/hexgrid-3d 1.0.0 → 1.1.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/build_log.txt +500 -0
  2. package/build_src_log.txt +8 -0
  3. package/examples/basic-usage.tsx +19 -19
  4. package/package.json +1 -1
  5. package/public/hexgrid-worker.js +2350 -1638
  6. package/site/.eslintrc.json +3 -0
  7. package/site/DEPLOYMENT.md +196 -0
  8. package/site/INDEX.md +127 -0
  9. package/site/QUICK_START.md +86 -0
  10. package/site/README.md +85 -0
  11. package/site/SITE_SUMMARY.md +180 -0
  12. package/site/next.config.js +12 -0
  13. package/site/package.json +26 -0
  14. package/site/src/app/docs/page.tsx +148 -0
  15. package/site/src/app/examples/page.tsx +133 -0
  16. package/site/src/app/globals.css +160 -0
  17. package/site/src/app/layout.tsx +29 -0
  18. package/site/src/app/page.tsx +163 -0
  19. package/site/tsconfig.json +29 -0
  20. package/site/vercel.json +6 -0
  21. package/src/Snapshot.ts +790 -585
  22. package/src/adapters/DashAdapter.ts +57 -0
  23. package/src/adapters.ts +16 -18
  24. package/src/algorithms/AdvancedStatistics.ts +58 -24
  25. package/src/algorithms/BayesianStatistics.ts +43 -12
  26. package/src/algorithms/FlowField.ts +30 -6
  27. package/src/algorithms/FlowField3D.ts +573 -0
  28. package/src/algorithms/FluidSimulation.ts +19 -3
  29. package/src/algorithms/FluidSimulation3D.ts +664 -0
  30. package/src/algorithms/GraphAlgorithms.ts +19 -12
  31. package/src/algorithms/OutlierDetection.ts +72 -38
  32. package/src/algorithms/ParticleSystem.ts +12 -2
  33. package/src/algorithms/ParticleSystem3D.ts +567 -0
  34. package/src/algorithms/index.ts +14 -8
  35. package/src/compat.ts +10 -10
  36. package/src/components/HexGrid.tsx +10 -23
  37. package/src/components/NarrationOverlay.tsx +140 -52
  38. package/src/components/index.ts +2 -1
  39. package/src/features.ts +31 -31
  40. package/src/index.ts +11 -11
  41. package/src/lib/narration.ts +17 -0
  42. package/src/lib/stats-tracker.ts +25 -0
  43. package/src/lib/theme-colors.ts +12 -0
  44. package/src/math/HexCoordinates.ts +849 -4
  45. package/src/math/Matrix4.ts +2 -12
  46. package/src/math/Vector3.ts +49 -1
  47. package/src/math/index.ts +6 -6
  48. package/src/note-adapter.ts +50 -42
  49. package/src/ontology-adapter.ts +30 -23
  50. package/src/stores/uiStore.ts +34 -34
  51. package/src/types/shared-utils.d.ts +10 -0
  52. package/src/types.ts +110 -98
  53. package/src/utils/image-utils.ts +9 -6
  54. package/src/wasm/HexGridWasmWrapper.ts +436 -388
  55. package/src/wasm/index.ts +2 -2
  56. package/src/workers/hexgrid-math.ts +40 -35
  57. package/src/workers/hexgrid-worker.worker.ts +1992 -1018
  58. package/tsconfig.json +21 -14
@@ -8,21 +8,11 @@ export class Matrix4 {
8
8
  }
9
9
 
10
10
  static identity(): Matrix4 {
11
- return new Matrix4([
12
- 1, 0, 0, 0,
13
- 0, 1, 0, 0,
14
- 0, 0, 1, 0,
15
- 0, 0, 0, 1,
16
- ]);
11
+ return new Matrix4([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
17
12
  }
18
13
 
19
14
  static translation(x: number, y: number, z: number): Matrix4 {
20
- return new Matrix4([
21
- 1, 0, 0, x,
22
- 0, 1, 0, y,
23
- 0, 0, 1, z,
24
- 0, 0, 0, 1,
25
- ]);
15
+ return new Matrix4([1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1]);
26
16
  }
27
17
 
28
18
  transformPoint(point: Vector3): Vector3 {
@@ -49,7 +49,11 @@ export class Vector3 {
49
49
  this.z = z;
50
50
  }
51
51
 
52
- static fromLatLng(latitude: number, longitude: number, radius: number = 1): Vector3 {
52
+ static fromLatLng(
53
+ latitude: number,
54
+ longitude: number,
55
+ radius: number = 1
56
+ ): Vector3 {
53
57
  const latRad = (latitude * Math.PI) / 180;
54
58
  const lonRad = (longitude * Math.PI) / 180;
55
59
 
@@ -60,6 +64,50 @@ export class Vector3 {
60
64
  return new Vector3(x, y, z);
61
65
  }
62
66
 
67
+ clone(): Vector3 {
68
+ return new Vector3(this.x, this.y, this.z);
69
+ }
70
+
71
+ add(other: Vector3): Vector3 {
72
+ return new Vector3(this.x + other.x, this.y + other.y, this.z + other.z);
73
+ }
74
+
75
+ subtract(other: Vector3): Vector3 {
76
+ return new Vector3(this.x - other.x, this.y - other.y, this.z - other.z);
77
+ }
78
+
79
+ scale(factor: number): Vector3 {
80
+ return new Vector3(this.x * factor, this.y * factor, this.z * factor);
81
+ }
82
+
83
+ dot(other: Vector3): number {
84
+ return this.x * other.x + this.y * other.y + this.z * other.z;
85
+ }
86
+
87
+ cross(other: Vector3): Vector3 {
88
+ return new Vector3(
89
+ this.y * other.z - this.z * other.y,
90
+ this.z * other.x - this.x * other.z,
91
+ this.x * other.y - this.y * other.x
92
+ );
93
+ }
94
+
95
+ length(): number {
96
+ return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
97
+ }
98
+
99
+ magnitude(): number {
100
+ return this.length();
101
+ }
102
+
103
+ normalize(): Vector3 {
104
+ const len = this.length();
105
+ if (len === 0) {
106
+ return new Vector3(0, 0, 0);
107
+ }
108
+ return new Vector3(this.x / len, this.y / len, this.z / len);
109
+ }
110
+
63
111
  distanceTo(other: Vector3): number {
64
112
  const dx = this.x - other.x;
65
113
  const dy = this.y - other.y;
package/src/math/index.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Math Module Exports
3
- *
3
+ *
4
4
  * @module math
5
5
  */
6
6
 
7
- export * from './Vector3'
8
- export * from './Matrix4'
9
- export * from './Quaternion'
10
- export * from './HexCoordinates'
11
- export * from './SpatialIndex'
7
+ export * from './Vector3';
8
+ export * from './Matrix4';
9
+ export * from './Quaternion';
10
+ export * from './HexCoordinates';
11
+ export * from './SpatialIndex';
@@ -1,53 +1,53 @@
1
1
  /**
2
2
  * Adapter for embedding notes directly in the grid
3
- *
3
+ *
4
4
  * This adapter allows notes (Cyrano's internal notes, user notes, etc.)
5
5
  * to be embedded directly in GridItems with full metadata preservation.
6
6
  */
7
7
 
8
- import type { GridItem } from './types'
9
- import type { ItemAdapter, AdapterOptions } from './adapters'
8
+ import type { GridItem } from './types';
9
+ import type { ItemAdapter, AdapterOptions } from './adapters';
10
10
 
11
11
  /**
12
12
  * Note content structure
13
13
  */
14
14
  export interface NoteContent {
15
- title?: string
16
- text: string
17
- summary?: string
15
+ title?: string;
16
+ text: string;
17
+ summary?: string;
18
18
  }
19
19
 
20
20
  /**
21
21
  * Note metadata structure
22
22
  */
23
23
  export interface NoteMetadata {
24
- type?: string
25
- category?: string
26
- subcategory?: string
27
- tags?: string[]
28
- targetUserId?: string
29
- status?: 'active' | 'archived' | 'draft'
30
- priority?: 'low' | 'medium' | 'high' | 'urgent'
31
- context?: string
32
- permissions?: 'private' | 'shared' | 'public'
33
- sharedWith?: string[]
24
+ type?: string;
25
+ category?: string;
26
+ subcategory?: string;
27
+ tags?: string[];
28
+ targetUserId?: string;
29
+ status?: 'active' | 'archived' | 'draft';
30
+ priority?: 'low' | 'medium' | 'high' | 'urgent';
31
+ context?: string;
32
+ permissions?: 'private' | 'shared' | 'public';
33
+ sharedWith?: string[];
34
34
  }
35
35
 
36
36
  /**
37
37
  * Note structure matching Affectively's note format
38
38
  */
39
39
  export interface Note {
40
- id: string
41
- content: NoteContent
42
- metadata?: NoteMetadata
43
- date: string
40
+ id: string;
41
+ content: NoteContent;
42
+ metadata?: NoteMetadata;
43
+ date: string;
44
44
  }
45
45
 
46
46
  /**
47
47
  * Calculate velocity for a note based on recency, priority, and metadata richness
48
48
  */
49
49
  function calculateNoteVelocity(note: Note): number {
50
- let velocity = 0.1 // Base minimum
50
+ let velocity = 0.1; // Base minimum
51
51
 
52
52
  // Priority contribution (0-0.3)
53
53
  const priorityMap: Record<string, number> = {
@@ -55,29 +55,30 @@ function calculateNoteVelocity(note: Note): number {
55
55
  high: 0.2,
56
56
  medium: 0.1,
57
57
  low: 0.05,
58
- }
58
+ };
59
59
  if (note.metadata?.priority) {
60
- velocity += priorityMap[note.metadata.priority] || 0.1
60
+ velocity += priorityMap[note.metadata.priority] || 0.1;
61
61
  }
62
62
 
63
63
  // Recency contribution (0-0.4)
64
64
  if (note.date) {
65
- const ageMs = Date.now() - new Date(note.date).getTime()
66
- const ageHours = ageMs / (1000 * 60 * 60)
67
- const recencyFactor = Math.max(0, 1 - ageHours / 168) // Decay over 1 week
68
- velocity += recencyFactor * 0.4
65
+ const ageMs = Date.now() - new Date(note.date).getTime();
66
+ const ageHours = ageMs / (1000 * 60 * 60);
67
+ const recencyFactor = Math.max(0, 1 - ageHours / 168); // Decay over 1 week
68
+ velocity += recencyFactor * 0.4;
69
69
  }
70
70
 
71
71
  // Metadata richness contribution (0-0.2)
72
- let contextScore = 0
73
- if (note.metadata?.type) contextScore += 0.05
74
- if (note.metadata?.category) contextScore += 0.05
75
- if (note.metadata?.tags && note.metadata.tags.length > 0) contextScore += 0.05
76
- if (note.metadata?.context) contextScore += 0.05
77
- velocity += Math.min(contextScore, 0.2)
72
+ let contextScore = 0;
73
+ if (note.metadata?.type) contextScore += 0.05;
74
+ if (note.metadata?.category) contextScore += 0.05;
75
+ if (note.metadata?.tags && note.metadata.tags.length > 0)
76
+ contextScore += 0.05;
77
+ if (note.metadata?.context) contextScore += 0.05;
78
+ velocity += Math.min(contextScore, 0.2);
78
79
 
79
80
  // Clamp to [0.1, 1.0]
80
- return Math.max(0.1, Math.min(1.0, velocity))
81
+ return Math.max(0.1, Math.min(1.0, velocity));
81
82
  }
82
83
 
83
84
  /**
@@ -85,7 +86,7 @@ function calculateNoteVelocity(note: Note): number {
85
86
  */
86
87
  export const noteAdapter: ItemAdapter<Note> = {
87
88
  toGridItem(note: Note, options?: AdapterOptions): GridItem<Note> {
88
- const velocity = options?.velocity ?? calculateNoteVelocity(note)
89
+ const velocity = options?.velocity ?? calculateNoteVelocity(note);
89
90
 
90
91
  return {
91
92
  id: note.id,
@@ -100,25 +101,32 @@ export const noteAdapter: ItemAdapter<Note> = {
100
101
  category: note.metadata?.category,
101
102
  // Store metadata in metrics for filtering/sorting
102
103
  metrics: {
103
- priority: note.metadata?.priority === 'urgent' ? 4 : note.metadata?.priority === 'high' ? 3 : note.metadata?.priority === 'medium' ? 2 : 1,
104
+ priority:
105
+ note.metadata?.priority === 'urgent'
106
+ ? 4
107
+ : note.metadata?.priority === 'high'
108
+ ? 3
109
+ : note.metadata?.priority === 'medium'
110
+ ? 2
111
+ : 1,
104
112
  tagCount: note.metadata?.tags?.length || 0,
105
113
  },
106
- }
114
+ };
107
115
  },
108
116
 
109
117
  fromGridItem(item: GridItem<Note>): Note {
110
118
  if (!item.data) {
111
- throw new Error('GridItem missing note data')
119
+ throw new Error('GridItem missing note data');
112
120
  }
113
- return item.data
121
+ return item.data;
114
122
  },
115
123
 
116
124
  calculateVelocity(note: Note): number {
117
- return calculateNoteVelocity(note)
125
+ return calculateNoteVelocity(note);
118
126
  },
119
127
 
120
128
  extractVisualUrl(note: Note): string | undefined {
121
129
  // Notes can have generated visualizations
122
- return `/api/notes/${note.id}/visualization`
130
+ return `/api/notes/${note.id}/visualization`;
123
131
  },
124
- }
132
+ };
@@ -1,49 +1,53 @@
1
1
  /**
2
2
  * Adapter for embedding ontology entities directly in the grid
3
- *
3
+ *
4
4
  * This adapter allows ontology entities from @emotions-app/shared-utils/ontology
5
5
  * to be embedded directly in GridItems with full metadata preservation.
6
6
  */
7
7
 
8
- import type { OntologyEntity } from '@emotions-app/shared-utils/ontology/types'
9
- import type { GridItem } from './types'
10
- import type { ItemAdapter, AdapterOptions } from './adapters'
8
+ import type { OntologyEntity } from '@emotions-app/shared-utils/ontology/types';
9
+ import type { GridItem } from './types';
10
+ import type { ItemAdapter, AdapterOptions } from './adapters';
11
11
 
12
12
  /**
13
13
  * Calculate velocity for an ontology entity based on provenance confidence
14
14
  * and recency
15
15
  */
16
16
  function calculateEntityVelocity(entity: OntologyEntity): number {
17
- let velocity = 0.1 // Base minimum
17
+ let velocity = 0.1; // Base minimum
18
18
 
19
19
  // Use provenance confidence (0-1) as primary factor
20
- const confidence = entity.metadata.provenance.confidence
21
- velocity += confidence * 0.5
20
+ const confidence = entity.metadata.provenance.confidence;
21
+ velocity += confidence * 0.5;
22
22
 
23
23
  // Recency factor based on lastModified
24
24
  if (entity.metadata.lastModified) {
25
- const ageMs = Date.now() - new Date(entity.metadata.lastModified).getTime()
26
- const ageHours = ageMs / (1000 * 60 * 60)
27
- const recencyFactor = Math.max(0, 1 - ageHours / 168) // Decay over 1 week
28
- velocity += recencyFactor * 0.4
25
+ const ageMs = Date.now() - new Date(entity.metadata.lastModified).getTime();
26
+ const ageHours = ageMs / (1000 * 60 * 60);
27
+ const recencyFactor = Math.max(0, 1 - ageHours / 168); // Decay over 1 week
28
+ velocity += recencyFactor * 0.4;
29
29
  }
30
30
 
31
31
  // Clamp to [0.1, 1.0]
32
- return Math.max(0.1, Math.min(1.0, velocity))
32
+ return Math.max(0.1, Math.min(1.0, velocity));
33
33
  }
34
34
 
35
35
  /**
36
36
  * Adapter for embedding ontology entities directly
37
37
  */
38
38
  export const ontologyEntityAdapter: ItemAdapter<OntologyEntity> = {
39
- toGridItem(entity: OntologyEntity, options?: AdapterOptions): GridItem<OntologyEntity> {
40
- const velocity = options?.velocity ?? calculateEntityVelocity(entity)
39
+ toGridItem(
40
+ entity: OntologyEntity,
41
+ options?: AdapterOptions
42
+ ): GridItem<OntologyEntity> {
43
+ const velocity = options?.velocity ?? calculateEntityVelocity(entity);
41
44
 
42
45
  return {
43
46
  id: entity['@id'],
44
47
  type: 'ontology-entity',
45
48
  title: entity.label,
46
- description: (entity.properties.description as string | undefined) || entity.label,
49
+ description:
50
+ (entity.properties.description as string | undefined) || entity.label,
47
51
  data: entity,
48
52
  ontologyMetadata: {
49
53
  entityId: entity['@id'],
@@ -54,24 +58,27 @@ export const ontologyEntityAdapter: ItemAdapter<OntologyEntity> = {
54
58
  provenance: entity.metadata.provenance,
55
59
  },
56
60
  velocity,
57
- createdAt: entity.metadata.lastModified || entity.metadata.provenance.extractedAt,
61
+ createdAt:
62
+ entity.metadata.lastModified || entity.metadata.provenance.extractedAt,
58
63
  // Extract visual URL if available in properties
59
- imageUrl: options?.visualUrl || (entity.properties.imageUrl as string | undefined),
60
- }
64
+ imageUrl:
65
+ options?.visualUrl ||
66
+ (entity.properties.imageUrl as string | undefined),
67
+ };
61
68
  },
62
69
 
63
70
  fromGridItem(item: GridItem<OntologyEntity>): OntologyEntity {
64
71
  if (!item.data) {
65
- throw new Error('GridItem missing ontology entity data')
72
+ throw new Error('GridItem missing ontology entity data');
66
73
  }
67
- return item.data
74
+ return item.data;
68
75
  },
69
76
 
70
77
  calculateVelocity(entity: OntologyEntity): number {
71
- return calculateEntityVelocity(entity)
78
+ return calculateEntityVelocity(entity);
72
79
  },
73
80
 
74
81
  extractVisualUrl(entity: OntologyEntity): string | undefined {
75
- return entity.properties.imageUrl as string | undefined
82
+ return entity.properties.imageUrl as string | undefined;
76
83
  },
77
- }
84
+ };
@@ -1,85 +1,85 @@
1
1
  type UIState = {
2
- debugOpen: boolean
3
- showStats: boolean
4
- cameraOpen?: boolean
5
- showNarration?: boolean
6
- }
2
+ debugOpen: boolean;
3
+ showStats: boolean;
4
+ cameraOpen?: boolean;
5
+ showNarration?: boolean;
6
+ };
7
7
 
8
8
  // Safe localStorage helpers that never throw
9
9
  const safeGetItem = (key: string): string | null => {
10
10
  // istanbul ignore next
11
- if (typeof window === 'undefined') return null
11
+ if (typeof window === 'undefined') return null;
12
12
  try {
13
- return window.localStorage.getItem(key)
13
+ return window.localStorage.getItem(key);
14
14
  } catch {
15
15
  // istanbul ignore next
16
- return null
16
+ return null;
17
17
  }
18
- }
18
+ };
19
19
 
20
20
  const safeSetItem = (key: string, value: string): void => {
21
21
  // istanbul ignore next
22
- if (typeof window === 'undefined') return
22
+ if (typeof window === 'undefined') return;
23
23
  try {
24
- window.localStorage.setItem(key, value)
24
+ window.localStorage.setItem(key, value);
25
25
  } catch {
26
26
  // istanbul ignore next - private browsing, quota exceeded
27
27
  }
28
- }
28
+ };
29
29
 
30
30
  // Initialize showNarration from localStorage if available
31
- const savedNarration = safeGetItem('hexgrid.showNarration')
32
- const initialShowNarration = savedNarration === 'true'
31
+ const savedNarration = safeGetItem('hexgrid.showNarration');
32
+ const initialShowNarration = savedNarration === 'true';
33
33
 
34
34
  const state: UIState = {
35
35
  debugOpen: false,
36
36
  showStats: false,
37
37
  cameraOpen: false,
38
- showNarration: initialShowNarration
39
- }
38
+ showNarration: initialShowNarration,
39
+ };
40
40
 
41
- const listeners = new Set<(s: UIState) => void>()
41
+ const listeners = new Set<(s: UIState) => void>();
42
42
 
43
43
  const uiStore = {
44
44
  getState(): UIState {
45
- return { ...state }
45
+ return { ...state };
46
46
  },
47
47
  set(partial: Partial<UIState>) {
48
- let changed = false
48
+ let changed = false;
49
49
  for (const k of Object.keys(partial) as (keyof UIState)[]) {
50
50
  if (partial[k] !== undefined && state[k] !== partial[k]) {
51
51
  // @ts-ignore
52
- state[k] = partial[k]
53
- changed = true
52
+ state[k] = partial[k];
53
+ changed = true;
54
54
  }
55
55
  }
56
56
  if (changed) {
57
57
  // Persist showNarration to localStorage for cross-refresh consistency
58
58
  if (partial.showNarration !== undefined) {
59
- safeSetItem('hexgrid.showNarration', String(!!partial.showNarration))
59
+ safeSetItem('hexgrid.showNarration', String(!!partial.showNarration));
60
60
  }
61
- for (const cb of Array.from(listeners)) cb({ ...state })
61
+ for (const cb of Array.from(listeners)) cb({ ...state });
62
62
  }
63
63
  },
64
64
  subscribe(cb: (s: UIState) => void) {
65
- listeners.add(cb)
65
+ listeners.add(cb);
66
66
  // emit current immediately
67
- cb({ ...state })
68
- return () => listeners.delete(cb)
67
+ cb({ ...state });
68
+ return () => listeners.delete(cb);
69
69
  },
70
70
  toggleDebug() {
71
- this.set({ debugOpen: !state.debugOpen })
71
+ this.set({ debugOpen: !state.debugOpen });
72
72
  },
73
73
  toggleStats() {
74
- this.set({ showStats: !state.showStats })
74
+ this.set({ showStats: !state.showStats });
75
75
  },
76
76
  toggleCamera() {
77
- this.set({ cameraOpen: !state.cameraOpen })
77
+ this.set({ cameraOpen: !state.cameraOpen });
78
78
  },
79
79
  toggleNarration() {
80
- this.set({ showNarration: !state.showNarration })
81
- }
82
- }
80
+ this.set({ showNarration: !state.showNarration });
81
+ },
82
+ };
83
83
 
84
- export { uiStore }
85
- export default uiStore
84
+ export { uiStore };
85
+ export default uiStore;
@@ -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
+ }