-
-
Notifications
You must be signed in to change notification settings - Fork 31
fix(graph): route the four spacetime sliders into d3 forces in non-galaxy mode #177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
434a94c
d700bba
8ff0dbf
8d42016
ee514b7
91e5d0f
fddfd94
573ec4a
f18d5f4
7ecc605
5f0e5d3
c2e79e3
afef952
fe00c88
031ee8f
f1b23d4
90efe82
7f4f1ac
39aa7e8
1e05eb5
d5f4244
8d0bc2e
194cf89
a0b4810
94fafaf
d33977e
5e750cf
e813d24
b995ce4
4e6e936
14fc296
44e4b47
1d26ebf
c1240ac
d63304b
6ebe9fb
6a66f38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,11 @@ | |
| const MAX_CENTROID_GROUPS = 512; | ||
|
|
||
| let model = null; | ||
| let settings = { repel: 48, link: 16, gravity: 48 }; | ||
| let settings = { | ||
| repel: 48, link: 16, gravity: 48, | ||
| gravitationalConstant: 1, blackHoleMass: 1, localGravitationalConstant: 1, | ||
| damping: 1, springStiffness: 1, | ||
| }; | ||
| let generation = 0; | ||
|
|
||
| function post(message) { self.postMessage(message); } | ||
|
|
@@ -217,16 +221,24 @@ | |
| springs run weak — they are visual routes between districts, not licence to drag | ||
| the districts into one another over the settle passes. */ | ||
| const scaledSpacing = SPACING * MAP_SCALE; | ||
| const spring = Number.isFinite(Number(settings.springStiffness)) | ||
| ? Math.max(0, Math.min(100 / 32, Number(settings.springStiffness))) : 1; | ||
| // springStiffness is already a normalized multiplier from the dashboard. Preserve its | ||
| // zero endpoint so the Link spring control can actually disable pair attraction. | ||
| const springScale = spring; | ||
| const rest = Math.max(scaledSpacing * 1.9, Number(settings.link) * 1.6 * (MAP_SCALE * 0.55)); | ||
| for (let edge = 0; edge < model.totalLinks; edge += 1) { | ||
| const a = model.sources[edge], b = model.targets[edge]; | ||
| const ddx = pos[b * 2] - pos[a * 2], ddy = pos[b * 2 + 1] - pos[a * 2 + 1]; | ||
| const dist = Math.sqrt(ddx * ddx + ddy * ddy) || 0.0001; | ||
| const crossCommunity = | ||
| model.communities[a] !== model.communities[b] ? 0.02 : 0.07; | ||
| const force = (dist - rest) / dist * crossCommunity; | ||
| dx[a] -= ddx * force; dy[a] -= ddy * force; | ||
| dx[b] += ddx * force; dy[b] += ddy * force; | ||
| const force = (dist - rest) / dist * crossCommunity * springScale; | ||
| /* `force` is positive when the pair is beyond rest and negative when it overlaps. | ||
| Apply equal-and-opposite corrections along a→b so positive force attracts the pair | ||
| and negative force separates it. */ | ||
| dx[a] += ddx * force; dy[a] += ddy * force; | ||
| dx[b] -= ddx * force; dy[b] -= ddy * force; | ||
| } | ||
|
|
||
| /* Local repulsion through a spatial hash with a per-node visit cap keeps each pass O(n) | ||
|
|
@@ -241,7 +253,11 @@ | |
| } | ||
| const minDist = SPACING * MAP_SCALE * 1.55; | ||
| const minDist2 = minDist * minDist; | ||
| const push = Number(settings.repel) / 48; | ||
| const cohesion = Number.isFinite(Number(settings.localGravitationalConstant)) | ||
| ? Math.max(0, Math.min(2, Number(settings.localGravitationalConstant))) : 1; | ||
| /* Cluster cohesion strengthens the attractive spring network above. Invert its influence | ||
| on the collision-style push so a higher cohesion setting does not spread clusters apart. */ | ||
| const push = Number(settings.repel) / 48 * (1.5 - 0.5 * cohesion); | ||
| for (let index = 0; index < count; index += 1) { | ||
| const gx = Math.floor(pos[index * 2] / cell), gy = Math.floor(pos[index * 2 + 1] / cell); | ||
| let checked = 0; | ||
|
|
@@ -314,14 +330,23 @@ | |
| } | ||
| } | ||
|
|
||
| const gravity = Number(settings.gravity) / 48 * 0.0015; | ||
| const coreAttraction = Number.isFinite(Number(settings.gravitationalConstant)) | ||
| ? Math.max(0, Math.min(2, Number(settings.gravitationalConstant))) : 1; | ||
| const coreMass = Number.isFinite(Number(settings.blackHoleMass)) | ||
| ? Math.max(0, Math.min(2, Number(settings.blackHoleMass))) : 1; | ||
|
Comment on lines
+333
to
+336
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In All Nodes views using Useful? React with 👍 / 👎. |
||
| const gravity = Number(settings.gravity) / 48 * 0.0015 * coreAttraction * coreMass; | ||
| for (let index = 0; index < count; index += 1) { | ||
| dx[index] += (cx - pos[index * 2]) * gravity; | ||
| dy[index] += (cy - pos[index * 2 + 1]) * gravity; | ||
| } | ||
|
|
||
| /* A tight per-pass step cap keeps the settle from smearing district boundaries. */ | ||
| const damp = 0.8, maxStep = SPACING * MAP_SCALE * 0.7; | ||
| const resistance = Number.isFinite(Number(settings.damping)) | ||
| ? Math.max(0, Math.min(15, Number(settings.damping))) : 1; | ||
| /* Keep the full 0..15 control range responsive: the reciprocal curve reaches 0.2 | ||
| at resistance 13, so a 0.2 floor would make the final two slider units inert. */ | ||
| const damp = Math.max(0.15, Math.min(0.95, 0.8 / (0.75 + 0.25 * resistance))); | ||
| const maxStep = SPACING * MAP_SCALE * 0.7; | ||
| for (let index = 0; index < count; index += 1) { | ||
| let vx = dx[index] * damp, vy = dy[index] * damp; | ||
| const speed = Math.sqrt(vx * vx + vy * vy); | ||
|
|
@@ -409,6 +434,15 @@ | |
| repel: Number.isFinite(Number(next.repel)) ? Number(next.repel) : settings.repel, | ||
| link: Number.isFinite(Number(next.link)) ? Number(next.link) : settings.link, | ||
| gravity: Number.isFinite(Number(next.gravity)) ? Number(next.gravity) : settings.gravity, | ||
| gravitationalConstant: Number.isFinite(Number(next.gravitationalConstant)) | ||
| ? Number(next.gravitationalConstant) : settings.gravitationalConstant, | ||
| blackHoleMass: Number.isFinite(Number(next.blackHoleMass)) | ||
| ? Number(next.blackHoleMass) : settings.blackHoleMass, | ||
| localGravitationalConstant: Number.isFinite(Number(next.localGravitationalConstant)) | ||
| ? Number(next.localGravitationalConstant) : settings.localGravitationalConstant, | ||
| damping: Number.isFinite(Number(next.damping)) ? Number(next.damping) : settings.damping, | ||
| springStiffness: Number.isFinite(Number(next.springStiffness)) | ||
| ? Number(next.springStiffness) : settings.springStiffness, | ||
| }; | ||
| if (data.relayout && model) { | ||
| generation += 1; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the All Nodes Every-worker path, when a linked pair is farther apart than
rest,forceis positive but the subsequent updates moveaoppositebandbfarther alongddx, pushing the pair apart; when they are too close, the same signs pull them closer. Multiplying this unstable correction byspringScalemeans raising Link spring amplifies divergence rather than making links settle more firmly at their rest length. Reverse the correction signs before applying the new stiffness multiplier.Useful? React with 👍 / 👎.