Collision Detection After Switching (d3v4)

I have a bubble chart in which the bubbles separate and come back together when the button is pressed.

In my case, the source data that I load into the bubble chart has 3 columns: Character, Total_Words and Sex. How the bubble chart works, each symbol is represented by its own bubble. The area of ​​each bubble is scaled based on Total_Words for each character. Bubbles are colored (and dynamically split) according to sex.

I was able to achieve this job beautifully. All bubbles form in a circle and are divided into a button, and then come back together when you press the second button. The problem is that when bubbles first appear on the screen, collision detection works great (evenly distributed bubbles are everywhere). But after separating the bubbles into two groups (through the second simulation), the collision detection no longer functions (even with an explicit call). In the above picture: Left: Collision detection works, all bubbles are positioned correctly. Right: “on” switch, bubble separation, but collision detection does not work. Bubbles overlap and never stop trembling.Left: Collision detection works, all bubbles are positioned correctly.  Right: Switch

bl.ocks , . https://bl.ocks.org/ProQuestionAsker/79d0228ae7161e349770e7d553cf4c94

.js script, . , " ", , - , .

(function() {
    var width = 400,
    height = 300;

    var svg = d3.select("#chart")
        .append("svg")
        .attr("height", height)
        .attr("width", width)
        .append("g")
        .attr("transform", "translate(0,0)")

    var radiusScale = d3.scaleSqrt().domain([1, 3114]).range([1, 50])

    var forceXSplit = d3.forceX(function(d){
        if(d.Sex === "male") {
            return (width * .30)
        } else {
            return (width * .70)
        }
        }).strength(0.15)

    var forceXCombine = d3.forceX((width)/2).strength(0.1)

    var forceCollide = d3.forceCollide(function(d){
         return radiusScale(d.Total_Words) + 1
         })

    var simulation = d3.forceSimulation()
        .force("x", forceXCombine)
        .force("y", d3.forceY(height / 2).strength(0.09))
        .force("collide", forceCollide) 

    var tooltip = d3.select("body")
        .append("div")
        .style("position", "absolute")
        .style("z-index", "20")
        .style("visibility", "hidden")
        .style("color", "white")
        .style("padding", "8px")
        .style("background-color", "rgba(0, 0, 0, 0.75)")
        .style("border-radius", "6px")
        .style("font", "12px sans-serif")
        .text("");  

// Importing data file

d3.queue()
    .defer(d3.csv, "data.csv")
    .await(ready)

function ready (error, datapoints) {

    var circles = svg.selectAll(".Character")
        .data(datapoints)
        .enter().append("circle")
        .attr("class", "Character")
        .attr("r", function(d){
            return radiusScale(d.Total_Words)
        })
        .style("fill", function(d) { 
            var returnColor;
                if (d.Sex === "male") { returnColor = "#355C7D";
                } else if (d.Sex === "female") {returnColor = "#F67280";}
                return returnColor;
            })
        .on("mouseover", function(d) {
            tooltip.html(d.Character + "<br><br> Words Spoken: " + d.Total_Words);
            tooltip.style("visibility", "visible");
            })
        .on("mousemove", function() {
            return tooltip.style("top", (d3.event.pageY-10)+"px").style("left", (d3.event.pageX+10)+"px");
            })
        .on("mouseout", function(){return tooltip.style("visibility", "hidden");});

// Adding Toggle Switches   

    var atRight = true

    var rect = svg.append("rect")
        .attr("x", 10)
        .attr("y", 10)
        .attr("rx", 22)
        .attr("ry", 22)
        .style("fill", "lightgray")
        .attr("width", 64)
        .attr("height", 40);

    var circle = svg.append("circle")
        .attr("cx", 30)
        .attr("cy", 30)
        .attr("r", 16)
        .style("fill", "white")
        .on("click", function(){
            if(atRight === true){
            simulation 
                .force("x", forceXSplit)
                .alphaTarget(0.2)
                .force("collide", forceCollide)
            setAtRight(!atRight)
            } else {
            simulation
                .restart()
                .force("x", forceXCombine)
                .alphaTarget(0.2)   
            forceCollide.initialize(simulation.nodes());
            setAtRight(!atRight)
            }   
        });

    var setAtRight = function(newValue) {
        atRight = newValue;
        circle.transition().duration(250)
            .attr("cx", (atRight? (30) : (54)))
            .style("fill", "white");
        rect.transition().duration(250)
            .style("fill", atRight? "lightgray" : "#F67280");  
    };


    var res = {
        'getValue': function() { return atRight; },
        'setValue': setAtRight,
        'remove': function() { circle.remove(); }
    };


    simulation.nodes(datapoints)
        .on('tick', ticked)


    function ticked() {
        circles
            .attr("cx", function(d) {
                return d.x
            })
            .attr("cy", function(d) {
                return d.y
            })
    }   
}       
})();

simulation.restart() . forceCollide.initialize(simulation.nodes());, , .

d3.js, , - , .

. !

+3
1

, / . . :

.on("click", function(){
  simulation 
    .force("x", atRight ? forceXSplit : forceXCombine)  // 1. Set the force
    .alpha(1)                                           // 2. Reheat
    .restart();                                         // 3. Restart
   setAtRight(!atRight);
});

  • x-force ,
  • .

, - .

Block . , , . , , .

+3

Source: https://habr.com/ru/post/1667724/


All Articles