Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change the alignment of circles in packed circle d3

I'm trying to change the alignment of the circle so that I can see the text in the middle Just like in this image.

image

I need help on how to align all the circles towards the circumference of the outer circle. I tried to add the text but it is overlapping with the circle in the center. Is it possible to change the alignment of the circle?

My code is mentioned below.

var root = {
            "name": "flare",
            "threat_level": "High",
            "size": 15000,
            "children": [{
                    "name": "Ghost",
                    "threat_level": "High",
                    "size": 1200
                },
                {
                    "name": "Wiper",
                    "threat_level": "Medium",
                    "size": 1330
                },
                {
                    "name": "PowerLiks",
                    "threat_level": "Medium",
                    "size": 1333
                },
                {
                    "name": "Fareit",
                    "threat_level": "Medium",
                    "size": 1300
                },
                {
                    "name": "Tribe",
                    "threat_level": "High",
                    "size": 1330
                },
                {
                    "name": "Oilrig",
                    "threat_level": "High",
                    "size": 1330
                }
            ]
        }

        var svg = d3.select("svg"),
            margin = 20,
            diameter = +svg.attr("width"),
            g = svg.append("g").attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");

        const color = (type) => type == 'High' ? '#F72047' : (type == 'Medium' ? '#FFFFFF' : '#fff0') //colorScale
        var defs = svg.append("defs");
        var levels = ['High', 'Medium', 'Low']
        levels.forEach((d) => {
            //Create a radial Sun-like gradient
            defs.append("radialGradient")
                .attr("id", "sun-gradient_" + d)
                .selectAll("stop")
                .data([{
                        offset: "0%",
                        color: "#1A1D27"
                    },
                    {
                        offset: "80%",
                        color: "#1A1D27"
                    },
                    {
                        offset: "100%",
                        color: d == 'High' ? "#CB1F40" : "#959595"
                    }
                ])
                .enter().append("stop")
                .attr("offset", function (d) {
                    return d.offset;
                })
                .attr("stop-color", function (d) {
                    return d.color;
                });
            // .interpolate(d3.interpolateHcl);
        })
        var pack = d3.pack()
            .size([diameter - margin, diameter - margin])
            .padding(50);

        root = d3.hierarchy(root)
            .sum(function (d) {
                return d.size;
            })
            .sort(function (a, b) {
                return b.value - a.value;
            });

        var focus = root,
            nodes = pack(root).descendants(),
            view;

        var circle = g.selectAll("circle")
            .data(nodes)
            .enter().append("circle")
            .attr("class", function (d) {
                return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root";
            })
            .style("fill", (d) => `url(#${'sun-gradient_' +  d.data.threat_level})`)
            .style('stroke', (d) => color(d.data.threat_level))
            .style('stroke-width', 1)
            .on("click", function (e, d) {
                if (focus !== d) zoom(d);
                e.stopPropagation();
            });

        var text = g.selectAll("text")
            .data(nodes)
            .enter().append("text")
            .attr("class", "label")
            .text(function (d) {
                return d.parent === root ? d.data.name : d.data.size;
            })
            .style('font-family', 'Metropolis Bold')
            .style('font-size', (d) => d.parent === root ? '12px' : '24px')
            .style('text-anchor', 'middle')
            .style('fill', (d) => d.parent === root ? '#FFFFFF' : '#F72047')
            .style('text-transform', 'uppercase')

        var node = g.selectAll("circle,text");

        svg
            .style("background", color(-1))
            .on("click", function () {
                zoom(root);
            });

        zoomTo([root.x, root.y, root.r * 2 + margin]);

        function zoom(d) {
            var focus0 = focus;
            focus = d;

            var transition = d3.transition()
                .duration(750)
                .tween("zoom", function (d) {
                    var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
                    return function (t) {
                        zoomTo(i(t));
                    };
                });

            transition.selectAll("text")
                .filter(function (d) {
                    return d.parent === focus || this.style.display === "inline";
                })
                .style("fill-opacity", function (d) {
                    return d.parent === focus ? 1 : 0;
                })
                .on("start", function (d) {
                    if (d.parent === focus) this.style.display = "inline";
                })
                .on("end", function (d) {
                    if (d.parent !== focus) this.style.display = "none";
                });
        }

        function zoomTo(v) {
            var k = diameter / v[2];
            view = v;
            node.attr("transform", function (d) {
                return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")";
            });
            circle.attr("r", function (d) {
                return d.r * k;
            });
        }
<svg width="600" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
like image 424
Lahari Avatar asked Sep 01 '25 11:09

Lahari


1 Answers

The packed circle layout is designed to take a group of objects and pack them as tightly as possible within a circle. So this isn't what you want.

Method 1: Radial Tree Layout

If you want to arrange a set of objects evenly around the circumference of a circle with a given radius, you want something similar to a "radial tree layout". You can see an example of that in Chapter 6 of the book you linked to (Tree, Cluster, and Radial Layouts), though you'll not want the lines between the nodes.

There's also an example given in the answer to this question: d3.js - how to arrange the `squre` box around the `circle` properly

Method 2: Use sin and cos

Alternatively, you can divide the circumference of the circle by the number of objects you want to place, then use sin and cos to calculate their center point x and y manually.

There's an example of that here: d3.js radially position elements around an object

And a variation here: https://spin.atomicobject.com/2015/06/12/objects-around-svg-circle-d3-js/

like image 92
lfalin Avatar answered Sep 03 '25 00:09

lfalin