block by jwilber c80548a912b03d6dafdef40305efd291

apply pack layout to existing nodes

Full Screen

Built with blockbuilder.org

index.html

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <style>
    body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
  </style>
</head>
<svg style="width:600px;height:600px;" ></svg>

<body>
  <script>
    const width = 500;
    const height = 400;
    const svgD3 = d3.select('svg')
    let sampleData = d3.range(250).map((d,i) => (
      {r: 40 - i * 0.5,
       value: width/2 + d3.randomNormal(0, 1.5)() * 50,
       nodeGroup: i <= 23 ? 'llama' : i <= 39 ? 'resp' : 'dsn',
                 dotValue: i % 2 === 0 ? 
                 d3.randomNormal(7, 2.5)().toFixed(1): 
                 d3.randomNormal(4.5, .75)().toFixed(1),
      permDsn: d3.randomNormal(0, 1)().toFixed(1)
       }));
    
    const x = d3.scaleLinear()
    .domain(d3.extent(sampleData.filter(d => d.nodeGroup === 'dsn'), d => +d.permDsn))
    .rangeRound([width/4, width/1.5]); // hist width(left, right)

const nbins = 18;
    
    function update(){

    let data = sampleData

    //histogram binning
    const histogram = d3.histogram()
      .domain(x.domain())
      .thresholds(x.ticks(nbins))
      .value(d => d.permDsn);

    //binning data and filtering out empty bins
    const bins = histogram(data);

    //g container for each bin
    let binContainer = svgD3.append('g')
          .attr('transform', `translate({x(d.x0)}, ${-height/8})`)
          .selectAll(".gBin")
      .data(bins);

//     binContainer.exit().remove()

    let binContainerEnter = binContainer.enter()
      .append("g")
        .attr("class", "gBin")
        .attr("transform", d => `translate(${x(d.x0)}, ${height})`)

    //need to populate the bin containers with data the first time
    binContainerEnter.selectAll(".preHistPosit")
        .data((d,i) => d.map((p, j) => {
          return {idx: j,
                  dataIndex: i,
                  value: p.Value,
                  radius: (x(d.x1)-x(d.x0))/2
                }
        }))
      .enter()
      .append("circle")
      .attr('class', 'histCirc')
      .attr('testStatValue', d => d.permDsn)
      .attr('', function(d,i) {
        if (i < 1 && d.dataIndex == 20) {
          d3.select(this).classed('response1', true)
        } else if (i < 1 && d.dataIndex == 9) {
          d3.select(this).classed('response2', true)
        } else if (i < 1 && d.dataIndex == 5) {
          d3.select(this).classed('response3', true)
        } else if (i < 1 && d.dataIndex == 11) {
          d3.select(this).classed('response4', true)
        } else if (i < 1 && d.dataIndex == 7) {
          d3.select(this).classed('response5', true)
        } else if (i < 1 && d.dataIndex == 6) {
          d3.select(this).classed('response6', true)
        } else if (i < 1 && d.dataIndex == 13) {
          d3.select(this).classed('response7', true)
        } else if (i < 1 && d.dataIndex == 8) {
          d3.select(this).classed('response8', true)
        } else if (i < 1 && d.dataIndex == 2) {
          d3.select(this).classed('response9', true)
        } else {
          d3.select(this).classed('histogramNode', true)
        }
      })
      .attr('', function(d) {
        if (d.dataIndex > 19) { // determine which test-stat nodes to highlight
          d3.select(this).classed('extreme', true)
        } else {
          d3.select(this).classed('notExtreme', true)
        }
      })
      .transition()
        .attr("cx", 0) //g element already at correct x pos
        .attr("cy", d => - d.idx * 2 * d.radius - d.radius - (height/8)) // control height here
        .attr("r", 10)
        .transition()
          .duration(800)
          .attr('r', 8)
          .transition()
          .attr('stroke-width', .3)
          .attr('stroke', 'black')

    binContainerEnter.merge(binContainer)
        .attr("transform", d => `translate(${x(d.x0)}, ${height})`)

    //enter/update/exit for circles, inside each container
    let dots = binContainer.selectAll("circle")
        .data(d => d.map((p, i) => {
          return {idx: i,
                  value: p.Value,
                  radius: (x(d.x1)-x(d.x0))/2
                }
        }))


    //UPDATE old elements present in new data.

    //ENTER new elements present in new data.
    dots.enter()
      .append("circle")
        .attr("class", "enter")
        .attr("cx", 0) //g element already at correct x pos
        .attr("cy", function(d) {
          return - d.idx * 2 * d.radius - d.radius; })
        .attr("r", 10)
      .merge(dots)
        .transition()
          .duration(500)
          .attr("r", function(d) {
          return (d.length==0) ? 10 : d.radius; })
};//update
    
    update();
  </script>
</body>