@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
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Adapter for Zero-Copy Data Sync between Dash 2.0 and HexGrid-3D.
3
+ *
4
+ * This adapter subscribes to a Dash "LiveQuery" which returns pointers to shared memory (SharedArrayBuffer)
5
+ * or Float32Arrays. It then syncs this data directly to the HexGridWasm instance.
6
+ */
7
+
8
+ // Placeholder types until we link the actual Dash package
9
+ interface DashQueryHandle {
10
+ ptr: number;
11
+ size: number;
12
+ buffer: SharedArrayBuffer | ArrayBuffer;
13
+ }
14
+
15
+ export class DashAdapter {
16
+ private dash: any; // Will be typed when we link @buley/dash
17
+
18
+ constructor(dashInstance: any) {
19
+ this.dash = dashInstance;
20
+ }
21
+
22
+ /**
23
+ * Subscribe to a semantic query in Dash and sync results to HexGrid.
24
+ *
25
+ * @param query The vector search query
26
+ * @param gridInstance The WASM instance of the HexGrid
27
+ */
28
+ bindSemanticSearch(query: string, particleSystem: any) {
29
+ console.log('[DashAdapter] Binding semantic search:', query);
30
+
31
+ // Hypothetical Zero-Copy API from Dash 2.0
32
+ if (this.dash.liveQueryPtr) {
33
+ this.dash.liveQueryPtr(`SELECT embedding FROM dash_vec_idx WHERE embedding MATCH '${query}'`).subscribe((handle: DashQueryHandle) => {
34
+ console.log(`[DashAdapter] Received ${handle.size} bytes from Dash.`);
35
+
36
+ // Assume the handle.buffer contains [pos, color, scale] interleaved or tightly packed
37
+ // For this MVP, we treat it as just positions
38
+ const floatView = new Float32Array(handle.buffer);
39
+
40
+ // Zero-Copy Injection logic would go here
41
+ // We can't strictly "inject" one buffer into 3 separate Float32Arrays unless they are contiguous in the SAB
42
+ // or we create views into offsets.
43
+
44
+ // Hypothetical offset logic:
45
+ const count = handle.size / 4 / 7; // pos + color + scale = 3+3+1 = 7 floats
46
+
47
+ // particleSystem.setSharedBuffers({
48
+ // positions: floatView.subarray(0, count * 3),
49
+ // colors: floatView.subarray(count * 3, count * 6),
50
+ // scales: floatView.subarray(count * 6, count * 7)
51
+ // });
52
+ });
53
+ } else {
54
+ console.warn('[DashAdapter] Dash instance does not support Zero-Copy liveQueryPtr yet.');
55
+ }
56
+ }
57
+ }
package/src/adapters.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Type-safe adapter pattern for converting domain objects to GridItems
3
3
  */
4
4
 
5
- import type { GridItem } from './types'
5
+ import type { GridItem } from './types';
6
6
 
7
7
  /**
8
8
  * Options for adapter conversion
@@ -11,15 +11,15 @@ export interface AdapterOptions {
11
11
  /**
12
12
  * Custom velocity calculation override
13
13
  */
14
- velocity?: number
14
+ velocity?: number;
15
15
  /**
16
16
  * Custom visual URL override
17
17
  */
18
- visualUrl?: string
18
+ visualUrl?: string;
19
19
  /**
20
20
  * Additional metadata to merge
21
21
  */
22
- metadata?: Record<string, unknown>
22
+ metadata?: Record<string, unknown>;
23
23
  }
24
24
 
25
25
  /**
@@ -29,37 +29,35 @@ export interface ItemAdapter<T> {
29
29
  /**
30
30
  * Convert a domain object to a GridItem
31
31
  */
32
- toGridItem(data: T, options?: AdapterOptions): GridItem<T>
32
+ toGridItem(data: T, options?: AdapterOptions): GridItem<T>;
33
33
  /**
34
34
  * Extract the original domain object from a GridItem
35
35
  */
36
- fromGridItem(item: GridItem<T>): T
36
+ fromGridItem(item: GridItem<T>): T;
37
37
  /**
38
38
  * Calculate velocity for the item (optional)
39
39
  */
40
- calculateVelocity?(data: T): number
40
+ calculateVelocity?(data: T): number;
41
41
  /**
42
42
  * Extract visual URL for the item (optional)
43
43
  */
44
- extractVisualUrl?(data: T): string | undefined
44
+ extractVisualUrl?(data: T): string | undefined;
45
45
  }
46
46
 
47
47
  /**
48
48
  * Helper to create adapters for common patterns
49
49
  */
50
- export function createAdapter<T>(
51
- config: {
52
- type: string
53
- toGridItem: (data: T, options?: AdapterOptions) => GridItem<T>
54
- fromGridItem: (item: GridItem<T>) => T
55
- calculateVelocity?: (data: T) => number
56
- extractVisualUrl?: (data: T) => string | undefined
57
- }
58
- ): ItemAdapter<T> {
50
+ export function createAdapter<T>(config: {
51
+ type: string;
52
+ toGridItem: (data: T, options?: AdapterOptions) => GridItem<T>;
53
+ fromGridItem: (item: GridItem<T>) => T;
54
+ calculateVelocity?: (data: T) => number;
55
+ extractVisualUrl?: (data: T) => string | undefined;
56
+ }): ItemAdapter<T> {
59
57
  return {
60
58
  toGridItem: config.toGridItem,
61
59
  fromGridItem: config.fromGridItem,
62
60
  calculateVelocity: config.calculateVelocity,
63
61
  extractVisualUrl: config.extractVisualUrl,
64
- }
62
+ };
65
63
  }
@@ -21,10 +21,12 @@ export function theilIndex(values: number[]): number {
21
21
  if (values.length === 0) return 0;
22
22
  const avg = values.reduce((sum, val) => sum + val, 0) / values.length;
23
23
  if (avg === 0) return 0;
24
- return values.reduce((sum, val) => {
25
- const ratio = val / avg;
26
- return sum + (ratio === 0 ? 0 : ratio * Math.log(ratio));
27
- }, 0) / values.length;
24
+ return (
25
+ values.reduce((sum, val) => {
26
+ const ratio = val / avg;
27
+ return sum + (ratio === 0 ? 0 : ratio * Math.log(ratio));
28
+ }, 0) / values.length
29
+ );
28
30
  }
29
31
 
30
32
  export function atkinsonIndex(values: number[], epsilon: number): number {
@@ -45,7 +47,10 @@ export function atkinsonIndex(values: number[], epsilon: number): number {
45
47
  return 1 - eq / avg;
46
48
  }
47
49
 
48
- export function paretoRatio(values: number[], topFraction: number): {
50
+ export function paretoRatio(
51
+ values: number[],
52
+ topFraction: number
53
+ ): {
49
54
  ratioHeld: number;
50
55
  paretoIndex: number;
51
56
  } {
@@ -126,7 +131,7 @@ export function detectTrend(values: number[]): TrendResult {
126
131
  let den = 0;
127
132
  let ssRes = 0;
128
133
  let ssTot = 0;
129
-
134
+
130
135
  for (let i = 0; i < n; i++) {
131
136
  const dx = i - xMean;
132
137
  num += dx * (values[i] - yMean);
@@ -134,7 +139,7 @@ export function detectTrend(values: number[]): TrendResult {
134
139
  }
135
140
  const slope = den === 0 ? 0 : num / den;
136
141
  const intercept = yMean - slope * xMean;
137
-
142
+
138
143
  // Calculate R-squared
139
144
  for (let i = 0; i < n; i++) {
140
145
  const predicted = slope * i + intercept;
@@ -142,8 +147,9 @@ export function detectTrend(values: number[]): TrendResult {
142
147
  ssTot += Math.pow(values[i] - yMean, 2);
143
148
  }
144
149
  const rSquared = ssTot === 0 ? 0 : 1 - ssRes / ssTot;
145
-
146
- const direction = slope > 0.1 ? 'increasing' : slope < -0.1 ? 'decreasing' : 'stable';
150
+
151
+ const direction =
152
+ slope > 0.1 ? 'increasing' : slope < -0.1 ? 'decreasing' : 'stable';
147
153
  return { slope, direction, rSquared };
148
154
  }
149
155
 
@@ -151,7 +157,8 @@ export function detectChangePoints(values: number[]): number[] {
151
157
  if (values.length < 3) return [];
152
158
  const changes: number[] = [];
153
159
  const avg = values.reduce((sum, val) => sum + val, 0) / values.length;
154
- const variance = values.reduce((sum, val) => sum + (val - avg) ** 2, 0) / values.length;
160
+ const variance =
161
+ values.reduce((sum, val) => sum + (val - avg) ** 2, 0) / values.length;
155
162
  const threshold = Math.sqrt(variance) * 1.5;
156
163
  for (let i = 1; i < values.length; i++) {
157
164
  if (Math.abs(values[i] - values[i - 1]) > threshold) {
@@ -173,7 +180,10 @@ export function movingAverage(values: number[], windowSize: number): number[] {
173
180
  return result;
174
181
  }
175
182
 
176
- export function exponentialMovingAverage(values: number[], alpha: number): number[] {
183
+ export function exponentialMovingAverage(
184
+ values: number[],
185
+ alpha: number
186
+ ): number[] {
177
187
  if (values.length === 0) return [];
178
188
  const result: number[] = [];
179
189
  let current = values[0] ?? 0;
@@ -205,7 +215,11 @@ export function sparkline(values: number[]): string {
205
215
  .join('');
206
216
  }
207
217
 
208
- export function sparklineSvg(values: number[], width: number = 100, height: number = 20): string {
218
+ export function sparklineSvg(
219
+ values: number[],
220
+ width: number = 100,
221
+ height: number = 20
222
+ ): string {
209
223
  if (values.length === 0) return '';
210
224
  const min = Math.min(...values);
211
225
  const max = Math.max(...values);
@@ -254,32 +268,48 @@ export function hellingerDistance(p: number[], q: number[]): number {
254
268
  }
255
269
 
256
270
  // Double Exponential Smoothing (Holt's method)
257
- export function doubleExponentialSmoothing(values: number[], alpha: number = 0.3, beta: number = 0.1): number[] {
271
+ export function doubleExponentialSmoothing(
272
+ values: number[],
273
+ alpha: number = 0.3,
274
+ beta: number = 0.1
275
+ ): number[] {
258
276
  if (values.length === 0) return [];
259
277
  const result: number[] = [];
260
278
  let level = values[0] ?? 0;
261
279
  let trend = 0;
262
-
280
+
263
281
  result.push(level);
264
-
282
+
265
283
  for (let i = 1; i < values.length; i++) {
266
284
  const prevLevel = level;
267
285
  level = alpha * values[i] + (1 - alpha) * (level + trend);
268
286
  trend = beta * (level - prevLevel) + (1 - beta) * trend;
269
287
  result.push(level + trend);
270
288
  }
271
-
289
+
272
290
  return result;
273
291
  }
274
292
 
275
293
  // Euler Characteristic (simplified for 2D)
276
- export function eulerCharacteristic(vertices: number, edges: number, faces: number): number {
294
+ export function eulerCharacteristic(
295
+ vertices: number,
296
+ edges: number,
297
+ faces: number
298
+ ): number {
277
299
  return vertices - edges + faces;
278
300
  }
279
301
 
280
302
  // Estimate Betti Numbers (simplified - returns basic topological invariants)
281
- export function estimateBettiNumbers(complex: { vertices: number; edges: number; faces: number }): { b0: number; b1: number } {
282
- const euler = eulerCharacteristic(complex.vertices, complex.edges, complex.faces);
303
+ export function estimateBettiNumbers(complex: {
304
+ vertices: number;
305
+ edges: number;
306
+ faces: number;
307
+ }): { b0: number; b1: number } {
308
+ const euler = eulerCharacteristic(
309
+ complex.vertices,
310
+ complex.edges,
311
+ complex.faces
312
+ );
283
313
  // Simplified: b0 = number of connected components (assume 1 for now)
284
314
  // b1 = edges - vertices + 1 (for a connected graph)
285
315
  const b0 = 1;
@@ -303,7 +333,9 @@ export interface TerritoryStats {
303
333
  compactness: number;
304
334
  }
305
335
 
306
- export function computeTerritoryStats(territories: Array<{ area: number; perimeter: number }>): TerritoryStats {
336
+ export function computeTerritoryStats(
337
+ territories: Array<{ area: number; perimeter: number }>
338
+ ): TerritoryStats {
307
339
  if (territories.length === 0) {
308
340
  return {
309
341
  totalTerritories: 0,
@@ -313,11 +345,13 @@ export function computeTerritoryStats(territories: Array<{ area: number; perimet
313
345
  compactness: 0,
314
346
  };
315
347
  }
316
-
317
- const sizes = territories.map(t => t.area);
348
+
349
+ const sizes = territories.map((t) => t.area);
318
350
  const totalSize = sizes.reduce((sum, s) => sum + s, 0);
319
- const avgCompactness = territories.reduce((sum, t) => sum + compactness(t.area, t.perimeter), 0) / territories.length;
320
-
351
+ const avgCompactness =
352
+ territories.reduce((sum, t) => sum + compactness(t.area, t.perimeter), 0) /
353
+ territories.length;
354
+
321
355
  return {
322
356
  totalTerritories: territories.length,
323
357
  averageSize: totalSize / territories.length,
@@ -47,7 +47,12 @@ export class KalmanFilter {
47
47
  private processNoise: number;
48
48
  private measurementNoise: number;
49
49
 
50
- constructor(initialState: number, initialUncertainty: number, processNoise: number, measurementNoise: number) {
50
+ constructor(
51
+ initialState: number,
52
+ initialUncertainty: number,
53
+ processNoise: number,
54
+ measurementNoise: number
55
+ ) {
51
56
  this.state = initialState;
52
57
  this.uncertainty = initialUncertainty;
53
58
  this.processNoise = processNoise;
@@ -56,7 +61,8 @@ export class KalmanFilter {
56
61
 
57
62
  update(measurement: number): void {
58
63
  const predictedUncertainty = this.uncertainty + this.processNoise;
59
- const kalmanGain = predictedUncertainty / (predictedUncertainty + this.measurementNoise);
64
+ const kalmanGain =
65
+ predictedUncertainty / (predictedUncertainty + this.measurementNoise);
60
66
  this.state = this.state + kalmanGain * (measurement - this.state);
61
67
  this.uncertainty = (1 - kalmanGain) * predictedUncertainty;
62
68
  }
@@ -106,7 +112,10 @@ export function bayesianWinProbability(wins: number, losses: number): number {
106
112
  return wins / total;
107
113
  }
108
114
 
109
- export function bayesianConquestRate(successes: number, trials: number): number {
115
+ export function bayesianConquestRate(
116
+ successes: number,
117
+ trials: number
118
+ ): number {
110
119
  if (trials === 0) return 0;
111
120
  return successes / trials;
112
121
  }
@@ -121,7 +130,9 @@ export interface ProbabilitySnapshot {
121
130
  probabilities: Array<{ label: string; probability: number }>;
122
131
  }
123
132
 
124
- export function generateProbabilitySnapshot(labels: string[]): ProbabilitySnapshot {
133
+ export function generateProbabilitySnapshot(
134
+ labels: string[]
135
+ ): ProbabilitySnapshot {
125
136
  const probability = labels.length > 0 ? 1 / labels.length : 0;
126
137
  return {
127
138
  probabilities: labels.map((label) => ({ label, probability })),
@@ -138,7 +149,7 @@ export class DirichletDistribution {
138
149
 
139
150
  mean(): number[] {
140
151
  const sum = this.alphas.reduce((s, a) => s + a, 0);
141
- return this.alphas.map(a => sum === 0 ? 0 : a / sum);
152
+ return this.alphas.map((a) => (sum === 0 ? 0 : a / sum));
142
153
  }
143
154
  }
144
155
 
@@ -222,19 +233,30 @@ export class HiddenMarkovModel {
222
233
  }
223
234
 
224
235
  // Bayesian A/B Test
225
- export function bayesianABTest(successA: number, trialsA: number, successB: number, trialsB: number): number {
236
+ export function bayesianABTest(
237
+ successA: number,
238
+ trialsA: number,
239
+ successB: number,
240
+ trialsB: number
241
+ ): number {
226
242
  const probA = trialsA === 0 ? 0.5 : successA / trialsA;
227
243
  const probB = trialsB === 0 ? 0.5 : successB / trialsB;
228
244
  return probB - probA; // Difference in probabilities
229
245
  }
230
246
 
231
247
  // Bayes Factor
232
- export function bayesFactor(priorOdds: number, likelihoodRatio: number): number {
248
+ export function bayesFactor(
249
+ priorOdds: number,
250
+ likelihoodRatio: number
251
+ ): number {
233
252
  return priorOdds * likelihoodRatio;
234
253
  }
235
254
 
236
255
  // MAP Estimate (Maximum A Posteriori)
237
- export function mapEstimate(data: number[], prior: { alpha: number; beta: number }): number {
256
+ export function mapEstimate(
257
+ data: number[],
258
+ prior: { alpha: number; beta: number }
259
+ ): number {
238
260
  const sum = data.reduce((s, x) => s + x, 0);
239
261
  const n = data.length;
240
262
  const alpha = prior.alpha + sum;
@@ -252,7 +274,11 @@ export function learnMarkovChain(sequence: string[]): MarkovChain {
252
274
  }
253
275
 
254
276
  // Bootstrap Confidence Interval
255
- export function bootstrapConfidenceInterval(data: number[], iterations: number = 1000, confidence: number = 0.95): { lower: number; upper: number } {
277
+ export function bootstrapConfidenceInterval(
278
+ data: number[],
279
+ iterations: number = 1000,
280
+ confidence: number = 0.95
281
+ ): { lower: number; upper: number } {
256
282
  const samples: number[] = [];
257
283
  for (let i = 0; i < iterations; i++) {
258
284
  const sample: number[] = [];
@@ -262,8 +288,8 @@ export function bootstrapConfidenceInterval(data: number[], iterations: number =
262
288
  samples.push(sample.reduce((s, x) => s + x, 0) / sample.length);
263
289
  }
264
290
  samples.sort((a, b) => a - b);
265
- const lowerIdx = Math.floor((1 - confidence) / 2 * samples.length);
266
- const upperIdx = Math.floor((1 + confidence) / 2 * samples.length);
291
+ const lowerIdx = Math.floor(((1 - confidence) / 2) * samples.length);
292
+ const upperIdx = Math.floor(((1 + confidence) / 2) * samples.length);
267
293
  return {
268
294
  lower: samples[lowerIdx] ?? 0,
269
295
  upper: samples[upperIdx] ?? 0,
@@ -271,7 +297,12 @@ export function bootstrapConfidenceInterval(data: number[], iterations: number =
271
297
  }
272
298
 
273
299
  // Monte Carlo Integration
274
- export function monteCarloIntegrate(fn: (x: number) => number, a: number, b: number, samples: number = 1000): number {
300
+ export function monteCarloIntegrate(
301
+ fn: (x: number) => number,
302
+ a: number,
303
+ b: number,
304
+ samples: number = 1000
305
+ ): number {
275
306
  let sum = 0;
276
307
  for (let i = 0; i < samples; i++) {
277
308
  const x = a + Math.random() * (b - a);
@@ -30,7 +30,13 @@ export class FlowField2D {
30
30
  this.sources = [];
31
31
  }
32
32
 
33
- addSource(x: number, y: number, vx: number, vy: number, strength: number): void {
33
+ addSource(
34
+ x: number,
35
+ y: number,
36
+ vx: number,
37
+ vy: number,
38
+ strength: number
39
+ ): void {
34
40
  this.sources.push({ x, y, vx, vy, strength });
35
41
  }
36
42
 
@@ -55,7 +61,10 @@ export class FlowField2D {
55
61
  return new Vector2(vx, vy);
56
62
  }
57
63
 
58
- sampleFull(x: number, y: number): { velocity: Vector2; divergence: number; curl: number } {
64
+ sampleFull(
65
+ x: number,
66
+ y: number
67
+ ): { velocity: Vector2; divergence: number; curl: number } {
59
68
  const velocity = this.sample(x, y);
60
69
  return {
61
70
  velocity,
@@ -64,7 +73,11 @@ export class FlowField2D {
64
73
  };
65
74
  }
66
75
 
67
- traceStreamline(x: number, y: number, options: { maxLength: number; stepSize: number; maxSteps: number }): Streamline {
76
+ traceStreamline(
77
+ x: number,
78
+ y: number,
79
+ options: { maxLength: number; stepSize: number; maxSteps: number }
80
+ ): Streamline {
68
81
  const points: Vector2[] = [new Vector2(x, y)];
69
82
  let current = new Vector2(x, y);
70
83
  for (let i = 0; i < options.maxSteps; i++) {
@@ -106,7 +119,12 @@ export class HeatMap {
106
119
  private resolution: number;
107
120
  private data: Float32Array;
108
121
 
109
- constructor(config: { width: number; height: number; resolution: number; kernelRadius: number }) {
122
+ constructor(config: {
123
+ width: number;
124
+ height: number;
125
+ resolution: number;
126
+ kernelRadius: number;
127
+ }) {
110
128
  this.width = Math.max(1, Math.round(config.width / config.resolution));
111
129
  this.height = Math.max(1, Math.round(config.height / config.resolution));
112
130
  this.resolution = config.resolution;
@@ -114,8 +132,14 @@ export class HeatMap {
114
132
  }
115
133
 
116
134
  addPoint(x: number, y: number, value: number): void {
117
- const gridX = Math.min(this.width - 1, Math.max(0, Math.floor(x / this.resolution)));
118
- const gridY = Math.min(this.height - 1, Math.max(0, Math.floor(y / this.resolution)));
135
+ const gridX = Math.min(
136
+ this.width - 1,
137
+ Math.max(0, Math.floor(x / this.resolution))
138
+ );
139
+ const gridY = Math.min(
140
+ this.height - 1,
141
+ Math.max(0, Math.floor(y / this.resolution))
142
+ );
119
143
  const index = gridY * this.width + gridX;
120
144
  this.data[index] += value;
121
145
  }