D3.js spread squares along circle with force
I want to spread squares with content along a circle with force() and collision, so the squares don't overlap each other and main circle.
Any thoughts how to do this? Should I use links to do this?
Here is a fiddle http://jsfiddle.net/benderlio/usbq839m/3/
Sometimes it spread the squares fine, but most of the time the squares are shifted as on the img.

var force = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody(1130))
// .force('link', d3.forceLink().links(links))
.on("tick", function () {
var k = this.alpha(),
kg = k * .02,
spaceAround = 0.04;
//console.log('', nodes);
nodes.forEach(function (a, i) {
...
});
svg.selectAll("rect")
.style("fill", function (d) {
return "#ccc"
})
.attr("x", function (d) {
return d.x - d.width / 2;
})
.attr("y", function (d) {
return d.y - d.height / 2;
});
svg.selectAll("line")
.attr("x2", function (d) {
return d.x;
})
.attr("y2", function (d) {
return d.y;
})
svg.selectAll("circle.end")
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
})
});
Thanks.
UPD:
Added a main fixed square, now it looks better. But looks like I have to do something with "collision order". The squares a flipped. Any thoughts how to fix it?

javascript d3.js d3-force-directed
add a comment |
I want to spread squares with content along a circle with force() and collision, so the squares don't overlap each other and main circle.
Any thoughts how to do this? Should I use links to do this?
Here is a fiddle http://jsfiddle.net/benderlio/usbq839m/3/
Sometimes it spread the squares fine, but most of the time the squares are shifted as on the img.

var force = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody(1130))
// .force('link', d3.forceLink().links(links))
.on("tick", function () {
var k = this.alpha(),
kg = k * .02,
spaceAround = 0.04;
//console.log('', nodes);
nodes.forEach(function (a, i) {
...
});
svg.selectAll("rect")
.style("fill", function (d) {
return "#ccc"
})
.attr("x", function (d) {
return d.x - d.width / 2;
})
.attr("y", function (d) {
return d.y - d.height / 2;
});
svg.selectAll("line")
.attr("x2", function (d) {
return d.x;
})
.attr("y2", function (d) {
return d.y;
})
svg.selectAll("circle.end")
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
})
});
Thanks.
UPD:
Added a main fixed square, now it looks better. But looks like I have to do something with "collision order". The squares a flipped. Any thoughts how to fix it?

javascript d3.js d3-force-directed
1
lxandlyare always< 0. what is the use ofidToNode,overlapCount?
– rioV8
Nov 28 '18 at 11:50
@rioV8 don't know about overlapCount and lx,ly. I just took that code for box collision. Removed idToNode
– SERG
Nov 28 '18 at 15:34
add a comment |
I want to spread squares with content along a circle with force() and collision, so the squares don't overlap each other and main circle.
Any thoughts how to do this? Should I use links to do this?
Here is a fiddle http://jsfiddle.net/benderlio/usbq839m/3/
Sometimes it spread the squares fine, but most of the time the squares are shifted as on the img.

var force = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody(1130))
// .force('link', d3.forceLink().links(links))
.on("tick", function () {
var k = this.alpha(),
kg = k * .02,
spaceAround = 0.04;
//console.log('', nodes);
nodes.forEach(function (a, i) {
...
});
svg.selectAll("rect")
.style("fill", function (d) {
return "#ccc"
})
.attr("x", function (d) {
return d.x - d.width / 2;
})
.attr("y", function (d) {
return d.y - d.height / 2;
});
svg.selectAll("line")
.attr("x2", function (d) {
return d.x;
})
.attr("y2", function (d) {
return d.y;
})
svg.selectAll("circle.end")
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
})
});
Thanks.
UPD:
Added a main fixed square, now it looks better. But looks like I have to do something with "collision order". The squares a flipped. Any thoughts how to fix it?

javascript d3.js d3-force-directed
I want to spread squares with content along a circle with force() and collision, so the squares don't overlap each other and main circle.
Any thoughts how to do this? Should I use links to do this?
Here is a fiddle http://jsfiddle.net/benderlio/usbq839m/3/
Sometimes it spread the squares fine, but most of the time the squares are shifted as on the img.

var force = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody(1130))
// .force('link', d3.forceLink().links(links))
.on("tick", function () {
var k = this.alpha(),
kg = k * .02,
spaceAround = 0.04;
//console.log('', nodes);
nodes.forEach(function (a, i) {
...
});
svg.selectAll("rect")
.style("fill", function (d) {
return "#ccc"
})
.attr("x", function (d) {
return d.x - d.width / 2;
})
.attr("y", function (d) {
return d.y - d.height / 2;
});
svg.selectAll("line")
.attr("x2", function (d) {
return d.x;
})
.attr("y2", function (d) {
return d.y;
})
svg.selectAll("circle.end")
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
})
});
Thanks.
UPD:
Added a main fixed square, now it looks better. But looks like I have to do something with "collision order". The squares a flipped. Any thoughts how to fix it?

javascript d3.js d3-force-directed
javascript d3.js d3-force-directed
edited Nov 28 '18 at 18:42
SERG
asked Nov 28 '18 at 9:08
SERGSERG
96852348
96852348
1
lxandlyare always< 0. what is the use ofidToNode,overlapCount?
– rioV8
Nov 28 '18 at 11:50
@rioV8 don't know about overlapCount and lx,ly. I just took that code for box collision. Removed idToNode
– SERG
Nov 28 '18 at 15:34
add a comment |
1
lxandlyare always< 0. what is the use ofidToNode,overlapCount?
– rioV8
Nov 28 '18 at 11:50
@rioV8 don't know about overlapCount and lx,ly. I just took that code for box collision. Removed idToNode
– SERG
Nov 28 '18 at 15:34
1
1
lx and ly are always < 0. what is the use of idToNode, overlapCount?– rioV8
Nov 28 '18 at 11:50
lx and ly are always < 0. what is the use of idToNode, overlapCount?– rioV8
Nov 28 '18 at 11:50
@rioV8 don't know about overlapCount and lx,ly. I just took that code for box collision. Removed idToNode
– SERG
Nov 28 '18 at 15:34
@rioV8 don't know about overlapCount and lx,ly. I just took that code for box collision. Removed idToNode
– SERG
Nov 28 '18 at 15:34
add a comment |
2 Answers
2
active
oldest
votes
I do not know the general way to solve this problem, but here are some ad hocs for better solution:
When you define "target points" for rects gravity algorithm, make them not on boundary of the circle, but some distance away.
x = ((150 + radius) * Math.cos(angle)) + (width / 2) + offset; // Calculate the x position of the element.
y = ((150 + radius) * Math.sin(angle)) + (width / 2) + offset; // Calculate the y position of the element.
If you want for rects to not collide with the circle, you need custom force. Since exact formula for rect-circle collision is not simple, perhaps, it is enough to treat rectangles as circles, so add this after
nodes.forEach(...):
nodes.forEach(function(a) {
const diag = Math.sqrt(a.width ** 2 + a.height ** 2) / 2;
const rad = 150;
const [cx, cy] = [500, 500];
const [dx, dy] = [a.x - cx, a.y - cy];
const dist = Math.sqrt(dx ** 2 + dy ** 2);
const shift = Math.max(0, rad + diag - dist);
a.x += shift * dx / dist;
a.y += shift * dy / dist;
})

Thanks. I added the main square. Now it looks better, but have to do something with "square order". Can you have a look at the updated question?
– SERG
Nov 28 '18 at 18:44
add a comment |
Your collision force is too strong. Make it a bit less.
lx *= 0.3;
ly *= 0.3;
The center rect can be added with
nodes.push( { x:500, y:500, width:300, height:300, fx:500, fy:500, center: true });
Drawing of the rects should now be filtered
var g = svg.selectAll("g")
.data(nodes.filter(d => !d.center))
.enter()
.append("g")
And if you also draw the center rect you must add a class
g.append("rect")
.attr("opacity", 0.5)
.attr("class", "node")
.attr("fill", "#ccc" )
.attr("width", d => d.width )
.attr("height", d => d.height )
.attr("rx", 10)
.attr("ry", 10);
And in the tick function filter on the rects with the class
svg.selectAll("rect.node")
.attr("x", d => d.x - d.width / 2 )
.attr("y", d => d.y - d.height / 2 );
Add a test to ignore the center rect
if (a.center) return;
Updated force code
nodes.forEach(function (a, i) {
if (a.center) return; // ignore this node
// Apply gravity forces.
a.x += (a.gravity.x - a.x) * kg;
a.y += (a.gravity.y - a.y) * kg;
nodes.slice(i + 1).forEach(function (b) {
dx = (a.x - b.x)
dy = (a.y - b.y)
adx = Math.abs(dx)
ady = Math.abs(dy)
mdx = (1 + spaceAround) * (a.width + b.width) / 2
mdy = (1 + spaceAround) * (a.height + b.height) / 2
if (adx < mdx && ady < mdy) {
l = Math.sqrt(dx * dx + dy * dy)
lx = (adx - mdx) / l * k
ly = (ady - mdy) / l * k
lx *= 0.3;
ly *= 0.3;
// choose the direction with less overlap
if (lx > ly && ly > 0) lx = 0;
else if (ly > lx && lx > 0) ly = 0;
dx *= lx
dy *= ly
a.x -= dx
a.y -= dy
b.x += dx
b.y += dy
}
});
});
Edit
A modification would be to make the movement independent of the distance of the rect centers. Only move a certain amount in the opposite direction of the other center.
The current method changes the nodes later in the list already to a new position and base the collision of next nodes on those new positions. Better is to calculate the movement first based on all the nodes for time N and at the end apply this movement. This is done in the force simulation by calculating/modifying the speed (d.vx, d.vy) of the node. The simulation will apply the speed on the tick.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53515750%2fd3-js-spread-squares-along-circle-with-force%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
I do not know the general way to solve this problem, but here are some ad hocs for better solution:
When you define "target points" for rects gravity algorithm, make them not on boundary of the circle, but some distance away.
x = ((150 + radius) * Math.cos(angle)) + (width / 2) + offset; // Calculate the x position of the element.
y = ((150 + radius) * Math.sin(angle)) + (width / 2) + offset; // Calculate the y position of the element.
If you want for rects to not collide with the circle, you need custom force. Since exact formula for rect-circle collision is not simple, perhaps, it is enough to treat rectangles as circles, so add this after
nodes.forEach(...):
nodes.forEach(function(a) {
const diag = Math.sqrt(a.width ** 2 + a.height ** 2) / 2;
const rad = 150;
const [cx, cy] = [500, 500];
const [dx, dy] = [a.x - cx, a.y - cy];
const dist = Math.sqrt(dx ** 2 + dy ** 2);
const shift = Math.max(0, rad + diag - dist);
a.x += shift * dx / dist;
a.y += shift * dy / dist;
})

Thanks. I added the main square. Now it looks better, but have to do something with "square order". Can you have a look at the updated question?
– SERG
Nov 28 '18 at 18:44
add a comment |
I do not know the general way to solve this problem, but here are some ad hocs for better solution:
When you define "target points" for rects gravity algorithm, make them not on boundary of the circle, but some distance away.
x = ((150 + radius) * Math.cos(angle)) + (width / 2) + offset; // Calculate the x position of the element.
y = ((150 + radius) * Math.sin(angle)) + (width / 2) + offset; // Calculate the y position of the element.
If you want for rects to not collide with the circle, you need custom force. Since exact formula for rect-circle collision is not simple, perhaps, it is enough to treat rectangles as circles, so add this after
nodes.forEach(...):
nodes.forEach(function(a) {
const diag = Math.sqrt(a.width ** 2 + a.height ** 2) / 2;
const rad = 150;
const [cx, cy] = [500, 500];
const [dx, dy] = [a.x - cx, a.y - cy];
const dist = Math.sqrt(dx ** 2 + dy ** 2);
const shift = Math.max(0, rad + diag - dist);
a.x += shift * dx / dist;
a.y += shift * dy / dist;
})

Thanks. I added the main square. Now it looks better, but have to do something with "square order". Can you have a look at the updated question?
– SERG
Nov 28 '18 at 18:44
add a comment |
I do not know the general way to solve this problem, but here are some ad hocs for better solution:
When you define "target points" for rects gravity algorithm, make them not on boundary of the circle, but some distance away.
x = ((150 + radius) * Math.cos(angle)) + (width / 2) + offset; // Calculate the x position of the element.
y = ((150 + radius) * Math.sin(angle)) + (width / 2) + offset; // Calculate the y position of the element.
If you want for rects to not collide with the circle, you need custom force. Since exact formula for rect-circle collision is not simple, perhaps, it is enough to treat rectangles as circles, so add this after
nodes.forEach(...):
nodes.forEach(function(a) {
const diag = Math.sqrt(a.width ** 2 + a.height ** 2) / 2;
const rad = 150;
const [cx, cy] = [500, 500];
const [dx, dy] = [a.x - cx, a.y - cy];
const dist = Math.sqrt(dx ** 2 + dy ** 2);
const shift = Math.max(0, rad + diag - dist);
a.x += shift * dx / dist;
a.y += shift * dy / dist;
})

I do not know the general way to solve this problem, but here are some ad hocs for better solution:
When you define "target points" for rects gravity algorithm, make them not on boundary of the circle, but some distance away.
x = ((150 + radius) * Math.cos(angle)) + (width / 2) + offset; // Calculate the x position of the element.
y = ((150 + radius) * Math.sin(angle)) + (width / 2) + offset; // Calculate the y position of the element.
If you want for rects to not collide with the circle, you need custom force. Since exact formula for rect-circle collision is not simple, perhaps, it is enough to treat rectangles as circles, so add this after
nodes.forEach(...):
nodes.forEach(function(a) {
const diag = Math.sqrt(a.width ** 2 + a.height ** 2) / 2;
const rad = 150;
const [cx, cy] = [500, 500];
const [dx, dy] = [a.x - cx, a.y - cy];
const dist = Math.sqrt(dx ** 2 + dy ** 2);
const shift = Math.max(0, rad + diag - dist);
a.x += shift * dx / dist;
a.y += shift * dy / dist;
})

answered Nov 28 '18 at 12:41
Yaroslav SergienkoYaroslav Sergienko
40016
40016
Thanks. I added the main square. Now it looks better, but have to do something with "square order". Can you have a look at the updated question?
– SERG
Nov 28 '18 at 18:44
add a comment |
Thanks. I added the main square. Now it looks better, but have to do something with "square order". Can you have a look at the updated question?
– SERG
Nov 28 '18 at 18:44
Thanks. I added the main square. Now it looks better, but have to do something with "square order". Can you have a look at the updated question?
– SERG
Nov 28 '18 at 18:44
Thanks. I added the main square. Now it looks better, but have to do something with "square order". Can you have a look at the updated question?
– SERG
Nov 28 '18 at 18:44
add a comment |
Your collision force is too strong. Make it a bit less.
lx *= 0.3;
ly *= 0.3;
The center rect can be added with
nodes.push( { x:500, y:500, width:300, height:300, fx:500, fy:500, center: true });
Drawing of the rects should now be filtered
var g = svg.selectAll("g")
.data(nodes.filter(d => !d.center))
.enter()
.append("g")
And if you also draw the center rect you must add a class
g.append("rect")
.attr("opacity", 0.5)
.attr("class", "node")
.attr("fill", "#ccc" )
.attr("width", d => d.width )
.attr("height", d => d.height )
.attr("rx", 10)
.attr("ry", 10);
And in the tick function filter on the rects with the class
svg.selectAll("rect.node")
.attr("x", d => d.x - d.width / 2 )
.attr("y", d => d.y - d.height / 2 );
Add a test to ignore the center rect
if (a.center) return;
Updated force code
nodes.forEach(function (a, i) {
if (a.center) return; // ignore this node
// Apply gravity forces.
a.x += (a.gravity.x - a.x) * kg;
a.y += (a.gravity.y - a.y) * kg;
nodes.slice(i + 1).forEach(function (b) {
dx = (a.x - b.x)
dy = (a.y - b.y)
adx = Math.abs(dx)
ady = Math.abs(dy)
mdx = (1 + spaceAround) * (a.width + b.width) / 2
mdy = (1 + spaceAround) * (a.height + b.height) / 2
if (adx < mdx && ady < mdy) {
l = Math.sqrt(dx * dx + dy * dy)
lx = (adx - mdx) / l * k
ly = (ady - mdy) / l * k
lx *= 0.3;
ly *= 0.3;
// choose the direction with less overlap
if (lx > ly && ly > 0) lx = 0;
else if (ly > lx && lx > 0) ly = 0;
dx *= lx
dy *= ly
a.x -= dx
a.y -= dy
b.x += dx
b.y += dy
}
});
});
Edit
A modification would be to make the movement independent of the distance of the rect centers. Only move a certain amount in the opposite direction of the other center.
The current method changes the nodes later in the list already to a new position and base the collision of next nodes on those new positions. Better is to calculate the movement first based on all the nodes for time N and at the end apply this movement. This is done in the force simulation by calculating/modifying the speed (d.vx, d.vy) of the node. The simulation will apply the speed on the tick.
add a comment |
Your collision force is too strong. Make it a bit less.
lx *= 0.3;
ly *= 0.3;
The center rect can be added with
nodes.push( { x:500, y:500, width:300, height:300, fx:500, fy:500, center: true });
Drawing of the rects should now be filtered
var g = svg.selectAll("g")
.data(nodes.filter(d => !d.center))
.enter()
.append("g")
And if you also draw the center rect you must add a class
g.append("rect")
.attr("opacity", 0.5)
.attr("class", "node")
.attr("fill", "#ccc" )
.attr("width", d => d.width )
.attr("height", d => d.height )
.attr("rx", 10)
.attr("ry", 10);
And in the tick function filter on the rects with the class
svg.selectAll("rect.node")
.attr("x", d => d.x - d.width / 2 )
.attr("y", d => d.y - d.height / 2 );
Add a test to ignore the center rect
if (a.center) return;
Updated force code
nodes.forEach(function (a, i) {
if (a.center) return; // ignore this node
// Apply gravity forces.
a.x += (a.gravity.x - a.x) * kg;
a.y += (a.gravity.y - a.y) * kg;
nodes.slice(i + 1).forEach(function (b) {
dx = (a.x - b.x)
dy = (a.y - b.y)
adx = Math.abs(dx)
ady = Math.abs(dy)
mdx = (1 + spaceAround) * (a.width + b.width) / 2
mdy = (1 + spaceAround) * (a.height + b.height) / 2
if (adx < mdx && ady < mdy) {
l = Math.sqrt(dx * dx + dy * dy)
lx = (adx - mdx) / l * k
ly = (ady - mdy) / l * k
lx *= 0.3;
ly *= 0.3;
// choose the direction with less overlap
if (lx > ly && ly > 0) lx = 0;
else if (ly > lx && lx > 0) ly = 0;
dx *= lx
dy *= ly
a.x -= dx
a.y -= dy
b.x += dx
b.y += dy
}
});
});
Edit
A modification would be to make the movement independent of the distance of the rect centers. Only move a certain amount in the opposite direction of the other center.
The current method changes the nodes later in the list already to a new position and base the collision of next nodes on those new positions. Better is to calculate the movement first based on all the nodes for time N and at the end apply this movement. This is done in the force simulation by calculating/modifying the speed (d.vx, d.vy) of the node. The simulation will apply the speed on the tick.
add a comment |
Your collision force is too strong. Make it a bit less.
lx *= 0.3;
ly *= 0.3;
The center rect can be added with
nodes.push( { x:500, y:500, width:300, height:300, fx:500, fy:500, center: true });
Drawing of the rects should now be filtered
var g = svg.selectAll("g")
.data(nodes.filter(d => !d.center))
.enter()
.append("g")
And if you also draw the center rect you must add a class
g.append("rect")
.attr("opacity", 0.5)
.attr("class", "node")
.attr("fill", "#ccc" )
.attr("width", d => d.width )
.attr("height", d => d.height )
.attr("rx", 10)
.attr("ry", 10);
And in the tick function filter on the rects with the class
svg.selectAll("rect.node")
.attr("x", d => d.x - d.width / 2 )
.attr("y", d => d.y - d.height / 2 );
Add a test to ignore the center rect
if (a.center) return;
Updated force code
nodes.forEach(function (a, i) {
if (a.center) return; // ignore this node
// Apply gravity forces.
a.x += (a.gravity.x - a.x) * kg;
a.y += (a.gravity.y - a.y) * kg;
nodes.slice(i + 1).forEach(function (b) {
dx = (a.x - b.x)
dy = (a.y - b.y)
adx = Math.abs(dx)
ady = Math.abs(dy)
mdx = (1 + spaceAround) * (a.width + b.width) / 2
mdy = (1 + spaceAround) * (a.height + b.height) / 2
if (adx < mdx && ady < mdy) {
l = Math.sqrt(dx * dx + dy * dy)
lx = (adx - mdx) / l * k
ly = (ady - mdy) / l * k
lx *= 0.3;
ly *= 0.3;
// choose the direction with less overlap
if (lx > ly && ly > 0) lx = 0;
else if (ly > lx && lx > 0) ly = 0;
dx *= lx
dy *= ly
a.x -= dx
a.y -= dy
b.x += dx
b.y += dy
}
});
});
Edit
A modification would be to make the movement independent of the distance of the rect centers. Only move a certain amount in the opposite direction of the other center.
The current method changes the nodes later in the list already to a new position and base the collision of next nodes on those new positions. Better is to calculate the movement first based on all the nodes for time N and at the end apply this movement. This is done in the force simulation by calculating/modifying the speed (d.vx, d.vy) of the node. The simulation will apply the speed on the tick.
Your collision force is too strong. Make it a bit less.
lx *= 0.3;
ly *= 0.3;
The center rect can be added with
nodes.push( { x:500, y:500, width:300, height:300, fx:500, fy:500, center: true });
Drawing of the rects should now be filtered
var g = svg.selectAll("g")
.data(nodes.filter(d => !d.center))
.enter()
.append("g")
And if you also draw the center rect you must add a class
g.append("rect")
.attr("opacity", 0.5)
.attr("class", "node")
.attr("fill", "#ccc" )
.attr("width", d => d.width )
.attr("height", d => d.height )
.attr("rx", 10)
.attr("ry", 10);
And in the tick function filter on the rects with the class
svg.selectAll("rect.node")
.attr("x", d => d.x - d.width / 2 )
.attr("y", d => d.y - d.height / 2 );
Add a test to ignore the center rect
if (a.center) return;
Updated force code
nodes.forEach(function (a, i) {
if (a.center) return; // ignore this node
// Apply gravity forces.
a.x += (a.gravity.x - a.x) * kg;
a.y += (a.gravity.y - a.y) * kg;
nodes.slice(i + 1).forEach(function (b) {
dx = (a.x - b.x)
dy = (a.y - b.y)
adx = Math.abs(dx)
ady = Math.abs(dy)
mdx = (1 + spaceAround) * (a.width + b.width) / 2
mdy = (1 + spaceAround) * (a.height + b.height) / 2
if (adx < mdx && ady < mdy) {
l = Math.sqrt(dx * dx + dy * dy)
lx = (adx - mdx) / l * k
ly = (ady - mdy) / l * k
lx *= 0.3;
ly *= 0.3;
// choose the direction with less overlap
if (lx > ly && ly > 0) lx = 0;
else if (ly > lx && lx > 0) ly = 0;
dx *= lx
dy *= ly
a.x -= dx
a.y -= dy
b.x += dx
b.y += dy
}
});
});
Edit
A modification would be to make the movement independent of the distance of the rect centers. Only move a certain amount in the opposite direction of the other center.
The current method changes the nodes later in the list already to a new position and base the collision of next nodes on those new positions. Better is to calculate the movement first based on all the nodes for time N and at the end apply this movement. This is done in the force simulation by calculating/modifying the speed (d.vx, d.vy) of the node. The simulation will apply the speed on the tick.
edited Nov 29 '18 at 6:24
answered Nov 29 '18 at 5:33
rioV8rioV8
4,5742312
4,5742312
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53515750%2fd3-js-spread-squares-along-circle-with-force%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
lxandlyare always< 0. what is the use ofidToNode,overlapCount?– rioV8
Nov 28 '18 at 11:50
@rioV8 don't know about overlapCount and lx,ly. I just took that code for box collision. Removed idToNode
– SERG
Nov 28 '18 at 15:34