2015-05-20 3 views
3

Я создаю диаграмму дуги, где я бы хотел, надеюсь, найти способ предотвратить перекрытие дуг. Пример working bl.ock here.Чередование или предотвращение пересекающихся путей в D3

Arc diagram

Более темные линии в этом случае накладываются друг на друга линий, где несколько узлов разделяют один и тот же край. Я хотел бы предотвратить это, возможно, выполнив два прохода: первый будет чередовать дугу, чтобы идти выше узлов, а не ниже, что дает вид спирали; второй будет рисовать немного большую дугу, если дуга уже существует выше/ниже, чтобы помочь дифференцировать ссылки.

var width = 1000, 
    height = 500, 
    margin = 20, 
    pad  = margin/2, 
    radius = 6, 
    yfixed = pad + radius; 

var color = d3.scale.category10(); 

// Main 
//----------------------------------------------------- 

function arcDiagram(graph) { 
    var radius = d3.scale.sqrt() 
    .domain([0, 20]) 
    .range([0, 15]); 

    var svg = d3.select("#chart").append("svg") 
     .attr("id", "arc") 
     .attr("width", width) 
     .attr("height", height); 

    // create plot within svg 
    var plot = svg.append("g") 
    .attr("id", "plot") 
    .attr("transform", "translate(" + pad + ", " + pad + ")"); 

    // fix graph links to map to objects 
    graph.links.forEach(function(d,i) { 
    d.source = isNaN(d.source) ? d.source : graph.nodes[d.source]; 
    d.target = isNaN(d.target) ? d.target : graph.nodes[d.target]; 
    }); 

    linearLayout(graph.nodes); 
    drawLinks(graph.links); 
    drawNodes(graph.nodes); 
} 

// layout nodes linearly 
function linearLayout(nodes) { 
    nodes.sort(function(a,b) { 
    return a.uniq - b.uniq; 
    }) 

    var xscale = d3.scale.linear() 
    .domain([0, nodes.length - 1]) 
    .range([radius, width - margin - radius]); 

    nodes.forEach(function(d, i) { 
    d.x = xscale(i); 
    d.y = yfixed; 
    }); 
} 

function drawNodes(nodes) { 

    var gnodes = d3.select("#plot").selectAll("g.node") 
    .data(nodes) 
    .enter().append('g'); 

    var nodes = gnodes.append("circle") 
    .attr("class", "node") 
    .attr("id", function(d, i) { return d.name; }) 
    .attr("cx", function(d, i) { return d.x; }) 
    .attr("cy", function(d, i) { return d.y; }) 
    .attr("r", 5) 
    .style("stroke", function(d, i) { return color(d.gender); }); 

    nodes.append("text") 
    .attr("dx", function(d) { return 20; }) 
    .attr("cy", ".35em") 
    .text(function(d) { return d.name; }) 

} 

function drawLinks(links) { 
    var radians = d3.scale.linear() 
    .range([Math.PI/2, 3 * Math.PI/2]); 

    var arc = d3.svg.line.radial() 
    .interpolate("basis") 
    .tension(0) 
    .angle(function(d) { return radians(d); }); 

    d3.select("#plot").selectAll(".link") 
    .data(links) 
    .enter().append("path") 
    .attr("class", "link") 
    .attr("transform", function(d,i) { 
     var xshift = d.source.x + (d.target.x - d.source.x)/2; 
     var yshift = yfixed; 
     return "translate(" + xshift + ", " + yshift + ")"; 
    }) 
    .attr("d", function(d,i) { 
     var xdist = Math.abs(d.source.x - d.target.x); 
     arc.radius(xdist/2); 
     var points = d3.range(0, Math.ceil(xdist/3)); 
     radians.domain([0, points.length - 1]); 
     return arc(points); 
    }); 
} 

Любые указатели на то, как я могу начать приближаться к проблеме?

ответ

1

Для справки: bl.ock. Он показывает исходные пути в сером цвете, а предлагаемые пути - красным.

Первый магазин подсчитывает, сколько раз происходит данный путь:

graph.links.forEach(function(d,i) { 
    var pathCount = 0; 
    for (var j = 0; j < i; j++) { 
    var otherPath = graph.links[j]; 
    if (otherPath.source === d.source && otherPath.target === d.target) { 
     pathCount++; 
    } 
    } 

    d.pathCount = pathCount; 
}); 

Затем, если у вас есть эти данные, я бы использовал эллипс вместо радиальной линии, так как она появляется радиальная линия может рисовать только кривая для окружности:

d3.select("#plot").selectAll(".ellipse-link") 
    .data(links) 
.enter().append("ellipse") 
    .attr("fill", "transparent") 
    .attr("stroke", "gray") 
    .attr("stroke-width", 1) 
    .attr("cx", function(d) { 
    return (d.target.x - d.source.x)/2 + radius; 
    }) 
    .attr("cy", pad) 
    .attr("rx", function(d) { 
    return Math.abs(d.target.x - d.source.x)/2; 
    }) 
    .attr("ry", function(d) { 
    return 150 + d.pathCount * 20; 
    }) 
    .attr("transform", function(d,i) { 
    var xshift = d.source.x - radius; 
    var yshift = yfixed; 
    return "translate(" + xshift + ", " + yshift + ")"; 
    }); 

Обратите внимание, что изменение значения для ry выше будет изменять высоту различных кривых.

Наконец, вам придется использовать клип, чтобы ограничить область каждого эллипса, который фактически показан, чтобы они отображались только под узлами. (Это не сделано в bl.ock)

+0

Спасибо! Это сделал трюк. –