@xingwangzhe/force-rs 0.2.2 → 0.2.3
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 +54 -35
- package/force-rs.darwin-arm64.node +0 -0
- package/force-rs.darwin-x64.node +0 -0
- package/force-rs.linux-arm64-gnu.node +0 -0
- package/force-rs.linux-x64-gnu.node +0 -0
- package/force-rs.linux-x64-musl.node +0 -0
- package/force-rs.win32-x64-msvc.node +0 -0
- package/package.json +29 -27
package/README.md
CHANGED
|
@@ -10,12 +10,12 @@ Fast force-directed 3D graph layout powered by **Rust + a contiguous Barnes-Hut
|
|
|
10
10
|
|
|
11
11
|
### Force model (fully d3-force compatible)
|
|
12
12
|
|
|
13
|
-
| Force
|
|
14
|
-
|
|
15
|
-
| Many-body repulsion
|
|
16
|
-
| Link (spring)
|
|
17
|
-
| Center (translational) | Shift centroid toward origin (same as d3-force `forceCenter`)
|
|
18
|
-
| Distance softening
|
|
13
|
+
| Force | Implementation |
|
|
14
|
+
| ---------------------- | ---------------------------------------------------------------------- |
|
|
15
|
+
| Many-body repulsion | Barnes-Hut octree, O(n log n), with `√(4/k)` correction |
|
|
16
|
+
| Link (spring) | Degree-biased strength: `1/min(deg, deg)`, bias = other's degree ratio |
|
|
17
|
+
| Center (translational) | Shift centroid toward origin (same as d3-force `forceCenter`) |
|
|
18
|
+
| Distance softening | Smooth `√(d_min² × d²)` below 1.0 (same as d3-force) |
|
|
19
19
|
|
|
20
20
|
### Other features
|
|
21
21
|
|
|
@@ -36,15 +36,15 @@ npm install @xingwangzhe/force-rs
|
|
|
36
36
|
Execute one tick of force-directed simulation.
|
|
37
37
|
|
|
38
38
|
```ts
|
|
39
|
-
import { simTick } from
|
|
39
|
+
import { simTick } from "@xingwangzhe/force-rs";
|
|
40
40
|
|
|
41
41
|
const opts = {
|
|
42
|
-
repulsion: 3000,
|
|
43
|
-
linkDistance: 500,
|
|
44
|
-
centerStrength: 0.005
|
|
45
|
-
theta: 0.8,
|
|
46
|
-
velocityDecay: 0.
|
|
47
|
-
alphaDecay: 0.02,
|
|
42
|
+
repulsion: 3000, // many-body charge strength
|
|
43
|
+
linkDistance: 500, // natural spring length
|
|
44
|
+
centerStrength: 0.005, // translational center gravity
|
|
45
|
+
theta: 0.8, // Barnes-Hut approximation threshold
|
|
46
|
+
velocityDecay: 0.6, // velocity damping per tick
|
|
47
|
+
alphaDecay: 0.02, // cooling rate per tick
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
// state: n*6 + 1 floats [x0,y0,z0,vx0,vy0,vz0, ..., alpha]
|
|
@@ -55,7 +55,7 @@ const newState = simTick(state, links, n, opts);
|
|
|
55
55
|
For repeated ticks, preprocess links once and use typed arrays:
|
|
56
56
|
|
|
57
57
|
```ts
|
|
58
|
-
import { createSimulation } from
|
|
58
|
+
import { createSimulation } from "@xingwangzhe/force-rs";
|
|
59
59
|
|
|
60
60
|
const simulation = createSimulation(new Uint32Array(links), n);
|
|
61
61
|
const nextState = simulation.tick(new Float64Array(state), opts);
|
|
@@ -63,18 +63,18 @@ const nextState = simulation.tick(new Float64Array(state), opts);
|
|
|
63
63
|
|
|
64
64
|
**ForceOptions:**
|
|
65
65
|
|
|
66
|
-
| Field
|
|
67
|
-
|
|
68
|
-
| `repulsion` | number | Many-body charge strength
|
|
69
|
-
| `linkDistance` | number | Natural spring length
|
|
70
|
-
| `centerStrength` | number | Translational center gravity
|
|
71
|
-
| `theta` | number | Barnes-Hut approximation threshold
|
|
72
|
-
| `velocityDecay` | number | Velocity damping factor per tick
|
|
73
|
-
| `alphaDecay` | number | Alpha cooling rate per tick
|
|
66
|
+
| Field | Type | Description |
|
|
67
|
+
| ---------------- | ------ | -------------------------------------------------------------------- |
|
|
68
|
+
| `repulsion` | number | Many-body charge strength |
|
|
69
|
+
| `linkDistance` | number | Natural spring length |
|
|
70
|
+
| `centerStrength` | number | Translational center gravity |
|
|
71
|
+
| `theta` | number | Barnes-Hut approximation threshold |
|
|
72
|
+
| `velocityDecay` | number | Velocity damping factor per tick |
|
|
73
|
+
| `alphaDecay` | number | Alpha cooling rate per tick |
|
|
74
74
|
| `algorithm` | string | `fast` (default), `linear` experimental, or `legacy` comparison path |
|
|
75
|
-
| `distanceMax` | number | Optional finite repulsion radius
|
|
76
|
-
| `algorithm` | string | `fast` (default) or `legacy` comparison path
|
|
77
|
-
| `distanceMax` | number | Optional finite repulsion radius
|
|
75
|
+
| `distanceMax` | number | Optional finite repulsion radius |
|
|
76
|
+
| `algorithm` | string | `fast` (default) or `legacy` comparison path |
|
|
77
|
+
| `distanceMax` | number | Optional finite repulsion radius |
|
|
78
78
|
|
|
79
79
|
**Returns:** `Array<number>` for `simTick`, or `Float64Array` for the prepared typed-array API.
|
|
80
80
|
|
|
@@ -83,14 +83,29 @@ The default `fast` path uses a reusable SoA state layout, precomputed CSR links,
|
|
|
83
83
|
## Usage Example
|
|
84
84
|
|
|
85
85
|
```ts
|
|
86
|
-
import { simTick } from
|
|
86
|
+
import { simTick } from "@xingwangzhe/force-rs";
|
|
87
87
|
|
|
88
88
|
// 3 nodes: positions + velocities + alpha
|
|
89
89
|
const state = [
|
|
90
|
-
0,
|
|
91
|
-
|
|
92
|
-
0,
|
|
93
|
-
|
|
90
|
+
0,
|
|
91
|
+
0,
|
|
92
|
+
0,
|
|
93
|
+
0,
|
|
94
|
+
0,
|
|
95
|
+
0, // node 0
|
|
96
|
+
100,
|
|
97
|
+
0,
|
|
98
|
+
0,
|
|
99
|
+
0,
|
|
100
|
+
0,
|
|
101
|
+
0, // node 1
|
|
102
|
+
0,
|
|
103
|
+
100,
|
|
104
|
+
0,
|
|
105
|
+
0,
|
|
106
|
+
0,
|
|
107
|
+
0, // node 2
|
|
108
|
+
1.0, // alpha
|
|
94
109
|
];
|
|
95
110
|
const links = [0, 1, 0, 2]; // node 0 connected to 1 and 2
|
|
96
111
|
|
|
@@ -99,7 +114,7 @@ const opts = {
|
|
|
99
114
|
linkDistance: 500,
|
|
100
115
|
centerStrength: 0.005,
|
|
101
116
|
theta: 0.8,
|
|
102
|
-
velocityDecay: 0.
|
|
117
|
+
velocityDecay: 0.6,
|
|
103
118
|
alphaDecay: 0.02,
|
|
104
119
|
};
|
|
105
120
|
|
|
@@ -112,13 +127,17 @@ while (s[s.length - 1] > 0.001) {
|
|
|
112
127
|
|
|
113
128
|
## Real-world Performance
|
|
114
129
|
|
|
115
|
-
| Platform | 47K nodes × 89K edges | Notes
|
|
116
|
-
|
|
117
|
-
| 16-core | **~0.01s/tick**
|
|
118
|
-
| 1-core | ~0.15s/tick
|
|
130
|
+
| Platform | 47K nodes × 89K edges | Notes |
|
|
131
|
+
| -------- | --------------------- | ---------------------------------- |
|
|
132
|
+
| 16-core | **~0.01s/tick** | Rayon `par_iter` across 16 threads |
|
|
133
|
+
| 1-core | ~0.15s/tick | Auto-fallback to sequential |
|
|
119
134
|
|
|
120
135
|
Measured on AMD EPYC / Intel Xeon build servers. The Barnes-Hut tree is custom-built in `src/lib.rs` (no external tree crate dependency); nodes are stored contiguously to reduce pointer chasing and allocator pressure.
|
|
121
136
|
|
|
122
137
|
## License
|
|
123
138
|
|
|
124
139
|
MIT
|
|
140
|
+
|
|
141
|
+
## Runtime-focused release builds
|
|
142
|
+
|
|
143
|
+
Native CI artifacts use full LTO, one code-generation unit, and O3, while preserving the existing CPU instruction baseline. CI trains a profile-guided optimization (PGO) candidate using representative graph workloads and publishes it only when the runtime comparison passes; otherwise it publishes the unprofiled release. `bun run build:pgo` produces the optimized addon; `bun run benchmark:pgo` compares it with an unprofiled release. Install `llvm-tools-preview` with the same Rust toolchain first. Details and measurement limits: [scripts/PERFORMANCE.md](scripts/PERFORMANCE.md).
|
|
Binary file
|
package/force-rs.darwin-x64.node
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,32 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xingwangzhe/force-rs",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Fast force-directed 3D graph layout powered by Rust + Barnes-Hut octree. Fully compatible with d3-force-3d force model (repulsion, spring with correct degree-biased strength, translational center gravity). Parallelized with Rayon — 47K nodes × 89K edges in ~0.01s/tick on 16 cores.",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"types": "index.d.ts",
|
|
7
|
-
"license": "MIT",
|
|
8
|
-
"author": "xingwangzhe",
|
|
9
|
-
"repository": {
|
|
10
|
-
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/xingwangzhe/force-rs.git"
|
|
12
|
-
},
|
|
13
5
|
"keywords": [
|
|
14
|
-
"
|
|
6
|
+
"3d",
|
|
15
7
|
"barnes-hut",
|
|
16
8
|
"d3-force",
|
|
17
9
|
"d3-force-3d",
|
|
18
|
-
"napi-rs",
|
|
19
|
-
"rust",
|
|
20
|
-
"graph-layout",
|
|
21
10
|
"force-directed",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
11
|
+
"force-layout",
|
|
12
|
+
"graph-layout",
|
|
13
|
+
"napi-rs",
|
|
14
|
+
"octree",
|
|
15
|
+
"rust"
|
|
24
16
|
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "xingwangzhe",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/xingwangzhe/force-rs.git"
|
|
22
|
+
},
|
|
25
23
|
"files": [
|
|
26
24
|
"index.d.ts",
|
|
27
25
|
"index.js",
|
|
28
26
|
"force-rs.*.node"
|
|
29
27
|
],
|
|
28
|
+
"main": "index.js",
|
|
29
|
+
"types": "index.d.ts",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "napi build --platform --release",
|
|
32
|
+
"test": "node --test force.test.mjs",
|
|
33
|
+
"benchmark": "node benchmark.mjs",
|
|
34
|
+
"lint": "oxlint",
|
|
35
|
+
"artifacts": "napi artifacts",
|
|
36
|
+
"build:pgo": "node scripts/build-pgo.mjs",
|
|
37
|
+
"benchmark:pgo": "node scripts/build-pgo.mjs --compare"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@napi-rs/cli": "^3.10.5",
|
|
41
|
+
"oxlint": "^1.85.0",
|
|
42
|
+
"typescript": "^7.0.2"
|
|
43
|
+
},
|
|
30
44
|
"napi": {
|
|
31
45
|
"binaryName": "force-rs",
|
|
32
46
|
"targets": [
|
|
@@ -40,17 +54,5 @@
|
|
|
40
54
|
},
|
|
41
55
|
"engines": {
|
|
42
56
|
"node": ">= 18"
|
|
43
|
-
},
|
|
44
|
-
"scripts": {
|
|
45
|
-
"build": "napi build --platform --release",
|
|
46
|
-
"test": "node --test force.test.mjs",
|
|
47
|
-
"benchmark": "node benchmark.mjs",
|
|
48
|
-
"lint": "oxlint",
|
|
49
|
-
"artifacts": "napi artifacts"
|
|
50
|
-
},
|
|
51
|
-
"devDependencies": {
|
|
52
|
-
"@napi-rs/cli": "^3.10.5",
|
|
53
|
-
"oxlint": "^1.85.0",
|
|
54
|
-
"typescript": "^7.0.2"
|
|
55
57
|
}
|
|
56
58
|
}
|