D3.js spread squares along circle with force












0















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.



enter image description here



    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?
enter image description here










share|improve this question




















  • 1





    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
















0















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.



enter image description here



    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?
enter image description here










share|improve this question




















  • 1





    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














0












0








0








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.



enter image description here



    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?
enter image description here










share|improve this question
















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.



enter image description here



    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?
enter image description here







javascript d3.js d3-force-directed






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 28 '18 at 18:42







SERG

















asked Nov 28 '18 at 9:08









SERGSERG

96852348




96852348








  • 1





    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














  • 1





    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








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












2 Answers
2






active

oldest

votes


















2














I do not know the general way to solve this problem, but here are some ad hocs for better solution:





  1. 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.



  2. 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;
    })



screenshot






share|improve this answer
























  • 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



















1














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.






share|improve this answer

























    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
    });


    }
    });














    draft saved

    draft discarded


















    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









    2














    I do not know the general way to solve this problem, but here are some ad hocs for better solution:





    1. 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.



    2. 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;
      })



    screenshot






    share|improve this answer
























    • 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
















    2














    I do not know the general way to solve this problem, but here are some ad hocs for better solution:





    1. 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.



    2. 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;
      })



    screenshot






    share|improve this answer
























    • 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














    2












    2








    2







    I do not know the general way to solve this problem, but here are some ad hocs for better solution:





    1. 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.



    2. 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;
      })



    screenshot






    share|improve this answer













    I do not know the general way to solve this problem, but here are some ad hocs for better solution:





    1. 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.



    2. 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;
      })



    screenshot







    share|improve this answer












    share|improve this answer



    share|improve this answer










    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



















    • 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













    1














    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.






    share|improve this answer






























      1














      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.






      share|improve this answer




























        1












        1








        1







        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.






        share|improve this answer















        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.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 29 '18 at 6:24

























        answered Nov 29 '18 at 5:33









        rioV8rioV8

        4,5742312




        4,5742312






























            draft saved

            draft discarded




















































            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.




            draft saved


            draft discarded














            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





















































            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







            Popular posts from this blog

            Lallio

            Futebolista

            Jornalista