如何将弦图的形状从圆形改为椭圆形?

时间:2016-05-23 12:15:27

标签: d3.js svg chord-diagram

我想用d3js创建和弦图。这是我正在实现的示例代码:

SonataBlockBundle

以下是data.csv文件:

<script src="http://d3js.org/d3.v3.min.js"></script>
<body>
<svg width="960" height="960"></svg>
<script>
d3.text("data.csv", function(error, datas) {
var dataf = d3.csv.parseRows(datas);

dataf.shift();

var text = [];
var nest = d3.nest()
    .key(function(d) { return d[1]; })
    .entries(dataf)
    .sort(function(a, b) { return a.key < b.key ? -1 : 1; });
nest.forEach(function(d) {
    text.push(d.key);
});
nest = d3.nest()
    .key(function(d) { return d[2]; })
    .entries(dataf)
    .sort(function(a, b) { return a.key < b.key ? -1 : 1; });
nest.forEach(function(d) {
    text.push(d.key);
});
var textnested = [];
d3.nest()
    .key(function(d) { return d; })
    .entries(text)
    .forEach(function(d) {
        textnested.push(d.key);
    });
if(textnested.length < text.length) {
    var inDirection = 1;
    text = textnested;
    text.sort(function(a, b) { return a < b ? -1 : 1; });
}
nest = d3.nest()
    .key(function(d) { return d[0]; })
    .entries(dataf);
if(nest.length < dataf.length) {
    nest.forEach(function(d) {
        d.values.forEach(function(e) { e.push(1/d.values.length); });
    });
}

var matrix = [];
text.forEach(function(d, i) {
matrix[i] = d3.range(text.length).map(function() { return 0; });
});
dataf.forEach(function(d) {
var x = text.indexOf(d[1]),
    y = text.indexOf(d[2]);
if(nest.length < dataf.length) matrix[x][y]+=d[d.length-1];
else matrix[x][y]++;
if(!inDirection) {
    if(nest.length < dataf.length) matrix[y][x]+=d[d.length-1];
    else matrix[y][x]++;
}
});
var chord = d3.layout.chord()
.padding(.04)
.matrix(matrix);
 var svg = d3.select("svg")
.append("g")
.attr("transform", "translate(480, 480)");
 var fill = d3.scale.category20();
 var g = svg.selectAll(".group")
.data(chord.groups)
.enter()
.append("g")
.attr("class", "group");
 g.append("path")
.attr("d", d3.svg.arc().innerRadius(350).outerRadius(370))
.style("fill", function(d) { return fill(d.index); })
.on("mouseover", standOut(0.1))
.on("mouseout", standOut(1));
 g.append("text")
.attr("transform", function(d) {
     return "rotate("+(((d.startAngle+d.endAngle)/2) * (180/Math.PI) -    90)+")"
        + "translate(376)" + (((d.startAngle+d.endAngle)/2) > Math.PI ? "rotate(180)" : "");    })
.attr("text-anchor", function(d) { return       ((d.startAngle+d.endAngle)/2) > Math.PI ? "end" : ""; })
.text(function(d) { return text[d.index]; });
svg.selectAll(".chord")
.data(chord.chords)
.enter()
.append("path")
.attr("class", "chord")
.attr("d", d3.svg.chord().radius(350))
.style("fill", function(d) { return fill(d.source.index); })
.style("stroke", function(d) { return   d3.rgb(fill(d.source.index)).darker(); });
function standOut(o) {
return function(d, i) {
    var gstandout = [];
    svg.selectAll(".chord")
        .each(function(e) { e.source.index == i ?  gstandout.push(e.target.index) : e.target.index == i ? gstandout.push(e.source.index) : ""; })
        .filter(function(e) { return e.source.index != i && e.target.index != i; })
        .style("opacity", o);
    svg.selectAll(".group")
        .filter(function(e, j) { return gstandout.indexOf(j) == -1 && j != i; })
        .style("opacity", o);
};
}
});
</script>
 </body>

但我想用椭圆形而不是圆形来表示和弦。有人知道怎么可能吗?非常感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

首先,和弦不是绘制成圆形,而是绘制在图形中心有一个控制点的贝塞尔曲线(哪种看起来像圆圈)。我建议将两个控制点放在靠近边缘的地方,以获得一个类似椭圆的&#34;效果。

我不确定如何从头开始绘制这样的弧,但是可以在绘制路径后编辑路径的d属性。

这是我的editPath功能:

function editPath() {
    var ratio = 0.6;
    var path=d3.select(this).attr("d")
    console.log(path);
    find=path.match(/([\-\.0-9]+)\,([\-\.0-9]+)Q 0,0 ([\-\.0-9]+),([\-\.0-9]+)/);
    if (!find) return;

    path=path.replace(/Q 0,0/,"C "+find[1]*ratio+","+find[2]*ratio+" "+find[3]*ratio+","+find[4]*ratio);

    console.log(path);
    d3.select(this).attr("d", path)
}

此功能执行以下操作:

  • 提取路径命令(path
  • 确定以Q
  • 为中心的贝塞尔曲线(0,0
  • 提取起点和终点(数组find
  • 的坐标
  • 计算新控制点的坐标(find [i] * ratio)
  • 将其注入新的贝塞尔曲线(带有两个控制点,C
  • 用新的
  • 替换原始路径

这只替换了路径中的一条贝塞尔曲线,因此您需要为每个和弦调用两次:

[...]
 .attr("class", "chord")
 .attr("d", d3.svg.chord().radius(350))
 .each(editPath).each(editPath) //call twice!

我在此示例中对此进行了测试:http://bl.ocks.org/mbostock/raw/4062006/ ...它在那里工作,我希望它可以使用d3.svg.chord在任何图表上工作。