Search code examples
d3.jschord-diagram

d3 chord diagram that produces parallel ribbons


I currently am working with a chord layout, I would like to have my chords parallel though (illustrated with red overlays).

How do I accomplish this? I tried the different sorting options and none seem to yield the desired results.

enter image description here

code:

var width = 1000;
var height = 600;

var svg = d3.select("#chart")
        .append("svg")
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform", "translate(" + width/2 + "," + height/2 + ")");

var matrix5x5 = [
    [0, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0],   
    [1, 0, 0, 0, 0, 0, 0, 0],   
    [1, 0, 0, 0, 0, 0, 0, 0]            
];
var range5 = ["#848484", "#848484", "#848484", "#848484", "#848484", "#848484", "848484", "848484"];

var chord = d3.layout.chord()
        .padding(0.1)
        .matrix(matrix5x5);

var fill = d3.scale.ordinal()
        .domain(d3.range(range5.length))
        .range(range5);


var innerRadius = Math.min(width, height) * .39;
var outerRadius = innerRadius * 1.1;

svg.append("g")
        .selectAll("path")
        .data(chord.groups)
        .enter().append("path")
        .style("fill", function(d) {
            return fill(d.index);
        })
        .style("stroke", function(d) {
            return fill(d.index);
        })
        .attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
        .on("mouseover", fade(.1))
        .on("mouseout", fade(1));

svg.append("g")
        .attr("class", "chord")
        .selectAll("path")
        .data(chord.chords)
        .enter().append("path")
        .style("fill", function(d) {
            return fill(d.target.index);
        })
        .attr("d", d3.svg.chord().radius(innerRadius))
        .style("opacity", 1);

var range5_artists = ["All", "one", "two", "three", "four", "five", "six", "seven"];

svg.selectAll("text")
        .data(chord.groups)
        .enter()
        .append("text")
        .text(function(d) {
            return range5_artists[d.index];
        })
      .each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
      .attr("dy", ".35em")
      .attr("transform", function(d) {
        return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
            + "translate(" + (innerRadius + 35) + ")"
            + (d.angle > Math.PI ? "rotate(180)" : "");
      })
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
        .attr("font-size", "11px")
        .attr("fill", function(d) {
            return  range5[d.index];
        })
        .on("mouseover", fade(.1))
        .on("mouseout", fade(1));


function fade(opacity) {
    return function(g, i) {
        svg.selectAll("g.chord path")
                .filter(function(d) {
                    return d.source.index != i && d.target.index != i;
                })
                .transition()
                .style("opacity", opacity);
    };
}

Solution

  • What you're looking for here is the .sortSubGroups() function. You want to sort based on index, but this information isn't passed to the comparator. So we use a dirty trick that relies on the order of evaluation and always return 1.

    Result here.