block by micahstubbs 0d74dca6457cacee52e6

custom tree layout sketch

Full Screen

an artisan hand-coded sketch of a custom tree layout. this very likely could be shaped into a proper custom tree layout that would be useful for the deeper tree structures found in wild-caught data.

based on a bespoke felt-marker-on-graph-paper design, reproduced below.

sketch

further research may lead to an bl.ock that builds on these examples:

Pedigree Tree “Elbow” Dendrogram Variable Offset Animated Links Tree Layout Orientations

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>custom tree layout sketch</title>
    <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <style>
    </style>
  </head>
  <body>
    <script>

// Extract the width and height that was computed by CSS.
var chartDiv = document.getElementById("vis");
var width = 960;
var height = 1440;
var nodeRadius = 6;
var linkWidth = '3px';

// Use the extracted size to set the size of an SVG element.
var svg = d3.select('body').append("svg")
  .attr("width", width)
  .attr("height", height);

var background = svg.append('rect')
  .classed('background', true)
  .attr({
    'x': 0,
    'y': 0,
    'rx': 10,
    'ry': 10,
    'height': height,
    'width': width
  })
  .style({
    'fill': '#EEF1F6',
    'stroke': 'gray',
    'stroke-width': '0px'
  });

var nodes = [
  {
    'id': 0,
    'x': 75,
    'y': 75
  },
  {
    'id': 1,
    'x': 885,
    'y': 72
  },
  {
    'id': 2,
    'x': 885,
    'y': 247
  },
  {
    'id': 3,
    'x': 885,
    'y': 840
  },
  {
    'id': 4,
    'x': 885,
    'y': 1014
  },
  {
    'id': 5,
    'x': 885,
    'y': 1189
  },
  {
    'id': 6,
    'x': 885,
    'y': 1363
  },
  {
    'id': 7,
    'hidden': true,
    'x': 308,
    'y': 75
  },
  {
    'id': 8,
    'hidden': true,
    'x': 308,
    'y': 247
  }
];

/////////////////////////////////////////////////////////////////////////////////
///// draw the hidden nodes /////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////

// do this first, so that our hiddden nodes
// ghost circles appear below our link paths
d3.select('svg').selectAll('.hiddenNodes')
  .data(nodes.filter(function(d) { 
    return d.hidden === true;
  }))
  .enter()
  .append('g')
  .classed('hiddenNodes', true)
  .classed('nodes', true)
    .append('circle')
    .attr('r', nodeRadius)
    .attr('cx', function(d) {
      return d.x;
    })
    .attr('cy', function(d) {
      return d.y;
    })
    .style({
      'fill': '#CFD7E6',
      'fill-opacity': .8
    });


/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
     
var links = [
  {
    'source': 7,
    'target': 8,
    'color': '#7F8DE1',
    'id': 0
  },
  {
    'source': 8,
    'target': 2,
    'color': '#7F8DE1',
    'id': 1
  },
  {
    'source': 0,
    'target': 1,
    'color': '#4BC076',
    'id': 2
  },
  {
    'source': 0,
    'target': 3,
    'color': '#F88962',
    'id': 3
  },
  {
    'source': 0,
    'target': 4,
    'color': '#F88962',
    'id': 4
  },
  {
    'source': 0,
    'target': 5,
    'color': '#F88962',
    'id': 5
  },
  {
    'source': 0,
    'target': 6,
    'color': '#EF7EAD',
    'id': 6
  }
]

/////////////////////////////////////////////////////////////////////////////////
////// draw the links ///////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////

d3.select('svg').selectAll('.links')
  .data(links)
  .enter()
  .append('g')
  .classed('links', true)
    .append('path')
    .attr('d', function(d) {
      return generateLinkPath(d);
    })
    .style('stroke', function(d) {
      return d.color;
    })
    .style('stroke-width', linkWidth)
    .style({
      'fill': 'none',
      'stroke-linecap': 'square'  
    })  

function generateLinkPath(link) {

  // a d3 path generator to draw a nice
  // bezier curve
  var bezierCurve = d3.svg.line()
  .x(function(d) { return d[0]; })
  .y(function(d) { return d[1]; })
  .interpolate('basis');

  var sourceId = link['source'];
  var targetId = link['target'];

  var xS = nodes[sourceId]['x'];
  var yS = nodes[sourceId]['y'];
  var xT = nodes[targetId]['x'];
  var yT = nodes[targetId]['y'];

  switch (link.id) {
    case 6:
      return bezierCurve([
        [xS, yS],
        [Math.abs(xS-.6*xT), Math.abs(yS-yT)],
        [xT, yT]
      ]);

    default:
      var path = 'M ' + xS + ' ' + yS + ' ' + xT + ' ' + yT;
      return path;
  }
}

/////////////////////////////////////////////////////////////////////////////////
///// draw the nodes ////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////

// do this second, so that our node circles appear
// above our link paths
d3.select('svg').selectAll('.visibleNodes')
  .data(nodes.filter(function(d) { 
    return d.hidden == undefined;
  }))
  .enter()
  .append('g')
  .classed('visibleNodes', true)
  .classed('nodes', true)
    .append('circle')
    .attr('r', nodeRadius)
    .attr('cx', function(d) {
      return d.x;
    })
    .attr('cy', function(d) {
      return d.y;
    })
    .style({'fill': '#00396B'});

/////////////////////////////////////////////////////////////////////////////////
// draw the labels for the nodes ////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////

d3.selectAll('.nodes')
  .append('text')
    .attr('x', function(d) {
      return d.x;
    })
    .attr('y', function(d) {
      return d.y;
    })
  .attr('dx', function(d) {
    if(d.id === 0) return '-1.2em';
    if(d.id === 7) return  '-.25em';
    if(d.id === 8) return  '.35em';
    return               '.7em';
  })
  .attr('dy', function(d) {
    if(d.id === 7) return '-.5em';
    if(d.id === 8) return  '-.35em';
    return                '.35em';
  })
  .style('stroke', 'none')
  .style('fill', '#A7B8D1')
  .text(function(d) { return d.id }); 

    </script>
  </body>
</html>