Demonstration of an ArcDiagram
layout function with both distance-based and compact level modes. The
compact
mode works better for orthogonal edges, and can use much less vertical
space. The distance
mode works better for curved arc edges.
The alice.json
dataset is the first line from Lewis Carroll's
Alice in Wonderland, parsed with
the Stanford Parser.
xxxxxxxxxx
<style>
.axis {
stroke: #000;
stroke-width: 1px;
}
.node {
fill: #000;
}
.label {
stroke: #000;
font: 12px sans-serif;
}
.link {
stroke: #00F;
stroke-width: 2px;
fill: none;
}
</style>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="arcDiagram.js"></script>
<body>
<form>Display Mode:
<label><input type="radio" name="arcStyle" value="ortho" checked> Orthogonal</label>
<label><input type="radio" name="arcStyle" value="arc"> Arcs</label>
</form>
<form>Level Height Mode:
<label><input type="radio" name="mode" value="compact" checked> Compact</label>
<label><input type="radio" name="mode" value="distance"> Distance</label>
</form>
<svg id="dep1" width=960 height=500>
<defs>
<marker id="arrowhead" refX="1" refY="2" markerWidth="5" markerHeight="4" orient="auto">
<path d="M0,0 L1,2 L0,4 L5,2 Z"/>
</marker>
</defs>
</svg>
<script>
var mode="compact",
arcStyle="ortho",
topMargin=50, // in case of overrun (perhaps i should just fix the code)
upperHeight=300, // where the arcs go
lowerHeight=150, // where the nodes and labels go
width=940, radius=5;
var arcd = d3.arcDiagram()
.linkLevel(mode);
var svg = d3.select("#dep1")
.append("svg:g")
.attr("transform", "translate("+radius*3+","+(upperHeight+topMargin)+")");
d3.json("alice.json", function(error, data) {
arcd.nodes(data.nodes).links(data.links);
arcd();
var xscale = d3.scale.linear()
.domain([0, data.nodes.length])
.range([0, width]);
var yscale = d3.scale.linear()
.domain([0, d3.max(arcd.links().map(function(l) { return l.height; }))+1])
.range([0, upperHeight]);
var arc = pathgen()
.xscale(xscale)
.yscale(yscale);
var nodes = svg.selectAll(".node")
.data(arcd.nodes())
.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function(d, i) {
return "translate(" + xscale(i) + "," + (radius*2) + ")";
});
nodes.append("svg:circle")
.attr("r", 5);
nodes.append("svg:text")
.attr("class", "label")
.attr("text-anchor", "end")
.attr("dx", -radius*2)
.attr("dy", "0.35em")
.attr("transform", "rotate(-90)")
.text(function(d) { return d.value; });
var links = svg.selectAll(".link")
.data(arcd.links())
.enter().append("svg:path")
.attr("class", "link")
.style("marker-end", "url(#arrowhead)")
.attr("d", arc[arcStyle]);
d3.selectAll("input").on("change", function change() {
if (this.name == "mode") mode = this.value;
else if (this.name == "arcStyle") arcStyle = this.value;
arcd.linkLevel(mode);
arcd(); // run the layout
yscale = d3.scale.linear()
.domain([0, d3.max(arcd.links().map(function(l) { return l.height; }))+1])
.range([0, upperHeight]);
arc.yscale(yscale);
links.data(arcd.links())
.attr("d", arc[arcStyle]);
});
});
// tiny path generator for a semicircle or orthogonal path
function pathgen() {
var gen = {},
x = function(x) { return x; },
y = function(y) { return y; };
gen.arc = function(d) {
var x1 = x(d.x1),
x2 = x(d.x2),
base = y(0),
height = y(d.height),
dir = x1 <= x2 ? 1 : 0;
return [
"M", x1, base,
"A", (x2-x1)/2, ",", height, 0, 0, ",", dir, x2, ",", base
].join(" ");
};
gen.ortho = function(d) {
var x1 = x(d.x1),
x2 = x(d.x2),
height = y(d.height),
dir = x1 < x2 ? 1 : -1;
return [
"M", x1, 0,
"v", -(height-10),
"q", 0, -10, dir*10, -10,
"H", x2 - (dir*10),
"q", (dir*10), 0, (dir*10), +10,
"V", -2
].join(" ");
};
gen.xscale = function(a) {
if (!arguments.length) return x;
x = a;
return gen;
}
gen.yscale = function(a) {
if (!arguments.length) return y;
y = a;
return gen;
}
return gen;
}
</script>
</body>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js