Monday, February 4, 2013

D3.js arrowhead markers

D3.js has example of Cluster Dendrogram. I adapted the code to show arrowhead markers for paths. Good explanation of markers could be found here (SVG marker arrowhead).
First you need to create merker:

svg.append("defs").append("marker")
    .attr("id", "arrowhead")
    .attr("refX", 6 + 3) /*must be smarter way to calculate shift*/
    .attr("refY", 2)
    .attr("markerWidth", 6)
    .attr("markerHeight", 4)
    .attr("orient", "auto")
    .append("path")
        .attr("d", "M 0,0 V 4 L6,2 Z"); //this is actual shape for arrowhead

Next add marker to the path:

var link = svg.selectAll(".link")
  .data(links)
  .enter().append("path")
  .attr("class", "link")
  .attr("marker-end", "url(#arrowhead)")
  .attr("d", diagonal);

Full listing below:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.node circle {
  fill: #fff;
  stroke: steelblue;
  stroke-width: 1.5px;
}

.node {
  font: 10px sans-serif;
}

.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 1.5px;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
var data = {
    "name": "node"
    , "children": [
        {"name": "child1"}
        , {"name": "child2"}
        , {"name": "child3"}
        , {"name": "child4"}
    ]
};

var width = 1000,
    height = 1000;

var cluster = d3.layout.cluster()
    .size([height, width - 160]);

var diagonal = d3.svg.diagonal()
    .projection(function(d) { return [d.y, d.x]; });

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(100,0)");

svg.append("defs").append("marker")
    .attr("id", "arrowhead")
    .attr("refX", 6 + 3) /*must be smarter way to calculate shift*/
    .attr("refY", 2)
    .attr("markerWidth", 6)
    .attr("markerHeight", 4)
    .attr("orient", "auto")
    .append("path")
        .attr("d", "M 0,0 V 4 L6,2 Z");

var nodes = cluster.nodes(data);
var links = cluster.links(nodes);

var link = svg.selectAll(".link")
  .data(links)
.enter().append("path")
  .attr("class", "link")
  .attr("marker-end", "url(#arrowhead)")
  .attr("d", diagonal);

var node = svg.selectAll(".node")
  .data(nodes)
    .enter().append("g")
        .attr("class", "node")
        .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

node.append("circle")
        .attr("r", 4.5);

node.append("text")
  .attr("dx", function(d) { return d.children ? -8 : 8; })
  .attr("dy", function(d) { return d.children ? -5 : 3; })
  .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
  .text(function(d) { return d.name; });

d3.select(self.frameElement).style("height", height + "px");
</script>
</body>

2 comments:

  1. The link is dead. http://svgquickref.com/properties/marker/index.html

    This can be used instead. http://tutorials.jenkov.com/svg/marker-element.html

    ReplyDelete