I want to make a graph simulation with d3 where I can drag the nodes and move them around, but the length of the links must not be less than 300 or more than 600, how can I approach this?
Here is my simulation:
const simulation = d3
.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength(-300))
.force(
"link",
d3
.forceLink(links)
.id((d) => d.id)
.distance(300),
)
.force(
"center",
d3.forceCenter(
container.node().clientWidth / 2 - 75,
container.node().clientHeight / 2 - 75,
),
);
For now I could only set the constat length of a link.
I tried to search in the d3 documentation, but didn't find anything, if there is alerady such a problem with described solution, I would be glad to see it.
Thank you!
I believe you'll have to implement your own force since none of the built-in forces support your use case. For example, forceLink.distance() only initializes the distances once.
A custom implementation would look something like this (see live demo):
function linkForce(links, {
min = 50, max = 200, strength = .5, id = d => d.id
} = {}) {
const clamp = (a, b, v) => v <= a ? a : v >= b ? b : v;
const update = (alpha) => {
const s = .5 * alpha * strength;
for(const {source: a, target: b} of links) {
const dx = a.x + a.vx - b.x - b.vy;
const dy = a.y + a.vy - b.y - b.vy;
const dn = Math.hypot(dx, dy);
const dc = clamp(min, max, dn);
if(dn === dc) continue;
const f = (dc - dn) / dn * .5 * alpha * strength;
a.vx += dx * f;
a.vy += dy * f;
b.vx -= dx * f;
b.vy -= dy * f;
}
};
const initialize = nodes => {
const map = new Map(nodes.map(d => [id(d), d]));
for(const link of links) {
link.source = map.get(link.source);
link.target = map.get(link.target);
}
};
return Object.assign(update, {initialize});
}