@buley/hexgrid-3d 3.1.0 → 3.2.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/dist/HexGridEnhanced.d.ts +15 -0
- package/dist/HexGridEnhanced.d.ts.map +1 -0
- package/dist/HexGridEnhanced.js +1 -0
- package/dist/Snapshot.d.ts +594 -0
- package/dist/Snapshot.d.ts.map +1 -0
- package/dist/Snapshot.js +757 -0
- package/dist/adapters/DashAdapter.d.ts +18 -0
- package/dist/adapters/DashAdapter.d.ts.map +1 -0
- package/dist/adapters/DashAdapter.js +42 -0
- package/dist/adapters.d.ts +53 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +14 -0
- package/dist/algorithms/AdvancedStatistics.d.ts +52 -0
- package/dist/algorithms/AdvancedStatistics.d.ts.map +1 -0
- package/dist/algorithms/AdvancedStatistics.js +307 -0
- package/dist/algorithms/BayesianStatistics.d.ts +86 -0
- package/dist/algorithms/BayesianStatistics.d.ts.map +1 -0
- package/dist/algorithms/BayesianStatistics.js +263 -0
- package/dist/algorithms/FlowField.d.ts +55 -0
- package/dist/algorithms/FlowField.d.ts.map +1 -0
- package/dist/algorithms/FlowField.js +80 -0
- package/dist/algorithms/FlowField3D.d.ts +166 -0
- package/dist/algorithms/FlowField3D.d.ts.map +1 -0
- package/dist/algorithms/FlowField3D.js +327 -0
- package/dist/algorithms/FluidEngineFactory.d.ts +15 -0
- package/dist/algorithms/FluidEngineFactory.d.ts.map +1 -0
- package/dist/algorithms/FluidEngineFactory.js +41 -0
- package/dist/algorithms/FluidSimulation.d.ts +41 -0
- package/dist/algorithms/FluidSimulation.d.ts.map +1 -0
- package/dist/algorithms/FluidSimulation.js +74 -0
- package/dist/algorithms/FluidSimulation3D.d.ts +137 -0
- package/dist/algorithms/FluidSimulation3D.d.ts.map +1 -0
- package/dist/algorithms/FluidSimulation3D.js +464 -0
- package/dist/algorithms/FluidSimulation3DGPU.d.ts +41 -0
- package/dist/algorithms/FluidSimulation3DGPU.d.ts.map +1 -0
- package/dist/algorithms/FluidSimulation3DGPU.js +328 -0
- package/dist/algorithms/FluidSimulationWebNN.d.ts +56 -0
- package/dist/algorithms/FluidSimulationWebNN.d.ts.map +1 -0
- package/dist/algorithms/FluidSimulationWebNN.js +84 -0
- package/dist/algorithms/GraphAlgorithms.d.ts +48 -0
- package/dist/algorithms/GraphAlgorithms.d.ts.map +1 -0
- package/dist/algorithms/GraphAlgorithms.js +122 -0
- package/dist/algorithms/OutlierDetection.d.ts +49 -0
- package/dist/algorithms/OutlierDetection.d.ts.map +1 -0
- package/dist/algorithms/OutlierDetection.js +284 -0
- package/dist/algorithms/ParticleSystem.d.ts +36 -0
- package/dist/algorithms/ParticleSystem.d.ts.map +1 -0
- package/dist/algorithms/ParticleSystem.js +59 -0
- package/dist/algorithms/ParticleSystem3D.d.ts +206 -0
- package/dist/algorithms/ParticleSystem3D.d.ts.map +1 -0
- package/dist/algorithms/ParticleSystem3D.js +371 -0
- package/dist/algorithms/index.d.ts +16 -0
- package/dist/algorithms/index.d.ts.map +1 -0
- package/{src/algorithms/index.ts → dist/algorithms/index.js} +0 -2
- package/dist/compat.d.ts +24 -0
- package/dist/compat.d.ts.map +1 -0
- package/dist/compat.js +88 -0
- package/dist/components/HexGrid.d.ts +5 -0
- package/dist/components/HexGrid.d.ts.map +1 -0
- package/dist/components/HexGrid.js +39 -0
- package/dist/components/NarrationOverlay.d.ts +16 -0
- package/dist/components/NarrationOverlay.d.ts.map +1 -0
- package/dist/components/NarrationOverlay.js +132 -0
- package/{src/components/index.ts → dist/components/index.d.ts} +1 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +1 -0
- package/dist/features.d.ts +54 -0
- package/dist/features.d.ts.map +1 -0
- package/dist/features.js +74 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/{src/index.ts → dist/index.js} +0 -9
- package/dist/lib/narration.d.ts +12 -0
- package/dist/lib/narration.d.ts.map +1 -0
- package/dist/lib/narration.js +8 -0
- package/dist/lib/stats-tracker.d.ts +7 -0
- package/dist/lib/stats-tracker.d.ts.map +1 -0
- package/dist/lib/stats-tracker.js +22 -0
- package/dist/lib/theme-colors.d.ts +7 -0
- package/dist/lib/theme-colors.d.ts.map +1 -0
- package/dist/lib/theme-colors.js +10 -0
- package/dist/math/HexCoordinates.d.ts +140 -0
- package/dist/math/HexCoordinates.d.ts.map +1 -0
- package/dist/math/HexCoordinates.js +741 -0
- package/dist/math/Matrix4.d.ts +9 -0
- package/dist/math/Matrix4.d.ts.map +1 -0
- package/dist/math/Matrix4.js +19 -0
- package/dist/math/Quaternion.d.ts +11 -0
- package/dist/math/Quaternion.d.ts.map +1 -0
- package/dist/math/Quaternion.js +23 -0
- package/dist/math/SpatialIndex.d.ts +34 -0
- package/dist/math/SpatialIndex.d.ts.map +1 -0
- package/dist/math/SpatialIndex.js +75 -0
- package/dist/math/Vector3.d.ts +110 -0
- package/dist/math/Vector3.d.ts.map +1 -0
- package/dist/math/Vector3.js +426 -0
- package/dist/math/index.d.ts +11 -0
- package/dist/math/index.d.ts.map +1 -0
- package/{src/math/index.ts → dist/math/index.js} +0 -1
- package/dist/note-adapter.d.ts +44 -0
- package/dist/note-adapter.d.ts.map +1 -0
- package/dist/note-adapter.js +86 -0
- package/dist/ontology-adapter.d.ts +13 -0
- package/dist/ontology-adapter.d.ts.map +1 -0
- package/dist/ontology-adapter.js +65 -0
- package/dist/stores/index.d.ts +2 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/uiStore.d.ts +18 -0
- package/dist/stores/uiStore.d.ts.map +1 -0
- package/dist/stores/uiStore.js +77 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types.d.ts +126 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/utils/image-utils.d.ts +13 -0
- package/dist/utils/image-utils.d.ts.map +1 -0
- package/dist/utils/image-utils.js +23 -0
- package/dist/wasm/HexGridWasmWrapper.d.ts +131 -0
- package/dist/wasm/HexGridWasmWrapper.d.ts.map +1 -0
- package/dist/wasm/HexGridWasmWrapper.js +610 -0
- package/dist/wasm/index.d.ts +7 -0
- package/dist/wasm/index.d.ts.map +1 -0
- package/{src/wasm/index.ts → dist/wasm/index.js} +0 -1
- package/dist/webgpu/WebGPUContext.d.ts +20 -0
- package/dist/webgpu/WebGPUContext.d.ts.map +1 -0
- package/dist/webgpu/WebGPUContext.js +60 -0
- package/dist/webnn/WebNNContext.d.ts +38 -0
- package/dist/webnn/WebNNContext.d.ts.map +1 -0
- package/dist/webnn/WebNNContext.js +66 -0
- package/dist/workers/hexgrid-math.d.ts +79 -0
- package/dist/workers/hexgrid-math.d.ts.map +1 -0
- package/dist/workers/hexgrid-math.js +136 -0
- package/dist/workers/hexgrid-worker.worker.d.ts +35 -0
- package/dist/workers/hexgrid-worker.worker.d.ts.map +1 -0
- package/dist/workers/hexgrid-worker.worker.js +2014 -0
- package/package.json +20 -7
- package/.eslintrc.json +0 -28
- package/build_log.txt +0 -500
- package/build_src_log.txt +0 -8
- package/examples/basic-usage.tsx +0 -52
- package/public/hexgrid-worker.js +0 -2475
- package/rust/Cargo.toml +0 -41
- package/rust/src/lib.rs +0 -740
- package/rust/src/math.rs +0 -574
- package/rust/src/spatial.rs +0 -245
- package/rust/src/statistics.rs +0 -496
- package/site/.eslintrc.json +0 -3
- package/site/DEPLOYMENT.md +0 -196
- package/site/INDEX.md +0 -127
- package/site/QUICK_START.md +0 -86
- package/site/README.md +0 -85
- package/site/SITE_SUMMARY.md +0 -180
- package/site/next.config.js +0 -12
- package/site/package.json +0 -26
- package/site/src/app/docs/page.tsx +0 -272
- package/site/src/app/examples/page.tsx +0 -151
- package/site/src/app/globals.css +0 -160
- package/site/src/app/layout.tsx +0 -39
- package/site/src/app/page.tsx +0 -235
- package/site/tsconfig.json +0 -29
- package/site/vercel.json +0 -6
- package/src/HexGridEnhanced.ts +0 -16
- package/src/Snapshot.ts +0 -1607
- package/src/adapters/DashAdapter.ts +0 -57
- package/src/adapters.ts +0 -63
- package/src/algorithms/AdvancedStatistics.ts +0 -362
- package/src/algorithms/BayesianStatistics.ts +0 -348
- package/src/algorithms/FlowField.ts +0 -150
- package/src/algorithms/FlowField3D.ts +0 -573
- package/src/algorithms/FluidEngineFactory.ts +0 -44
- package/src/algorithms/FluidSimulation.ts +0 -115
- package/src/algorithms/FluidSimulation3D.ts +0 -664
- package/src/algorithms/FluidSimulation3DGPU.ts +0 -408
- package/src/algorithms/FluidSimulationWebNN.ts +0 -141
- package/src/algorithms/GraphAlgorithms.ts +0 -191
- package/src/algorithms/OutlierDetection.ts +0 -425
- package/src/algorithms/ParticleSystem.ts +0 -95
- package/src/algorithms/ParticleSystem3D.ts +0 -567
- package/src/compat.ts +0 -96
- package/src/components/HexGrid.tsx +0 -61
- package/src/components/NarrationOverlay.tsx +0 -309
- package/src/features.ts +0 -125
- package/src/lib/narration.ts +0 -17
- package/src/lib/stats-tracker.ts +0 -25
- package/src/lib/theme-colors.ts +0 -12
- package/src/math/HexCoordinates.ts +0 -863
- package/src/math/Matrix4.ts +0 -25
- package/src/math/Quaternion.ts +0 -37
- package/src/math/SpatialIndex.ts +0 -114
- package/src/math/Vector3.ts +0 -540
- package/src/note-adapter.ts +0 -132
- package/src/ontology-adapter.ts +0 -84
- package/src/stores/uiStore.ts +0 -85
- package/src/types/index.ts +0 -3
- package/src/types/shared-utils.d.ts +0 -10
- package/src/types/wgsl.d.ts +0 -4
- package/src/types.ts +0 -164
- package/src/utils/image-utils.ts +0 -28
- package/src/wasm/HexGridWasmWrapper.ts +0 -801
- package/src/webgpu/WebGPUContext.ts +0 -71
- package/src/webgpu/shaders/fluid_sim.wgsl +0 -140
- package/src/webnn/WebNNContext.ts +0 -99
- package/src/workers/hexgrid-math.ts +0 -182
- package/src/workers/hexgrid-worker.worker.ts +0 -2781
- package/tsconfig.json +0 -26
- /package/{src/stores/index.ts → dist/stores/index.js} +0 -0
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
export interface Cluster {
|
|
2
|
-
centroid: number[];
|
|
3
|
-
members: number[];
|
|
4
|
-
cohesion: number;
|
|
5
|
-
separation: number;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface VoronoiCell {
|
|
9
|
-
siteIndex: number;
|
|
10
|
-
site: [number, number];
|
|
11
|
-
vertices: Array<[number, number]>;
|
|
12
|
-
neighbors: number[];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface VoronoiDiagram {
|
|
16
|
-
cells: VoronoiCell[];
|
|
17
|
-
vertices: Array<[number, number]>;
|
|
18
|
-
edges: Array<[[number, number], [number, number]]>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function kMeansClustering(points: number[][], k: number): Cluster[] {
|
|
22
|
-
if (points.length === 0 || k <= 0) return [];
|
|
23
|
-
const clusters: Cluster[] = Array.from({ length: k }, () => ({
|
|
24
|
-
centroid: new Array(points[0]?.length || 0).fill(0),
|
|
25
|
-
members: [],
|
|
26
|
-
cohesion: 0,
|
|
27
|
-
separation: 0,
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
points.forEach((_point, index) => {
|
|
31
|
-
clusters[index % k].members.push(index);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
for (const cluster of clusters) {
|
|
35
|
-
if (cluster.members.length === 0) continue;
|
|
36
|
-
const centroid = cluster.centroid.map((_, dim) => {
|
|
37
|
-
const sum = cluster.members.reduce(
|
|
38
|
-
(acc, memberIdx) => acc + (points[memberIdx]?.[dim] ?? 0),
|
|
39
|
-
0
|
|
40
|
-
);
|
|
41
|
-
return sum / cluster.members.length;
|
|
42
|
-
});
|
|
43
|
-
cluster.centroid = centroid;
|
|
44
|
-
cluster.cohesion =
|
|
45
|
-
cluster.members.reduce((sum, idx) => {
|
|
46
|
-
const point = points[idx];
|
|
47
|
-
const distance = Math.sqrt(
|
|
48
|
-
centroid.reduce((acc, value, dim) => {
|
|
49
|
-
const diff = value - (point?.[dim] ?? 0);
|
|
50
|
-
return acc + diff * diff;
|
|
51
|
-
}, 0)
|
|
52
|
-
);
|
|
53
|
-
return sum + distance;
|
|
54
|
-
}, 0) / cluster.members.length;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return clusters;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function dbscan(
|
|
61
|
-
points: number[][],
|
|
62
|
-
_eps: number,
|
|
63
|
-
_minPoints: number
|
|
64
|
-
): Cluster[] {
|
|
65
|
-
if (points.length === 0) return [];
|
|
66
|
-
return kMeansClustering(points, Math.min(1, points.length));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function computeVoronoi(
|
|
70
|
-
sites: Array<[number, number]>,
|
|
71
|
-
bounds: { minX: number; maxX: number; minY: number; maxY: number }
|
|
72
|
-
): VoronoiDiagram {
|
|
73
|
-
const vertices: Array<[number, number]> = [
|
|
74
|
-
[bounds.minX, bounds.minY],
|
|
75
|
-
[bounds.maxX, bounds.minY],
|
|
76
|
-
[bounds.maxX, bounds.maxY],
|
|
77
|
-
[bounds.minX, bounds.maxY],
|
|
78
|
-
];
|
|
79
|
-
|
|
80
|
-
const cells: VoronoiCell[] = sites.map((site, index) => ({
|
|
81
|
-
siteIndex: index,
|
|
82
|
-
site,
|
|
83
|
-
vertices,
|
|
84
|
-
neighbors: sites.map((_s, i) => i).filter((i) => i !== index),
|
|
85
|
-
}));
|
|
86
|
-
|
|
87
|
-
const edges: Array<[[number, number], [number, number]]> = [];
|
|
88
|
-
for (let i = 0; i < vertices.length; i++) {
|
|
89
|
-
const start = vertices[i];
|
|
90
|
-
const end = vertices[(i + 1) % vertices.length];
|
|
91
|
-
edges.push([start, end]);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return { cells, vertices, edges };
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function analyzeTerritorBoundaries(
|
|
98
|
-
infections: Map<number, { photoId: string }>,
|
|
99
|
-
neighbors: number[][]
|
|
100
|
-
): {
|
|
101
|
-
frontLength: Map<string, number>;
|
|
102
|
-
hotspots: number[];
|
|
103
|
-
} {
|
|
104
|
-
const frontLength = new Map<string, number>();
|
|
105
|
-
const hotspots: number[] = [];
|
|
106
|
-
|
|
107
|
-
infections.forEach((value, idx) => {
|
|
108
|
-
const neighborList = neighbors[idx] ?? [];
|
|
109
|
-
const different = neighborList.filter(
|
|
110
|
-
(neighborIdx) => infections.get(neighborIdx)?.photoId !== value.photoId
|
|
111
|
-
);
|
|
112
|
-
if (different.length > 0) {
|
|
113
|
-
hotspots.push(idx);
|
|
114
|
-
}
|
|
115
|
-
const current = frontLength.get(value.photoId) ?? 0;
|
|
116
|
-
frontLength.set(value.photoId, current + different.length);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
return { frontLength, hotspots };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function kMeansClustering2D(
|
|
123
|
-
points: Array<[number, number]>,
|
|
124
|
-
k: number
|
|
125
|
-
): Cluster[] {
|
|
126
|
-
const asPoints = points.map((p) => [p[0], p[1]]);
|
|
127
|
-
return kMeansClustering(asPoints, k);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function findConnectedComponents(graph: {
|
|
131
|
-
nodes: number[];
|
|
132
|
-
edges: Map<number, number[]>;
|
|
133
|
-
}): number[][] {
|
|
134
|
-
const visited = new Set<number>();
|
|
135
|
-
const components: number[][] = [];
|
|
136
|
-
|
|
137
|
-
for (const node of graph.nodes) {
|
|
138
|
-
if (visited.has(node)) continue;
|
|
139
|
-
const stack = [node];
|
|
140
|
-
const component: number[] = [];
|
|
141
|
-
|
|
142
|
-
while (stack.length > 0) {
|
|
143
|
-
const current = stack.pop();
|
|
144
|
-
if (current === undefined || visited.has(current)) continue;
|
|
145
|
-
visited.add(current);
|
|
146
|
-
component.push(current);
|
|
147
|
-
const neighbors = graph.edges.get(current) ?? [];
|
|
148
|
-
for (const neighbor of neighbors) {
|
|
149
|
-
if (!visited.has(neighbor)) {
|
|
150
|
-
stack.push(neighbor);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
components.push(component);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return components;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function louvainCommunities(graph: {
|
|
162
|
-
nodes: number[];
|
|
163
|
-
edges: Map<number, number[]>;
|
|
164
|
-
weights?: Map<string, number>;
|
|
165
|
-
}): Array<{ members: number[]; modularity: number }> {
|
|
166
|
-
const components = findConnectedComponents(graph);
|
|
167
|
-
return components.map((members) => ({
|
|
168
|
-
members,
|
|
169
|
-
modularity: members.length / Math.max(1, graph.nodes.length),
|
|
170
|
-
}));
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export function computeVoronoiGraph(sites: Array<[number, number]>) {
|
|
174
|
-
return computeVoronoi(sites, {
|
|
175
|
-
minX: Math.min(...sites.map((s) => s[0])),
|
|
176
|
-
maxX: Math.max(...sites.map((s) => s[0])),
|
|
177
|
-
minY: Math.min(...sites.map((s) => s[1])),
|
|
178
|
-
maxY: Math.max(...sites.map((s) => s[1])),
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function kMeansCluster(points: number[][], k: number): Cluster[] {
|
|
183
|
-
return kMeansClustering(points, k);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export function kMeansClusteringWithLabels(
|
|
187
|
-
points: number[][],
|
|
188
|
-
k: number
|
|
189
|
-
): Cluster[] {
|
|
190
|
-
return kMeansClustering(points, k);
|
|
191
|
-
}
|
|
@@ -1,425 +0,0 @@
|
|
|
1
|
-
export interface OutlierStats {
|
|
2
|
-
mean: number;
|
|
3
|
-
stdDev: number;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface OutlierResult {
|
|
7
|
-
outlierIndices: number[];
|
|
8
|
-
scores: number[];
|
|
9
|
-
stats: OutlierStats;
|
|
10
|
-
threshold: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface TimeSeriesAnomaly {
|
|
14
|
-
index: number;
|
|
15
|
-
isAnomaly: boolean;
|
|
16
|
-
zScore: number;
|
|
17
|
-
expectedValue: number;
|
|
18
|
-
actualValue: number;
|
|
19
|
-
confidence: number;
|
|
20
|
-
anomalyType: 'spike' | 'drop' | 'change';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface VarianceChangeResult {
|
|
24
|
-
index: number;
|
|
25
|
-
ratio: number;
|
|
26
|
-
beforeVariance: number;
|
|
27
|
-
afterVariance: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function mean(values: number[]): number {
|
|
31
|
-
if (values.length === 0) return 0;
|
|
32
|
-
return values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function stdDev(values: number[], avg: number): number {
|
|
36
|
-
if (values.length === 0) return 0;
|
|
37
|
-
const variance =
|
|
38
|
-
values.reduce((sum, value) => sum + (value - avg) ** 2, 0) / values.length;
|
|
39
|
-
return Math.sqrt(variance);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function detectOutliersZScore(
|
|
43
|
-
values: number[],
|
|
44
|
-
threshold: number = 3
|
|
45
|
-
): OutlierResult {
|
|
46
|
-
const avg = mean(values);
|
|
47
|
-
const deviation = stdDev(values, avg);
|
|
48
|
-
const scores = values.map((value) =>
|
|
49
|
-
deviation === 0 ? 0 : (value - avg) / deviation
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
const outlierIndices = scores
|
|
53
|
-
.map((score, index) => ({ score, index }))
|
|
54
|
-
.filter(({ score }) => Math.abs(score) >= threshold)
|
|
55
|
-
.map(({ index }) => index);
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
outlierIndices,
|
|
59
|
-
scores,
|
|
60
|
-
stats: { mean: avg, stdDev: deviation },
|
|
61
|
-
threshold,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function detectOutliersModifiedZScore(
|
|
66
|
-
values: number[],
|
|
67
|
-
threshold: number = 3.5
|
|
68
|
-
): OutlierResult {
|
|
69
|
-
if (values.length === 0) {
|
|
70
|
-
return {
|
|
71
|
-
outlierIndices: [],
|
|
72
|
-
scores: [],
|
|
73
|
-
stats: { mean: 0, stdDev: 0 },
|
|
74
|
-
threshold,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
79
|
-
const median = sorted[Math.floor(sorted.length / 2)] ?? 0;
|
|
80
|
-
const deviations = values.map((value) => Math.abs(value - median));
|
|
81
|
-
const sortedDeviations = [...deviations].sort((a, b) => a - b);
|
|
82
|
-
const mad = sortedDeviations[Math.floor(sortedDeviations.length / 2)] || 0;
|
|
83
|
-
|
|
84
|
-
const scores = values.map((value) =>
|
|
85
|
-
mad === 0 ? 0 : (0.6745 * (value - median)) / mad
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
const outlierIndices = scores
|
|
89
|
-
.map((score, index) => ({ score, index }))
|
|
90
|
-
.filter(({ score }) => Math.abs(score) >= threshold)
|
|
91
|
-
.map(({ index }) => index);
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
outlierIndices,
|
|
95
|
-
scores,
|
|
96
|
-
stats: { mean: median, stdDev: mad },
|
|
97
|
-
threshold,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function detectOutliersIQR(
|
|
102
|
-
values: number[],
|
|
103
|
-
threshold: number = 1.5
|
|
104
|
-
): OutlierResult {
|
|
105
|
-
if (values.length === 0) {
|
|
106
|
-
return {
|
|
107
|
-
outlierIndices: [],
|
|
108
|
-
scores: [],
|
|
109
|
-
stats: { mean: 0, stdDev: 0 },
|
|
110
|
-
threshold,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
115
|
-
const q1 = sorted[Math.floor(sorted.length * 0.25)] ?? 0;
|
|
116
|
-
const q3 = sorted[Math.floor(sorted.length * 0.75)] ?? 0;
|
|
117
|
-
const iqr = q3 - q1;
|
|
118
|
-
|
|
119
|
-
const lowerBound = q1 - threshold * iqr;
|
|
120
|
-
const upperBound = q3 + threshold * iqr;
|
|
121
|
-
|
|
122
|
-
const outlierIndices = values
|
|
123
|
-
.map((value, index) => ({ value, index }))
|
|
124
|
-
.filter(({ value }) => value < lowerBound || value > upperBound)
|
|
125
|
-
.map(({ index }) => index);
|
|
126
|
-
|
|
127
|
-
const avg = mean(values);
|
|
128
|
-
const deviation = stdDev(values, avg);
|
|
129
|
-
const scores = values.map((value) =>
|
|
130
|
-
deviation === 0 ? 0 : (value - avg) / deviation
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
outlierIndices,
|
|
135
|
-
scores,
|
|
136
|
-
stats: { mean: avg, stdDev: deviation },
|
|
137
|
-
threshold,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function detectGrowthSpikes(values: number[]): TimeSeriesAnomaly[] {
|
|
142
|
-
if (values.length < 2) return [];
|
|
143
|
-
|
|
144
|
-
const diffs = values.slice(1).map((value, index) => value - values[index]);
|
|
145
|
-
const diffStats = detectOutliersZScore(diffs, 2.5);
|
|
146
|
-
|
|
147
|
-
return diffs.map((diff, index) => {
|
|
148
|
-
const score = diffStats.scores[index] ?? 0;
|
|
149
|
-
const isAnomaly = Math.abs(score) >= 2.5;
|
|
150
|
-
return {
|
|
151
|
-
index: index + 1,
|
|
152
|
-
isAnomaly,
|
|
153
|
-
zScore: Math.abs(score),
|
|
154
|
-
expectedValue: values[index],
|
|
155
|
-
actualValue: values[index + 1],
|
|
156
|
-
confidence: Math.min(0.99, Math.abs(score) / 4),
|
|
157
|
-
anomalyType: diff >= 0 ? 'spike' : 'drop',
|
|
158
|
-
};
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function detectVarianceChanges(
|
|
163
|
-
values: number[]
|
|
164
|
-
): VarianceChangeResult[] {
|
|
165
|
-
if (values.length < 6) return [];
|
|
166
|
-
const results: VarianceChangeResult[] = [];
|
|
167
|
-
const windowSize = Math.max(3, Math.floor(values.length / 4));
|
|
168
|
-
|
|
169
|
-
for (let i = windowSize; i < values.length - windowSize; i++) {
|
|
170
|
-
const before = values.slice(i - windowSize, i);
|
|
171
|
-
const after = values.slice(i, i + windowSize);
|
|
172
|
-
const beforeMean = mean(before);
|
|
173
|
-
const afterMean = mean(after);
|
|
174
|
-
const beforeVar = stdDev(before, beforeMean) ** 2;
|
|
175
|
-
const afterVar = stdDev(after, afterMean) ** 2;
|
|
176
|
-
const ratio =
|
|
177
|
-
beforeVar === 0 ? (afterVar === 0 ? 1 : Infinity) : afterVar / beforeVar;
|
|
178
|
-
|
|
179
|
-
results.push({
|
|
180
|
-
index: i,
|
|
181
|
-
ratio,
|
|
182
|
-
beforeVariance: beforeVar,
|
|
183
|
-
afterVariance: afterVar,
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return results;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export function mahalanobisOutliers(
|
|
191
|
-
values: number[],
|
|
192
|
-
threshold: number = 3
|
|
193
|
-
): OutlierResult {
|
|
194
|
-
return detectOutliersZScore(values, threshold);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Game Anomaly Detection
|
|
198
|
-
export interface GameAnomaly {
|
|
199
|
-
index: number;
|
|
200
|
-
type: 'sudden_change' | 'pattern_break' | 'statistical_outlier';
|
|
201
|
-
severity: number;
|
|
202
|
-
description: string;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
export function detectGameAnomalies(
|
|
206
|
-
values: number[],
|
|
207
|
-
windowSize: number = 5
|
|
208
|
-
): GameAnomaly[] {
|
|
209
|
-
const anomalies: GameAnomaly[] = [];
|
|
210
|
-
if (values.length < windowSize * 2) return anomalies;
|
|
211
|
-
|
|
212
|
-
for (let i = windowSize; i < values.length - windowSize; i++) {
|
|
213
|
-
const before = values.slice(i - windowSize, i);
|
|
214
|
-
const after = values.slice(i, i + windowSize);
|
|
215
|
-
const beforeMean = before.reduce((s, v) => s + v, 0) / before.length;
|
|
216
|
-
const afterMean = after.reduce((s, v) => s + v, 0) / after.length;
|
|
217
|
-
const change = Math.abs(afterMean - beforeMean);
|
|
218
|
-
const threshold =
|
|
219
|
-
before.reduce((s, v) => s + Math.abs(v - beforeMean), 0) / before.length;
|
|
220
|
-
|
|
221
|
-
if (change > threshold * 2) {
|
|
222
|
-
anomalies.push({
|
|
223
|
-
index: i,
|
|
224
|
-
type: 'sudden_change',
|
|
225
|
-
severity: Math.min(1, change / (threshold || 1)),
|
|
226
|
-
description: `Sudden change detected at index ${i}`,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return anomalies;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Comprehensive Outlier Analysis
|
|
235
|
-
export interface MultivariateOutlierResult {
|
|
236
|
-
outliers: number[];
|
|
237
|
-
scores: number[];
|
|
238
|
-
method: string;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
export function comprehensiveOutlierAnalysis(
|
|
242
|
-
values: number[]
|
|
243
|
-
): MultivariateOutlierResult {
|
|
244
|
-
const zScore = detectOutliersZScore(values);
|
|
245
|
-
const iqr = detectOutliersIQR(values);
|
|
246
|
-
const combined = new Set([...zScore.outlierIndices, ...iqr.outlierIndices]);
|
|
247
|
-
|
|
248
|
-
return {
|
|
249
|
-
outliers: Array.from(combined),
|
|
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
|
|
257
|
-
),
|
|
258
|
-
method: 'comprehensive',
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Local Outlier Factor (simplified)
|
|
263
|
-
export function localOutlierFactor(
|
|
264
|
-
values: number[],
|
|
265
|
-
k: number = 5
|
|
266
|
-
): OutlierResult {
|
|
267
|
-
// Simplified LOF implementation
|
|
268
|
-
if (values.length < k + 1) {
|
|
269
|
-
return {
|
|
270
|
-
outlierIndices: [],
|
|
271
|
-
scores: values.map(() => 0),
|
|
272
|
-
stats: { mean: 0, stdDev: 0 },
|
|
273
|
-
threshold: 1.5,
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const scores: number[] = [];
|
|
278
|
-
for (let i = 0; i < values.length; i++) {
|
|
279
|
-
const distances = values
|
|
280
|
-
.map((v, j) => (i === j ? Infinity : Math.abs(v - values[i]!)))
|
|
281
|
-
.sort((a, b) => a - b);
|
|
282
|
-
const kthDistance = distances[k] ?? 0;
|
|
283
|
-
const reachability = values.map((v, j) =>
|
|
284
|
-
Math.max(kthDistance, Math.abs(v - values[i]!))
|
|
285
|
-
);
|
|
286
|
-
const lrd = k / reachability.reduce((s, r) => s + r, 0);
|
|
287
|
-
scores.push(lrd);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const avgLrd = scores.reduce((s, lrd) => s + lrd, 0) / scores.length;
|
|
291
|
-
const lofScores = scores.map((lrd) => (lrd === 0 ? 0 : avgLrd / lrd));
|
|
292
|
-
const threshold = 1.5;
|
|
293
|
-
const outlierIndices = lofScores
|
|
294
|
-
.map((score, idx) => ({ score, idx }))
|
|
295
|
-
.filter(({ score }) => score > threshold)
|
|
296
|
-
.map(({ idx }) => idx);
|
|
297
|
-
|
|
298
|
-
const avg = values.reduce((s, v) => s + v, 0) / values.length;
|
|
299
|
-
const variance =
|
|
300
|
-
values.reduce((s, v) => s + Math.pow(v - avg, 2), 0) / values.length;
|
|
301
|
-
const stdDev = Math.sqrt(variance);
|
|
302
|
-
|
|
303
|
-
return {
|
|
304
|
-
outlierIndices,
|
|
305
|
-
scores: lofScores,
|
|
306
|
-
stats: { mean: avg, stdDev },
|
|
307
|
-
threshold,
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Isolation Forest (simplified)
|
|
312
|
-
export function isolationForest(
|
|
313
|
-
values: number[],
|
|
314
|
-
trees: number = 100,
|
|
315
|
-
samples: number = 256
|
|
316
|
-
): OutlierResult {
|
|
317
|
-
// Simplified isolation forest
|
|
318
|
-
const scores: number[] = [];
|
|
319
|
-
const sampleSize = Math.min(samples, values.length);
|
|
320
|
-
|
|
321
|
-
for (let i = 0; i < values.length; i++) {
|
|
322
|
-
let pathLengthSum = 0;
|
|
323
|
-
for (let t = 0; t < trees; t++) {
|
|
324
|
-
const sample = [];
|
|
325
|
-
for (let s = 0; s < sampleSize; s++) {
|
|
326
|
-
sample.push(values[Math.floor(Math.random() * values.length)] ?? 0);
|
|
327
|
-
}
|
|
328
|
-
const min = Math.min(...sample);
|
|
329
|
-
const max = Math.max(...sample);
|
|
330
|
-
const range = max - min || 1;
|
|
331
|
-
const normalized = (values[i]! - min) / range;
|
|
332
|
-
pathLengthSum += Math.log2(Math.max(1, normalized * sampleSize));
|
|
333
|
-
}
|
|
334
|
-
const avgPathLength = pathLengthSum / trees;
|
|
335
|
-
const anomalyScore = Math.pow(2, -avgPathLength / Math.log2(sampleSize));
|
|
336
|
-
scores.push(anomalyScore);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const threshold = 0.5;
|
|
340
|
-
const outlierIndices = scores
|
|
341
|
-
.map((score, idx) => ({ score, idx }))
|
|
342
|
-
.filter(({ score }) => score > threshold)
|
|
343
|
-
.map(({ idx }) => idx);
|
|
344
|
-
|
|
345
|
-
const avg = values.reduce((s, v) => s + v, 0) / values.length;
|
|
346
|
-
const variance =
|
|
347
|
-
values.reduce((s, v) => s + Math.pow(v - avg, 2), 0) / values.length;
|
|
348
|
-
const stdDev = Math.sqrt(variance);
|
|
349
|
-
|
|
350
|
-
return {
|
|
351
|
-
outlierIndices,
|
|
352
|
-
scores,
|
|
353
|
-
stats: { mean: avg, stdDev },
|
|
354
|
-
threshold,
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// CUSUM Chart
|
|
359
|
-
export function cusumChart(
|
|
360
|
-
values: number[],
|
|
361
|
-
target: number,
|
|
362
|
-
h: number = 5,
|
|
363
|
-
k: number = 0.5
|
|
364
|
-
): TimeSeriesAnomaly[] {
|
|
365
|
-
const anomalies: TimeSeriesAnomaly[] = [];
|
|
366
|
-
let sPos = 0;
|
|
367
|
-
let sNeg = 0;
|
|
368
|
-
|
|
369
|
-
for (let i = 0; i < values.length; i++) {
|
|
370
|
-
const deviation = values[i]! - target;
|
|
371
|
-
sPos = Math.max(0, sPos + deviation - k);
|
|
372
|
-
sNeg = Math.max(0, sNeg - deviation - k);
|
|
373
|
-
|
|
374
|
-
if (sPos > h || sNeg > h) {
|
|
375
|
-
anomalies.push({
|
|
376
|
-
index: i,
|
|
377
|
-
isAnomaly: true,
|
|
378
|
-
zScore: Math.max(sPos, sNeg) / h,
|
|
379
|
-
expectedValue: target,
|
|
380
|
-
actualValue: values[i]!,
|
|
381
|
-
confidence: Math.min(0.99, Math.max(sPos, sNeg) / (h * 2)),
|
|
382
|
-
anomalyType: sPos > sNeg ? 'spike' : 'drop',
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
return anomalies;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// EWMA Chart (Exponentially Weighted Moving Average)
|
|
391
|
-
export function ewmaChart(
|
|
392
|
-
values: number[],
|
|
393
|
-
lambda: number = 0.2,
|
|
394
|
-
lcl: number = -3,
|
|
395
|
-
ucl: number = 3
|
|
396
|
-
): TimeSeriesAnomaly[] {
|
|
397
|
-
const anomalies: TimeSeriesAnomaly[] = [];
|
|
398
|
-
if (values.length === 0) return anomalies;
|
|
399
|
-
|
|
400
|
-
let ewma = values[0]!;
|
|
401
|
-
const mean = values.reduce((s, v) => s + v, 0) / values.length;
|
|
402
|
-
const variance =
|
|
403
|
-
values.reduce((s, v) => s + Math.pow(v - mean, 2), 0) / values.length;
|
|
404
|
-
const stdDev = Math.sqrt(variance);
|
|
405
|
-
const ewmaStdDev = stdDev * Math.sqrt(lambda / (2 - lambda));
|
|
406
|
-
|
|
407
|
-
for (let i = 1; i < values.length; i++) {
|
|
408
|
-
ewma = lambda * values[i]! + (1 - lambda) * ewma;
|
|
409
|
-
const zScore = (ewma - mean) / (ewmaStdDev || 1);
|
|
410
|
-
|
|
411
|
-
if (zScore < lcl || zScore > ucl) {
|
|
412
|
-
anomalies.push({
|
|
413
|
-
index: i,
|
|
414
|
-
isAnomaly: true,
|
|
415
|
-
zScore: Math.abs(zScore),
|
|
416
|
-
expectedValue: mean,
|
|
417
|
-
actualValue: values[i]!,
|
|
418
|
-
confidence: Math.min(0.99, Math.abs(zScore) / 5),
|
|
419
|
-
anomalyType: zScore > 0 ? 'spike' : 'drop',
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
return anomalies;
|
|
425
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { Vector2 } from '../math/Vector3';
|
|
2
|
-
|
|
3
|
-
type ParticleEffect = 'birth' | 'trail' | 'victory' | 'sparkle';
|
|
4
|
-
|
|
5
|
-
export interface ParticleConfig {
|
|
6
|
-
maxParticles: number;
|
|
7
|
-
gravity: Vector2;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface Particle {
|
|
11
|
-
x: number;
|
|
12
|
-
y: number;
|
|
13
|
-
size: number;
|
|
14
|
-
color: string;
|
|
15
|
-
alpha: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class ParticleSystem {
|
|
19
|
-
private particles: Particle[] = [];
|
|
20
|
-
|
|
21
|
-
addParticle(particle: Particle): void {
|
|
22
|
-
this.particles.push(particle);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
update(_deltaTime: number): void {
|
|
26
|
-
// No-op for stub
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
getPositions(): Particle[] {
|
|
30
|
-
return this.particles;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
clear(): void {
|
|
34
|
-
this.particles = [];
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export class ParticleEffectManager {
|
|
39
|
-
private systems: Map<string, ParticleSystem> = new Map();
|
|
40
|
-
private config: ParticleConfig;
|
|
41
|
-
|
|
42
|
-
constructor(config: ParticleConfig) {
|
|
43
|
-
this.config = config;
|
|
44
|
-
this.systems.set('default', new ParticleSystem());
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
triggerEffect(
|
|
48
|
-
effect: ParticleEffect,
|
|
49
|
-
position: Vector2,
|
|
50
|
-
options?: {
|
|
51
|
-
count?: number;
|
|
52
|
-
color?: [number, number, number];
|
|
53
|
-
velocity?: Vector2;
|
|
54
|
-
}
|
|
55
|
-
): void {
|
|
56
|
-
const system = this.systems.get('default');
|
|
57
|
-
if (!system) return;
|
|
58
|
-
const count = options?.count ?? 1;
|
|
59
|
-
const color = options?.color ?? [1, 1, 1];
|
|
60
|
-
for (let i = 0; i < count; i++) {
|
|
61
|
-
system.addParticle({
|
|
62
|
-
x: position.x,
|
|
63
|
-
y: position.y,
|
|
64
|
-
size: 4,
|
|
65
|
-
color: `rgb(${Math.round(color[0] * 255)}, ${Math.round(
|
|
66
|
-
color[1] * 255
|
|
67
|
-
)}, ${Math.round(color[2] * 255)})`,
|
|
68
|
-
alpha: 1,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
update(deltaTime: number): void {
|
|
74
|
-
for (const system of this.systems.values()) {
|
|
75
|
-
system.update(deltaTime);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
getSystem(name: string): ParticleSystem | undefined {
|
|
80
|
-
return this.systems.get(name);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
clearAll(): void {
|
|
84
|
-
for (const system of this.systems.values()) {
|
|
85
|
-
system.clear();
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export const ParticlePresets: Record<string, ParticleEffect> = {
|
|
91
|
-
birth: 'birth',
|
|
92
|
-
trail: 'trail',
|
|
93
|
-
victory: 'victory',
|
|
94
|
-
sparkle: 'sparkle',
|
|
95
|
-
};
|