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

喜欢而已 提交于 2021-01-04 07:12:40

问题


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 d.x, d.y 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' });
    }



  }

  

}

回答1:


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



来源:https://stackoverflow.com/questions/63947769/why-the-value-for-r-is-negative-from-d3-hierarchy-in-circle-packing-in-d3-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!