简体   繁体   中英

D3 pack: setting same radius for the nodes that include the different number of children

I am trying to make the same radius for all child nodes using d3.pack(). In my experience, the problem is that d3 pack generates the radius according to child nodes count or some other value.

Here is data structure sample.

{
    "id": "CoC-home",
    "name": "HOME",
    "type": "circle",
    "value": 1,
    "children": [
      {
        "id": "CT-home-1",
        "name": "Sales Systems, Digital & Omnichannel",
        "type": "circle",
        "value": 1,
        "children": [
          {
            "id": "ET-1-home",
            "name": "Omnichannel",
            "type": "circle",
            "value":  0.25,
            "children": [
              {
                "id": "EG-1-home",
                "name": "CRM",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-2-home",
                "name": "New Business Models",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-3-home",
                "name": "Business Development Stores",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-4-home",
                "name": "Business Development Customer & Advisor Service",
                "type": "circle",
                "value": 1
              }
            ]
          },
          {
            "id": "ET-2-home",
            "name": "Business Development Advisor Sales",
            "type": "circle",
            "value": 0.2,
            "children": [
              {
                "id": "EG-5-home",
                "name": "Business Development Advisor Sales Kobold",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-6-home",
                "name": "Business Development Advisor Sales Temial",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-7-home",
                "name": "Recruitment and Training & Learning",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-8-home",
                "name": "Income System",
                "type": "circle",
                "value": 1
              }
            ]
          },
          {
            "id": "ET-3-home",
            "name": "Advisor Solutions",
            "type": "circle",
            "value": 0.25,
            "children": [
              {
                "id": "EG-9-home",
                "name": "Commission Engine",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-10-home",
                "name": "Advisor Lifecycle & LMS",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-11-home",
                "name": "Solutions Small Countries",
                "type": "circle",
                "value": 1
              }
            ]
          },
          {
            "id": "ET-4-home",
            "name": "Digital",
            "type": "circle",
            "value": 0.2,
            "children": [
              {
                "id": "EG-12-home",
                "name": "eBusiness",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-13-home",
                "name": "Cleaning",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-14-home",
                "name": "Cookidoo",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-15-home",
                "name": "Digital Experience Design",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-16-home",
                "name": "Community",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-17-home",
                "name": "Big Data & Analytics",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-18-home",
                "name": "Insights Engine",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-19-home",
                "name": "Digital Marketing & Lead Management",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-20-home",
                "name": "Recipe Business",
                "type": "circle",
                "value": 1
              }
            ]
          },
          {
            "id": "ET-5-home",
            "name": "Program Management",
            "type": "circle",
            "value": 0.2,
            "children": [
              {
                "id": "EG-22-home",
                "name": "Program Mgmt. Customer Service",
                "type": "circle",
                "value": 1
              },
              {
                "id": "EG-23-home",
                "name": "TBD",
                "type": "circle",
                "value": 1
              }
            ]
          }
        ]
      },
      {
        "id": "CT-home-2",
        "name": "Marketing",
        "type": "circle",
        "value": 1,
        "children": [
          {
            "id": "ET-6-home",
            "name": "Brand Marketing",
            "type": "circle",
            "value":  0.25
          },
          {
            "id": "ET-7-home",
            "name": "Communication",
            "type": "circle",
            "value":  0.25
          },
          {
            "id": "ET-8-home",
            "name": "Market Research & Insights",
            "type": "circle",
            "value":  0.25
          },
          {
            "id": "ET-9-home",
            "name": "Operative Marketing Coordination",
            "type": "circle",
            "value":  0.25
          }
        ]
      },
      {
        "id": "CT-home-4",
        "name": "Sales",
        "type": "circle",
        "value": 1,
        "children": [
          {
            "id": "ET-15-home",
            "name": "IDB",
            "type": "circle",
            "value": 1
          },
          {
            "id": "ET-16-home",
            "name": "KS/TM Country xxx",
            "type": "circle",
            "value": 1
          },
          {
            "id": "ET-17-home",
            "name": "KS/TM Country xxx",
            "type": "circle",
            "value": 1
          }
        ]
      },
      {
        "id": "CT-home-5",
        "name": "Complementary Product Portfolio",
        "type": "circle",
        "value": 1
      },
      {
        "id": "CT-home-6",
        "name": "Recognition & Events",
        "type": "circle",
        "value": 1
      },
      {
        "id": "CT-home-7",
        "name": "Finance Markets & Business Partnering",
        "type": "circle",
        "value": 1
      },
      {
        "id": "CT-home-8",
        "name": "Operations",
        "type": "circle",
        "value": 1,
        "children": [
          {
            "id": "ET-20-home",
            "name": "Intl. Trade",
            "type": "circle",
            "value": 1
          },
          {
            "id": "ET-21-home",
            "name": "Master Data",
            "type": "circle",
            "value": 1
          },
          {
            "id": "ET-21-home",
            "name": "Purchasing",
            "type": "circle",
            "value": 1
          },
          {
            "id": "ET-21-home",
            "name": "Operational Logistics",
            "type": "circle",
            "value": 1
          },
          {
            "id": "ET-21-home",
            "name": "Performance Management",
            "type": "circle",
            "value": 1
          },
          {
            "id": "ET-21-home",
            "name": "Repair",
            "type": "circle",
            "value": 1
          },
          {
            "id": "ET-21-home",
            "name": "Planning",
            "type": "circle",
            "value": 1
          },
          {
            "id": "ET-21-home",
            "name": "Projects",
            "type": "circle",
            "value": 1
          }
        ]
      }
    ]
  },

There are all 4 levels in the json data and each level node has the different count of children nodes. If the number of child nodes for each level is same, then its radius is same.

Here's what I'm getting:

https://prnt.sc/vrc7oi

https://prnt.sc/vrc8p h

Here is my code snippet


nodeG.append('g')
.filter(d => d.type === 'circle' | d.type === 'inner-circle')
.each(function (d) {
  drawHexagons(
    d3.select(this),
    d,
    {
      width: radiusAccessor(d) * 2,
      height: radiusAccessor(d) * 2
    }
  )
})

const radiusAccessor = (d) => {
  return d.id === 'exec' ?
    50 :
    d.type === 'circle' ?
      110 :
      d.type === 'inner-circle' ?
        60 : 50;
}

function drawHexagons(nodeElement, data, options) {

  const width = options.width
  const height = options.height
  
  const pack = data => d3.pack()
    .size([width, height])
    .padding(3)
    (d3.hierarchy(data)
      .sum(d => d.value)
      .sort((a, b) => b.value - a.value))

  const root = pack(data);
  let focus = root;

  const node = nodeElement.append("g")
    .selectAll("circle")
    .data(root.descendants().slice(1))
    .join("circle")
    .attr("id", d => d.data.id)
    .attr("class", d => {
      return d.data.groupId
    })
    .attr("fill", d => (d.data.groupId === "corporate_governance" | d.data.groupId === "communications") ? color(d.depth + 1) : color(d.depth))
    .attr("transform", d => `translate(${(d.x - root.x)},${(d.y - root.y)})`)
    .attr("r", d => { 
      return d.r;
    })
    .style("visibility", d => { 
      return d.depth > 0 ? "hidden" : "visible" 
    })
}

I just want to make all child node to take the same radius regardless of its child nodes number.

For clarity: I just want to take the same radius for each level nodes regardless each node has children or not. For example, all node's radius for level 1 is 50, radius for level 2 is 30 and etc.

Do you have any idea?

Thank for your reading.

I just want to take the same radius for each level nodes regardless each node has children or not. For example, all node's radius for level 1 is 50, radius for level 2 is 30 and etc

This cannot be achieved with circle packing. The goal of circle packing is to child circles into the smallest bounding circle. You could make all of one generation have the same size regardless of child count, but to specify each generation/level's won't work.

If parent A has one child of radius r and parent A's sibling, parent, B another has two children of radius r, then naturally, the smallest bounding circle for parent A will not be the same as parent B. For each additional child of fixed radius r, the minimum bounding circle's radius will increase .

Consequently, any circle packing algorithm won't work - you cannot optimally pack a different number of children with the same radius into larger parent circles sharing some larger radius: there will be wasted space in the parent with less children.

I'm not aware of any alternative algorithm to achieve this, using rectangles would likely be the easiest to implement a solution manually - as rectangles are easily stacked making overlap prevention easy. A circular solution without math could be a nested d3-force layout, but could be cumbersome from a code and overhead perspective.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM