简体   繁体   中英

Why the value for r is negative from d3.hierarchy in circle packing in d3.js?

I'm using https://observablehq.com/@d3/zoomable-circle-packing as an example to try d3 and circle packing in angular. I have the data which seems to be hierarchical and I'm following along the code that's presented. However, my d3.hierarchy() is giving me strange results. For some reason, all dx , dy and r are all negative. I couldn't figure out why. I'm still reading some guides and books trying to understand if I can figure out how d3.hierarchy works to understand why the values are negative. But I'm bit struggling with that. What's happening in the code and how can I show the circle packing with 3 circles for each object in the array?

I have the codes in stackblitz below.

StackBlitz

The code is here in stackblitz

Code

import { Component, OnInit, ElementRef, ViewChild,  VERSION, AfterViewInit } from '@angular/core';
import * as d3 from 'd3';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit, AfterViewInit  {
 
  private diameter: number;
  private margin = { top: 20, right: 20, bottom: 20, left: 20 };
  private width: number;
  private height: number;
  private svg; any;
  private g: any;
  private svgContainer: ElementRef;
  private color: any;
  private dac: any;
  private pack: any;

  @ViewChild('circleContainer', { static: false }) set content(content: ElementRef) {

    if (content) {
      this.svgContainer = content;
    }

  }

  ngOnInit() {

  
    this.dac = {
    "name": "DAC",
    "children":
    [
        {
            "name":
            [
                "Direction"
            ],
            "children":
            {
                "children":
                [
                    {"name":"leader","score":3.33,"_row":"leader"},
                    {"name":"same_sector","score":3.64,"_row":"same_sector"},
                    {"name":"senior_teams","score":3.81,"_row":"senior_teams"},
                    {"name":"team","score":3.81,"_row":"team"}
                ]
            }
        },
        {
            "title":
            [
                "Alignment"
            ],
            "children":
            {
                "children":
                [
                    {"name":"leader","score":3,"_row":"leader"},
                    {"name":"same_sector","score":3.51,"_row":"same_sector"},
                    {"name":"senior_teams","score":3.48,"_row":"senior_teams"},
                    {"name":"team","score":3.48,"_row":"team"}
                ]
            }
        },
        {
            "title":
            [
                "Commitment"
            ],
            "children":
            {
                "children":
                [
                    {"name":"leader","score":3.67,"_row":"leader"},
                    {"name":"same_sector","score":4.05,"_row":"same_sector"},
                    {"name":"senior_teams","score":3.57,"_row":"senior_teams"},
                    {"name":"team","score":3.57,"_row":"team"}
                ]
            }
        }
    ]
}
    this.createChart();
  
  }

  ngAfterViewInit() {

    this.width = 500 - this.margin.left - this.margin.right;
    this.height = 400 - this.margin.top - this.margin.bottom;

    this.color = d3.scaleLinear<string>()
      .domain([1, 5])
      .range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
      .interpolate(d3.interpolateHcl);

    if (this.svgContainer && this.dac) {

      this.createChart()

    }

  }


    private createSVG(rect) {

    this.svg = d3.select(rect)
      .append('svg')
      .attr('viewBox', `0 0  ${this.width / 2} ${this.height / 2}`)
      .attr('preserveAspectRatio', 'xMinYMin meet')
      .style('display', 'block')
      .style('margin', "0 auto")
      .style('background', this.color(1))
      .style('cursor', 'pointer')
      .attr('class', 'bubble-chart')

    this.diameter = +this.svg.attr("width")
    console.log("diameter: ", this.diameter);

    this.pack = data => d3.pack()
      .size([this.diameter - this.margin.left, this.diameter - this.margin.right])
      .padding(2)
      (d3.hierarchy(data)
        .sum(d => d.score)
        .sort((a, b) => b.value - a.value))
      

  }

 private createChart() {

    let that = this;

    const rect = this.svgContainer.nativeElement;

    this.createSVG(rect);

    const root = this.pack(this.dac);
    let focus = root;
    let view;

    console.log(root);
    this.svg = this.svg
      .on('click', (event) => zoom(event, root));

    const node = this.svg.append('g')
      .selectAll('circle')
      .data(root.descendants().slice(1))
      .join('circle')
      .attr('fill', d => d.children ? this.color(d.depth) : "white")
      .attr('pointer-events', d => !d.children ? "none" : null)
      .on('mouseover', function () { d3.select(this).attr('stroke', '#999'); })
      .on('mouseout', function () { d3.select(this).attr('stroke', 'null'); })
      .on('click', (event, d) => focus !== d && (zoom(event, d), event.stopPropagation()));

    const label = this.svg.append('g')
      .style('font', '10px Roboto')
      .attr('pointer-events', 'none')
      .attr('text-anchor', 'middle')
      .selectAll('text')
      .data(root.descendants())
      .join('text')
      .style('fill-opacity', d => d.parent === root ? 1 : 0)
      .style('display', d => d.parent === root ? 'inline' : 'none')
      .text(d => d.data.name);


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

    function zoomTo(v) {
      const k = that.width / v[2];

      view = v;

      
      console.log("k: ", k);
      console.log("v:",v);

      label.attr('transform', d => {
        console.log("dx: ", d.x)
        console.log("dy: ", d.y);
        return `translate(${(d.x - v[0]) * k}, ${(d.y - v[1]) * k})`});
      node.attr('transform', d => `translate(${(d.x - v[0]) * k}, ${(d.y - v[1]) * k})`);
      node.attr('r', d => d.r * k);
    }


    function zoom(event, d) {
      const focus0 = focus;
      focus = d;

      const transition = that.svg.transition()
        .duration(event.altKey ? 7500 : 750)
        .tween('zoom', d => {
          const i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2]);
          return t => zoomTo(i(t));
        });

      label
        .filter(function (d) { return d.parent === focus || this.style.display === 'inline'; })
        .transition(transition)
        .style('fill-opacity', d => 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' });
    }



  }

  

}

You have 2 problems:

  1. Your data structure is incorrect, you have objects as the value of your children properties. They should be arrays.

  2. You are setting the SVG viewBox , not its width. Therefore, this...

     this.diameter = +this.svg.attr("width")

    ...is just +null , which is 0 . Because of that, the array in the size() method will have negative values, which explains your main issue. Use your width and height instead.

Here is the forked code: https://stackblitz.com/edit/angular-circle-packing-uyigxs?file=src/app/app.component.ts

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