Open belukha opened 1 year ago
Not sure if this is a bug or works as intended—in any case, forceCollide doesn't check if the nodes are fixed or not, see https://github.com/d3/d3-force/blob/475ec96f1afad3bab5442d614af67fa0ce7c9785/src/collide.js#L53
Maybe try to change some parameters, by making the collision force slower, and solving in many more ticks:
.strength(0.03);
simulation.tick(1000)
It looks good for this demo, but I think I can't use it in my project. Because I call collision resolution simulation on every zoom event and 1000 iterations on each such event call is too much and laggy. Do you have any ideas how can I patch forceCollide
to make it consider fixed positions?
You'd need to make two additional tests, I think:
@fil It is a bit more complicated
You can have a fixed X or fixed Y or both.
The other forces of the simulation change the vx
and vy
of the fixed nodes.
The collision force will use a wrong position of the fixed nodes
https://github.com/d3/d3-force/blob/c3e73cf641813c1d6490e6acc8b8f5aa3c239ea9/src/collide.js#L36-L37
Thus the moving nodes collide to a wrong circumference of the fixed node.
The construction of the quadtree
uses the functions x()
and y()
https://github.com/d3/d3-force/blob/c3e73cf641813c1d6490e6acc8b8f5aa3c239ea9/src/collide.js#L5-L11
to place the node in the QuadTree. It is the wrong position for (partial) fixed nodes. So some tests may fail and a collision is not checked.
In the apply()
function the nodes data
and node
https://github.com/d3/d3-force/blob/c3e73cf641813c1d6490e6acc8b8f5aa3c239ea9/src/collide.js#L45
can be moveable or (partially) fixed. If both are XY-fixed do nothing
@belukha The 2 empty spaces are caused by the "collision" of the 2 fixed nodes. they get a vx,vy
component
https://github.com/d3/d3-force/blob/c3e73cf641813c1d6490e6acc8b8f5aa3c239ea9/src/collide.js#L53-L56
All the other nodes see a shadow version
https://github.com/d3/d3-force/blob/c3e73cf641813c1d6490e6acc8b8f5aa3c239ea9/src/collide.js#L46-L47
I have created pull request #225
The result is like:
I used the code from
You can create your own customforce.js
by modifying the code related to the collision force.
See the pull request for the lines of the force()
to copy and replace.
You get radial force x
and y
functions too. Because this gist was created for pull request 113
In the HTML use the following code:
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min); // The maximum is exclusive and the minimum is inclusive
}
var svg = d3.select("#svganchor")
.append("svg")
.attr("width", 1000)
.attr("height", 1000);
var data = [];
data.push({ x: 145, y: 145, fx: 145, fy: 145, r: 40, color: "blue" });
data.push({ x: 155, y: 155, fx: 155, fy: 155, r: 40, color: "green" });
for (let i = 0; i < 80; i++) {
data.push({ x: getRandomInt(0, 400), y: getRandomInt(0, 400), r: 10, color: "red" });
}
var simulation = d3.forceSimulation(data)
.force("collide", d3.forceCollide(d => d.r).strength(1).iterations(10))
.force("x", d3.forceX(150))
.force("y", d3.forceY(150));
var nodes = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", d => d.r)
.attr("fill", 'transparent')
.style("stroke", d => d.color);
simulation.on("tick", tick);
function tick() {
nodes
.attr("cx", function(d) { return d.x })
.attr("cy", function(d) { return d.y });
}
I try to force circles to the point, and resolve collisions. But if some of these circles have fixed position, other circles behave like there're no circles with fixed positions.
demo: https://codesandbox.io/s/d3-forces-collide-ignore-fixed-position-w2qbkg?file=/src/index.ts