@fusefactory/fuse-three-forcegraph 1.0.5 → 1.0.6
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/README.md +61 -43
- package/dist/index.d.mts +869 -860
- package/dist/index.mjs +725 -649
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -15,25 +15,33 @@ A high-performance GPU-accelerated force-directed graph visualization library bu
|
|
|
15
15
|
### Core Components
|
|
16
16
|
|
|
17
17
|
#### Engine (`core/Engine.ts`)
|
|
18
|
+
|
|
18
19
|
The main orchestrator that owns and coordinates all components:
|
|
20
|
+
|
|
19
21
|
- Manages shared GPU buffers (SimulationBuffers, StaticAssets, PickBuffer)
|
|
20
22
|
- Coordinates GraphStore, GraphScene, and ForceSimulation
|
|
21
23
|
- Handles the render loop and user interactions
|
|
22
24
|
|
|
23
25
|
#### SimulationBuffers (`textures/SimulationBuffers.ts`)
|
|
26
|
+
|
|
24
27
|
Manages dynamic render targets updated by force simulation:
|
|
28
|
+
|
|
25
29
|
- Position buffers (current, previous, original) for ping-pong rendering
|
|
26
30
|
- Velocity buffers for force accumulation
|
|
27
31
|
- Automatically sizes textures based on node count
|
|
28
32
|
|
|
29
33
|
#### StaticAssets (`textures/StaticAssets.ts`)
|
|
34
|
+
|
|
30
35
|
Manages read-only GPU textures:
|
|
36
|
+
|
|
31
37
|
- Node radii and colors
|
|
32
38
|
- Link indices and properties
|
|
33
39
|
- Created once at initialization, updated only on mode changes
|
|
34
40
|
|
|
35
41
|
#### GraphScene (`rendering/GraphScene.ts`)
|
|
42
|
+
|
|
36
43
|
Manages the 3D scene and visual rendering:
|
|
44
|
+
|
|
37
45
|
- Node and link renderers
|
|
38
46
|
- Camera controls
|
|
39
47
|
- Visual mode application
|
|
@@ -43,7 +51,9 @@ Manages the 3D scene and visual rendering:
|
|
|
43
51
|
The simulation uses a **pass-based architecture** where each force type is implemented as an independent pass:
|
|
44
52
|
|
|
45
53
|
#### BasePass (`simulation/BasePass.ts`)
|
|
54
|
+
|
|
46
55
|
Abstract base class for all force passes. Provides:
|
|
56
|
+
|
|
47
57
|
- Material management
|
|
48
58
|
- Uniform updates
|
|
49
59
|
- Enable/disable control
|
|
@@ -63,7 +73,9 @@ Located in `simulation/passes/`:
|
|
|
63
73
|
8. **IntegratePass** - Updates positions from velocities
|
|
64
74
|
|
|
65
75
|
#### ForceSimulation (`simulation/ForceSimulation.ts`)
|
|
76
|
+
|
|
66
77
|
Manages and executes force passes:
|
|
78
|
+
|
|
67
79
|
```typescript
|
|
68
80
|
// Add custom force pass
|
|
69
81
|
simulation.addPass('myForce', new MyCustomPass(config))
|
|
@@ -121,43 +133,42 @@ engine.setData(graphData)
|
|
|
121
133
|
// Start simulation and rendering
|
|
122
134
|
engine.start()
|
|
123
135
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// Get simulation config - direct access, changes take effect immediately
|
|
131
|
-
const simulation = engine.getSimulation()
|
|
132
|
-
const config = simulation.config
|
|
136
|
+
GRAPH.styleRegistry.setNodeStyles({
|
|
137
|
+
root: { color: 0xE53E3E, size: 55 },
|
|
138
|
+
series: { color: 0x38A169, size: 33 },
|
|
139
|
+
artwork: { color: 0x3182CE, size: 22 },
|
|
140
|
+
})
|
|
133
141
|
|
|
134
|
-
|
|
135
|
-
|
|
142
|
+
// Get simulation config - direct access, changes take effect immediately
|
|
143
|
+
const simulation = engine.getSimulation()
|
|
144
|
+
const config = simulation.config
|
|
136
145
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
simFolder.addBinding(config, 'alpha', { min: 0, max: 1 })
|
|
140
|
-
simFolder.addBinding(config, 'alphaDecay', { min: 0, max: 0.1 })
|
|
141
|
-
simFolder.addBinding(config, 'damping', { min: 0, max: 1 })
|
|
146
|
+
// Create Tweakpane
|
|
147
|
+
pane = new Pane()
|
|
142
148
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
149
|
+
// Bind simulation parameters - changes to config work directly, no sync needed
|
|
150
|
+
const simFolder = pane.addFolder({ title: 'Simulation' })
|
|
151
|
+
simFolder.addBinding(config, 'alpha', { min: 0, max: 1 })
|
|
152
|
+
simFolder.addBinding(config, 'alphaDecay', { min: 0, max: 0.1 })
|
|
153
|
+
simFolder.addBinding(config, 'damping', { min: 0, max: 1 })
|
|
147
154
|
|
|
155
|
+
// Many-body force, check uniforms that can be added in binding...
|
|
156
|
+
const manyBodyFolder = pane.addFolder({ title: 'Many-Body Force' })
|
|
157
|
+
manyBodyFolder.addBinding(config, 'enableManyBody')
|
|
158
|
+
manyBodyFolder.addBinding(config, 'manyBodyStrength', { min: 0, max: 100 })
|
|
148
159
|
|
|
149
|
-
|
|
150
|
-
|
|
160
|
+
// Set up attractors - each pulls specific categories
|
|
161
|
+
simulation.setAttractors([
|
|
151
162
|
{
|
|
152
163
|
id: 'center',
|
|
153
164
|
position: { x: 0, y: 0.0, z: 0 },
|
|
154
|
-
categories: ['root'],
|
|
155
|
-
strength: 55.
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
// Adjust global attractor strength
|
|
159
|
-
simulation.config.attractorStrength = 0.03
|
|
165
|
+
categories: ['root'],
|
|
166
|
+
strength: 55.0
|
|
167
|
+
},
|
|
168
|
+
])
|
|
160
169
|
|
|
170
|
+
// Adjust global attractor strength
|
|
171
|
+
simulation.config.attractorStrength = 0.03
|
|
161
172
|
|
|
162
173
|
```
|
|
163
174
|
|
|
@@ -167,9 +178,9 @@ For testing and prototyping, you can generate random graph data:
|
|
|
167
178
|
|
|
168
179
|
```typescript
|
|
169
180
|
// Utility function to create random nodes and links
|
|
170
|
-
|
|
181
|
+
function createRandomData(nodeCount: number = 10, linkCount: number = 15) {
|
|
171
182
|
const groups = ['root', 'series', 'artwork', 'character', 'location']
|
|
172
|
-
|
|
183
|
+
|
|
173
184
|
// Generate random nodes
|
|
174
185
|
const nodes = Array.from({ length: nodeCount }, (_, i) => ({
|
|
175
186
|
id: (i + 1).toString(),
|
|
@@ -178,23 +189,23 @@ const createRandomData = (nodeCount: number = 10, linkCount: number = 15) => {
|
|
|
178
189
|
y: (Math.random() - 0.5) * 2,
|
|
179
190
|
z: (Math.random() - 0.5) * 2
|
|
180
191
|
}))
|
|
181
|
-
|
|
192
|
+
|
|
182
193
|
// Generate random links
|
|
183
194
|
const links = Array.from({ length: linkCount }, () => {
|
|
184
195
|
const sourceId = Math.floor(Math.random() * nodeCount) + 1
|
|
185
196
|
let targetId = Math.floor(Math.random() * nodeCount) + 1
|
|
186
|
-
|
|
197
|
+
|
|
187
198
|
// Ensure source and target are different
|
|
188
199
|
while (targetId === sourceId) {
|
|
189
200
|
targetId = Math.floor(Math.random() * nodeCount) + 1
|
|
190
201
|
}
|
|
191
|
-
|
|
202
|
+
|
|
192
203
|
return {
|
|
193
204
|
source: sourceId.toString(),
|
|
194
205
|
target: targetId.toString()
|
|
195
206
|
}
|
|
196
207
|
})
|
|
197
|
-
|
|
208
|
+
|
|
198
209
|
return { nodes, links }
|
|
199
210
|
}
|
|
200
211
|
|
|
@@ -205,7 +216,8 @@ engine.setData(createRandomData(100, 70))
|
|
|
205
216
|
### Custom Force Pass
|
|
206
217
|
|
|
207
218
|
```typescript
|
|
208
|
-
import {
|
|
219
|
+
import type { PassContext } from './simulation/BasePass'
|
|
220
|
+
import { BasePass } from './simulation/BasePass'
|
|
209
221
|
|
|
210
222
|
class CustomForcePass extends BasePass {
|
|
211
223
|
private strength: number = 1.0
|
|
@@ -228,12 +240,13 @@ class CustomForcePass extends BasePass {
|
|
|
228
240
|
}
|
|
229
241
|
|
|
230
242
|
updateUniforms(context: PassContext): void {
|
|
231
|
-
if (!this.material)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
243
|
+
if (!this.material)
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
this.material.uniforms.uPositionsTexture.value
|
|
247
|
+
= context.simBuffers.getCurrentPositionTexture()
|
|
248
|
+
this.material.uniforms.uVelocityTexture.value
|
|
249
|
+
= context.simBuffers.getCurrentVelocityTexture()
|
|
237
250
|
this.material.uniforms.uAlpha.value = context.alpha
|
|
238
251
|
this.material.uniforms.uStrength.value = this.strength
|
|
239
252
|
}
|
|
@@ -309,12 +322,14 @@ fuse-three-forcegraph/
|
|
|
309
322
|
## GPU Texture Layout
|
|
310
323
|
|
|
311
324
|
### Position/Velocity Buffers
|
|
325
|
+
|
|
312
326
|
- Format: `RGBA Float`
|
|
313
327
|
- Layout: Grid where each pixel = one node
|
|
314
328
|
- Channels: `(x, y, z, unused)`
|
|
315
329
|
- Size: Next power-of-2 square ≥ √nodeCount
|
|
316
330
|
|
|
317
331
|
### Static Assets
|
|
332
|
+
|
|
318
333
|
- **Radii**: `Red Float` (1 channel)
|
|
319
334
|
- **Colors**: `RGBA Float` (4 channels)
|
|
320
335
|
- **Link Indices**: `RGBA Float` (source_x, source_y, target_x, target_y)
|
|
@@ -332,9 +347,10 @@ fuse-three-forcegraph/
|
|
|
332
347
|
The library implements a three-tiered interaction system for progressive engagement:
|
|
333
348
|
|
|
334
349
|
### 1. Hover
|
|
350
|
+
|
|
335
351
|
- **Trigger**: Mouse cursor enters node boundary
|
|
336
352
|
- **Purpose**: Lightweight preview and visual feedback
|
|
337
|
-
- **Response**:
|
|
353
|
+
- **Response**:
|
|
338
354
|
- Node highlight/glow effect
|
|
339
355
|
- Cursor change
|
|
340
356
|
- Optional tooltip display
|
|
@@ -342,6 +358,7 @@ The library implements a three-tiered interaction system for progressive engagem
|
|
|
342
358
|
- **Use Case**: Quick scanning and exploration
|
|
343
359
|
|
|
344
360
|
### 2. Pop (Dwell/Long Hover)
|
|
361
|
+
|
|
345
362
|
- **Trigger**: Cursor remains over node for defined duration (e.g., 500ms)
|
|
346
363
|
- **Purpose**: Detailed information display without commitment
|
|
347
364
|
- **Response**:
|
|
@@ -352,6 +369,7 @@ The library implements a three-tiered interaction system for progressive engagem
|
|
|
352
369
|
- **Use Case**: Examining node details and immediate connections
|
|
353
370
|
|
|
354
371
|
### 3. Click
|
|
372
|
+
|
|
355
373
|
- **Trigger**: Primary mouse button click on node
|
|
356
374
|
- **Purpose**: Full interaction and state change
|
|
357
375
|
- **Response**:
|
|
@@ -363,6 +381,7 @@ The library implements a three-tiered interaction system for progressive engagem
|
|
|
363
381
|
- **Use Case**: Focused analysis and permanent selection
|
|
364
382
|
|
|
365
383
|
### Interaction Pipeline
|
|
384
|
+
|
|
366
385
|
```typescript
|
|
367
386
|
// Hover
|
|
368
387
|
interactionManager.on('hover', ({ node, position }) => {
|
|
@@ -394,4 +413,3 @@ This progressive disclosure pattern prevents overwhelming users while enabling d
|
|
|
394
413
|
- **three.js** - 3D rendering engine
|
|
395
414
|
- **camera-controls** - Camera manipulation
|
|
396
415
|
- **gsap** - Animation and transitions
|
|
397
|
-
|