Problem with adding/removing bubbles on a force bubble chart

Viewed 79

I'm trying to create a dynamic bubble chart where bubbles are added/removed based on selected filters. Eventually the data is going to be called from my server through Ajax, but currently I just have a set array of objects that i add to or remove from with buttons.

I have two problems that i cannot figure out at the moment.

  1. When I click the Add Bubble button a bubble is added but the it doesn't appear to be added to the force simulation?
  2. When i click the Remove Bubble button the bubble with the data that is removed isn't being removed with exit.remove()

The full code can be found here https://jsfiddle.net/codered1988/s2z63crL/

function drawBubbles() {
    
    let root = d3.hierarchy({ children: data })
        .sum(d => d.value);
    let nodes = pack(root).leaves().map(node => {
        const data = node.data;
        return {
            x: centerX + (node.x - centerX) * 3, 
            y: centerY + (node.y - centerY) * 3,
            r: 10, // for tweening
            radius: node.r, //original radius
            id: data.id,
            type: data.type,
            name: data.name,
            value: data.value,
        }
    });
    simulation.nodes(nodes).on('tick', ticked);
  
    node = svg.selectAll('.node')
        .data(nodes, d => d.id)
        .join(
            function(n){ // enter 

                enter = n.append('g')
                    .attr('class', 'node')
                        .call(d3.drag()
                            .on('start', (event, d) => {
                                if (!event.active) simulation.alphaTarget(0.2).restart();
                                d.fx = d.x;
                                d.fy = d.y;
                            })
                            .on('drag', (event, d) => {
                                d.fx = event.x;
                                d.fy = event.y;
                            })
                            .on('end', (event, d) => {
                                if (!event.active) simulation.alphaTarget(0);
                                d.fx = null;
                                d.fy = null;
                            }))
                // Create Circles 
                enter.append('circle')
                    .attr('id', d => d.id)
                    .attr('r', d => d.radius)
                    //.style('fill', d => scaleColor(d.type))
                    .style('fill', d => fill(d.type))
                    .attr("stroke", "black")
                    .style("stroke-width", 2)
                    .transition().duration(2000).ease(d3.easeElasticOut)
                            .tween('circleIn', (d) => {
                            console.log('tween');
                            let i = d3.interpolateNumber(d.r, d.radius);
                            return (t) => {
                                d.r = i(t);
                                simulation.force('collide', forceCollide);
                            }
                        })
                enter.append('clipPath')
                .attr('id', d => `clip-${d.id}`)
                .append('use')
                .attr('xlink:href', d => `#${d.id}`);
                
                // display text as circle icon
                enter.filter(d => !String(d.name).includes('img/')) 
                    .append('text')
                    .classed('node-icon', true)
                    .attr('clip-path', d => `url(#clip-${d.id})`)
                    .selectAll('tspan')
                    .data(d => d.name.split(';'))
                    .enter()
                        .append('tspan')
                        .attr('x', 0)
                        .attr('y',(d, i, nodes) => (13 + (i - nodes.length / 2 - 0.5) * 10))
                        .text(name => name);
        
                return enter;
            },
            
            update => update
                .attr("fill", "black")
                .attr("y", 0)
              .call(update => update.transition(t)
                .attr("x", (d, i) => i * 16)),
            
            
            exit => exit
                .attr("fill", "brown")
              .call(exit => exit.transition(t)
                .attr("y", 30)
                .remove())
                         );

    //simulation.nodes(nodes).alpha(1).restart();
    
}
0 Answers
Related