@buley/hexgrid-3d 1.0.0 → 1.1.0
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/examples/basic-usage.tsx +19 -19
- package/package.json +1 -1
- package/public/hexgrid-worker.js +2350 -1638
- package/src/Snapshot.ts +790 -585
- package/src/adapters.ts +16 -18
- package/src/algorithms/AdvancedStatistics.ts +58 -24
- package/src/algorithms/BayesianStatistics.ts +43 -12
- package/src/algorithms/FlowField.ts +30 -6
- package/src/algorithms/FlowField3D.ts +573 -0
- package/src/algorithms/FluidSimulation.ts +19 -3
- package/src/algorithms/FluidSimulation3D.ts +664 -0
- package/src/algorithms/GraphAlgorithms.ts +19 -12
- package/src/algorithms/OutlierDetection.ts +72 -38
- package/src/algorithms/ParticleSystem.ts +12 -2
- package/src/algorithms/ParticleSystem3D.ts +546 -0
- package/src/algorithms/index.ts +14 -8
- package/src/compat.ts +10 -10
- package/src/components/HexGrid.tsx +10 -23
- package/src/components/NarrationOverlay.tsx +139 -51
- package/src/components/index.ts +2 -1
- package/src/features.ts +31 -31
- package/src/index.ts +11 -11
- package/src/math/HexCoordinates.ts +1 -1
- package/src/math/Matrix4.ts +2 -12
- package/src/math/Vector3.ts +5 -1
- package/src/math/index.ts +6 -6
- package/src/note-adapter.ts +50 -42
- package/src/ontology-adapter.ts +30 -23
- package/src/stores/uiStore.ts +34 -34
- package/src/types.ts +109 -98
- package/src/utils/image-utils.ts +9 -6
- package/src/wasm/HexGridWasmWrapper.ts +436 -388
- package/src/wasm/index.ts +2 -2
- package/src/workers/hexgrid-math.ts +40 -35
- package/src/workers/hexgrid-worker.worker.ts +1992 -1018
|
@@ -159,7 +159,9 @@ export function detectGrowthSpikes(values: number[]): TimeSeriesAnomaly[] {
|
|
|
159
159
|
});
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
export function detectVarianceChanges(
|
|
162
|
+
export function detectVarianceChanges(
|
|
163
|
+
values: number[]
|
|
164
|
+
): VarianceChangeResult[] {
|
|
163
165
|
if (values.length < 6) return [];
|
|
164
166
|
const results: VarianceChangeResult[] = [];
|
|
165
167
|
const windowSize = Math.max(3, Math.floor(values.length / 4));
|
|
@@ -171,7 +173,8 @@ export function detectVarianceChanges(values: number[]): VarianceChangeResult[]
|
|
|
171
173
|
const afterMean = mean(after);
|
|
172
174
|
const beforeVar = stdDev(before, beforeMean) ** 2;
|
|
173
175
|
const afterVar = stdDev(after, afterMean) ** 2;
|
|
174
|
-
const ratio =
|
|
176
|
+
const ratio =
|
|
177
|
+
beforeVar === 0 ? (afterVar === 0 ? 1 : Infinity) : afterVar / beforeVar;
|
|
175
178
|
|
|
176
179
|
results.push({
|
|
177
180
|
index: i,
|
|
@@ -199,18 +202,22 @@ export interface GameAnomaly {
|
|
|
199
202
|
description: string;
|
|
200
203
|
}
|
|
201
204
|
|
|
202
|
-
export function detectGameAnomalies(
|
|
205
|
+
export function detectGameAnomalies(
|
|
206
|
+
values: number[],
|
|
207
|
+
windowSize: number = 5
|
|
208
|
+
): GameAnomaly[] {
|
|
203
209
|
const anomalies: GameAnomaly[] = [];
|
|
204
210
|
if (values.length < windowSize * 2) return anomalies;
|
|
205
|
-
|
|
211
|
+
|
|
206
212
|
for (let i = windowSize; i < values.length - windowSize; i++) {
|
|
207
213
|
const before = values.slice(i - windowSize, i);
|
|
208
214
|
const after = values.slice(i, i + windowSize);
|
|
209
215
|
const beforeMean = before.reduce((s, v) => s + v, 0) / before.length;
|
|
210
216
|
const afterMean = after.reduce((s, v) => s + v, 0) / after.length;
|
|
211
217
|
const change = Math.abs(afterMean - beforeMean);
|
|
212
|
-
const threshold =
|
|
213
|
-
|
|
218
|
+
const threshold =
|
|
219
|
+
before.reduce((s, v) => s + Math.abs(v - beforeMean), 0) / before.length;
|
|
220
|
+
|
|
214
221
|
if (change > threshold * 2) {
|
|
215
222
|
anomalies.push({
|
|
216
223
|
index: i,
|
|
@@ -220,7 +227,7 @@ export function detectGameAnomalies(values: number[], windowSize: number = 5): G
|
|
|
220
227
|
});
|
|
221
228
|
}
|
|
222
229
|
}
|
|
223
|
-
|
|
230
|
+
|
|
224
231
|
return anomalies;
|
|
225
232
|
}
|
|
226
233
|
|
|
@@ -231,22 +238,32 @@ export interface MultivariateOutlierResult {
|
|
|
231
238
|
method: string;
|
|
232
239
|
}
|
|
233
240
|
|
|
234
|
-
export function comprehensiveOutlierAnalysis(
|
|
241
|
+
export function comprehensiveOutlierAnalysis(
|
|
242
|
+
values: number[]
|
|
243
|
+
): MultivariateOutlierResult {
|
|
235
244
|
const zScore = detectOutliersZScore(values);
|
|
236
245
|
const iqr = detectOutliersIQR(values);
|
|
237
246
|
const combined = new Set([...zScore.outlierIndices, ...iqr.outlierIndices]);
|
|
238
|
-
|
|
247
|
+
|
|
239
248
|
return {
|
|
240
249
|
outliers: Array.from(combined),
|
|
241
|
-
scores: values.map((v, i) =>
|
|
242
|
-
combined.has(i)
|
|
250
|
+
scores: values.map((v, i) =>
|
|
251
|
+
combined.has(i)
|
|
252
|
+
? Math.max(
|
|
253
|
+
Math.abs(zScore.scores[i] ?? 0),
|
|
254
|
+
Math.abs(iqr.scores[i] ?? 0)
|
|
255
|
+
)
|
|
256
|
+
: 0
|
|
243
257
|
),
|
|
244
258
|
method: 'comprehensive',
|
|
245
259
|
};
|
|
246
260
|
}
|
|
247
261
|
|
|
248
262
|
// Local Outlier Factor (simplified)
|
|
249
|
-
export function localOutlierFactor(
|
|
263
|
+
export function localOutlierFactor(
|
|
264
|
+
values: number[],
|
|
265
|
+
k: number = 5
|
|
266
|
+
): OutlierResult {
|
|
250
267
|
// Simplified LOF implementation
|
|
251
268
|
if (values.length < k + 1) {
|
|
252
269
|
return {
|
|
@@ -256,32 +273,33 @@ export function localOutlierFactor(values: number[], k: number = 5): OutlierResu
|
|
|
256
273
|
threshold: 1.5,
|
|
257
274
|
};
|
|
258
275
|
}
|
|
259
|
-
|
|
276
|
+
|
|
260
277
|
const scores: number[] = [];
|
|
261
278
|
for (let i = 0; i < values.length; i++) {
|
|
262
|
-
const distances = values
|
|
263
|
-
i === j ? Infinity : Math.abs(v - values[i]!)
|
|
264
|
-
|
|
279
|
+
const distances = values
|
|
280
|
+
.map((v, j) => (i === j ? Infinity : Math.abs(v - values[i]!)))
|
|
281
|
+
.sort((a, b) => a - b);
|
|
265
282
|
const kthDistance = distances[k] ?? 0;
|
|
266
|
-
const reachability = values.map((v, j) =>
|
|
283
|
+
const reachability = values.map((v, j) =>
|
|
267
284
|
Math.max(kthDistance, Math.abs(v - values[i]!))
|
|
268
285
|
);
|
|
269
286
|
const lrd = k / reachability.reduce((s, r) => s + r, 0);
|
|
270
287
|
scores.push(lrd);
|
|
271
288
|
}
|
|
272
|
-
|
|
289
|
+
|
|
273
290
|
const avgLrd = scores.reduce((s, lrd) => s + lrd, 0) / scores.length;
|
|
274
|
-
const lofScores = scores.map(lrd => lrd === 0 ? 0 : avgLrd / lrd);
|
|
291
|
+
const lofScores = scores.map((lrd) => (lrd === 0 ? 0 : avgLrd / lrd));
|
|
275
292
|
const threshold = 1.5;
|
|
276
293
|
const outlierIndices = lofScores
|
|
277
294
|
.map((score, idx) => ({ score, idx }))
|
|
278
295
|
.filter(({ score }) => score > threshold)
|
|
279
296
|
.map(({ idx }) => idx);
|
|
280
|
-
|
|
297
|
+
|
|
281
298
|
const avg = values.reduce((s, v) => s + v, 0) / values.length;
|
|
282
|
-
const variance =
|
|
299
|
+
const variance =
|
|
300
|
+
values.reduce((s, v) => s + Math.pow(v - avg, 2), 0) / values.length;
|
|
283
301
|
const stdDev = Math.sqrt(variance);
|
|
284
|
-
|
|
302
|
+
|
|
285
303
|
return {
|
|
286
304
|
outlierIndices,
|
|
287
305
|
scores: lofScores,
|
|
@@ -291,11 +309,15 @@ export function localOutlierFactor(values: number[], k: number = 5): OutlierResu
|
|
|
291
309
|
}
|
|
292
310
|
|
|
293
311
|
// Isolation Forest (simplified)
|
|
294
|
-
export function isolationForest(
|
|
312
|
+
export function isolationForest(
|
|
313
|
+
values: number[],
|
|
314
|
+
trees: number = 100,
|
|
315
|
+
samples: number = 256
|
|
316
|
+
): OutlierResult {
|
|
295
317
|
// Simplified isolation forest
|
|
296
318
|
const scores: number[] = [];
|
|
297
319
|
const sampleSize = Math.min(samples, values.length);
|
|
298
|
-
|
|
320
|
+
|
|
299
321
|
for (let i = 0; i < values.length; i++) {
|
|
300
322
|
let pathLengthSum = 0;
|
|
301
323
|
for (let t = 0; t < trees; t++) {
|
|
@@ -313,17 +335,18 @@ export function isolationForest(values: number[], trees: number = 100, samples:
|
|
|
313
335
|
const anomalyScore = Math.pow(2, -avgPathLength / Math.log2(sampleSize));
|
|
314
336
|
scores.push(anomalyScore);
|
|
315
337
|
}
|
|
316
|
-
|
|
338
|
+
|
|
317
339
|
const threshold = 0.5;
|
|
318
340
|
const outlierIndices = scores
|
|
319
341
|
.map((score, idx) => ({ score, idx }))
|
|
320
342
|
.filter(({ score }) => score > threshold)
|
|
321
343
|
.map(({ idx }) => idx);
|
|
322
|
-
|
|
344
|
+
|
|
323
345
|
const avg = values.reduce((s, v) => s + v, 0) / values.length;
|
|
324
|
-
const variance =
|
|
346
|
+
const variance =
|
|
347
|
+
values.reduce((s, v) => s + Math.pow(v - avg, 2), 0) / values.length;
|
|
325
348
|
const stdDev = Math.sqrt(variance);
|
|
326
|
-
|
|
349
|
+
|
|
327
350
|
return {
|
|
328
351
|
outlierIndices,
|
|
329
352
|
scores,
|
|
@@ -333,16 +356,21 @@ export function isolationForest(values: number[], trees: number = 100, samples:
|
|
|
333
356
|
}
|
|
334
357
|
|
|
335
358
|
// CUSUM Chart
|
|
336
|
-
export function cusumChart(
|
|
359
|
+
export function cusumChart(
|
|
360
|
+
values: number[],
|
|
361
|
+
target: number,
|
|
362
|
+
h: number = 5,
|
|
363
|
+
k: number = 0.5
|
|
364
|
+
): TimeSeriesAnomaly[] {
|
|
337
365
|
const anomalies: TimeSeriesAnomaly[] = [];
|
|
338
366
|
let sPos = 0;
|
|
339
367
|
let sNeg = 0;
|
|
340
|
-
|
|
368
|
+
|
|
341
369
|
for (let i = 0; i < values.length; i++) {
|
|
342
370
|
const deviation = values[i]! - target;
|
|
343
371
|
sPos = Math.max(0, sPos + deviation - k);
|
|
344
372
|
sNeg = Math.max(0, sNeg - deviation - k);
|
|
345
|
-
|
|
373
|
+
|
|
346
374
|
if (sPos > h || sNeg > h) {
|
|
347
375
|
anomalies.push({
|
|
348
376
|
index: i,
|
|
@@ -355,25 +383,31 @@ export function cusumChart(values: number[], target: number, h: number = 5, k: n
|
|
|
355
383
|
});
|
|
356
384
|
}
|
|
357
385
|
}
|
|
358
|
-
|
|
386
|
+
|
|
359
387
|
return anomalies;
|
|
360
388
|
}
|
|
361
389
|
|
|
362
390
|
// EWMA Chart (Exponentially Weighted Moving Average)
|
|
363
|
-
export function ewmaChart(
|
|
391
|
+
export function ewmaChart(
|
|
392
|
+
values: number[],
|
|
393
|
+
lambda: number = 0.2,
|
|
394
|
+
lcl: number = -3,
|
|
395
|
+
ucl: number = 3
|
|
396
|
+
): TimeSeriesAnomaly[] {
|
|
364
397
|
const anomalies: TimeSeriesAnomaly[] = [];
|
|
365
398
|
if (values.length === 0) return anomalies;
|
|
366
|
-
|
|
399
|
+
|
|
367
400
|
let ewma = values[0]!;
|
|
368
401
|
const mean = values.reduce((s, v) => s + v, 0) / values.length;
|
|
369
|
-
const variance =
|
|
402
|
+
const variance =
|
|
403
|
+
values.reduce((s, v) => s + Math.pow(v - mean, 2), 0) / values.length;
|
|
370
404
|
const stdDev = Math.sqrt(variance);
|
|
371
405
|
const ewmaStdDev = stdDev * Math.sqrt(lambda / (2 - lambda));
|
|
372
|
-
|
|
406
|
+
|
|
373
407
|
for (let i = 1; i < values.length; i++) {
|
|
374
408
|
ewma = lambda * values[i]! + (1 - lambda) * ewma;
|
|
375
409
|
const zScore = (ewma - mean) / (ewmaStdDev || 1);
|
|
376
|
-
|
|
410
|
+
|
|
377
411
|
if (zScore < lcl || zScore > ucl) {
|
|
378
412
|
anomalies.push({
|
|
379
413
|
index: i,
|
|
@@ -386,6 +420,6 @@ export function ewmaChart(values: number[], lambda: number = 0.2, lcl: number =
|
|
|
386
420
|
});
|
|
387
421
|
}
|
|
388
422
|
}
|
|
389
|
-
|
|
423
|
+
|
|
390
424
|
return anomalies;
|
|
391
425
|
}
|
|
@@ -44,7 +44,15 @@ export class ParticleEffectManager {
|
|
|
44
44
|
this.systems.set('default', new ParticleSystem());
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
triggerEffect(
|
|
47
|
+
triggerEffect(
|
|
48
|
+
effect: ParticleEffect,
|
|
49
|
+
position: Vector2,
|
|
50
|
+
options?: {
|
|
51
|
+
count?: number;
|
|
52
|
+
color?: [number, number, number];
|
|
53
|
+
velocity?: Vector2;
|
|
54
|
+
}
|
|
55
|
+
): void {
|
|
48
56
|
const system = this.systems.get('default');
|
|
49
57
|
if (!system) return;
|
|
50
58
|
const count = options?.count ?? 1;
|
|
@@ -54,7 +62,9 @@ export class ParticleEffectManager {
|
|
|
54
62
|
x: position.x,
|
|
55
63
|
y: position.y,
|
|
56
64
|
size: 4,
|
|
57
|
-
color: `rgb(${Math.round(color[0] * 255)}, ${Math.round(
|
|
65
|
+
color: `rgb(${Math.round(color[0] * 255)}, ${Math.round(
|
|
66
|
+
color[1] * 255
|
|
67
|
+
)}, ${Math.round(color[2] * 255)})`,
|
|
58
68
|
alpha: 1,
|
|
59
69
|
});
|
|
60
70
|
}
|