:
- node, ,
- node ( node, )
- , node , node
- node
- node ,
- node, (
, )
- , node , , .
:
https://jsfiddle.net/toh7d9tq/1/
( ): p {node, offset}. , .
, ( , ), , .
function key(p) {
return p.time+"_"+p.value
}
var root={time:200, value:height, series:[], direction:-Math.PI/2};
var nodes = d3.map([root], key);
series.forEach(function(s){
s.pWithOffset=[];
var parent=root;
s.p.forEach(function(d) {
var n=nodes.get(key(d));
if (!n) {
n={time:d.time,
value:d.value,
parent:parent,
series:[],
direction:Math.atan2(d.value-parent.value, d.time-parent.time)};
nodes.set(key(n),n);
if (!parent.children) parent.children=[];
parent.children.push(n);
}
parent=n;
})
s.leafNode=parent;
parent.series.push(s);
})
nodes.values().forEach(function(n){
if (n.children)
n.children.sort(function (a,b){
if (a.direction>n.direction)
return a.direction-b.direction;
});
});
function listSeries(n) {
if (!n.children) return;
n.children.forEach(listSeries);
n.series=d3.merge(n.children.map(function(c){return c.series}));
}
listSeries(root);
function listOffsets(n) {
var offset=0;
n.series.forEach(function(s){
s.pWithOffset.push( {node:n, offset:offset+s.w/2})
offset+=s.w;
})
n.totalOffset=offset;
if (n.children)
n.children.forEach(listOffsets);
}
listOffsets(root);
:
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return (d.node.time-Math.sin(d.node.direction)*(d.offset-d.node.totalOffset/2)); })
.y(function(d) { return (d.node.value+Math.cos(d.node.direction)*(d.offset-d.node.totalOffset/2)); })
;
LineGroup.selectAll(".line")
.data(series)
.enter().append("path")
.attr("class", "line")
.attr("d", function(d){ return line(d.pWithOffset); })
.attr("stroke", function(d){ return d.c; })
.attr("stroke-width", function(d){ return d.w; })
.attr("fill", "none");