记得上次看d3应该是1年前的事情了,当时还一边看一边写了d3(v5.7)的一个学习笔记:https://www.cnblogs.com/eco-just/tag/d3/

后来转战three.js就没继续研究了(其实也是感觉api层面的东西也没有深入研究的必要,何况后续项目也不会用到这些东西)。

期间也有同行通过博客问过弦图的问题,出于种种原因吧,当时并没有深入研究。

但是今天!我们就结合d3的3.5.16版本来深入解析一下d3的弦图吧。(demo是找的简书上这为同学的笔记:https://www.jianshu.com/p/4b44c708c2da

先上效果:

step1:根据数据初始化布局

code:

// 初始数据
var city_name = [ "北京" , "上海" , "广州" , "深圳" , "香港" ];
var population = [
[ , , , , ],
[ , , , , ],
[ , , , , ],
[ , , , , ],
[ , , , , ]
]; // 弦布局初始化
var chord_layout = d3.layout.chord()
.padding(0.03)
.sortSubgroups(d3.descending)
.matrix(population); // 获取弦布局初始化后的数据
var groups = chord_layout.groups();
var chords = chord_layout.chords();

解析:

population数据表格化

  北京 上海 广州 深圳 香港
北京 1000 3045 4567 1234 3714
上海 3214 2000 2060 124 3234
广州 8761 6545 3000 8045 647
深圳 3211 1067 3214 4000 1006
香港 2146 1034 6745 4764 5000

先用d3.layout.chord()这个api传入数据,初始化布局所需要的数据groups、chords;

groups数据5条,5个城市,根据population所占权重分配圆弧的大小,在上述数据上的反应就是startAngle和endAngle;

chords数据15条,5个城市选两个(source,target),根据排列组合应该是5+4+3+2+1=15种(source,target可以相同);

step2:绘制画布和计算内外圆半径

// svg画布
var width = ;
var height = ;
var svg = d3.select(".d3content")
.append("svg")
.attr("width",width)
.attr('height', height)
.append("g")
.attr('transform', 'translate(' + width/ + "," + height/ + ")"); var color20 = d3.scale.category20(); var innerRadius = width/ * 0.7;
var outerRadius = innerRadius * 1.1;

解析:

.d3content是画布依赖的根元素dom,上述代码将会在600X600的画布上绘制接下来的弦图;

step3:绘制外圆和文字

    var outer_arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
//绘制外圆(5个城市)
var g_outer = svg.append("g");
g_outer.selectAll("path")
.data(groups)
.enter()
.append("path")
.style("fill",function(d) {
return color20(d.index);
})
.style("stroke",function(d) {
color20(d.index);
})
.attr("d",outer_arc) // 此处调用了弧生成器
;
  //绘制文字
g_outer.selectAll("text")
.data(groups)
.enter()
.append("text")
.each(function(d,i) { // 对每个绑定的数据添加两个变量
d.angle = (d.startAngle + d.endAngle) / ;
d.name = city_name[i];
})
.attr("dy",".35em")
.attr('transform', function(d) { // 平移属性
var result = "rotate(" + (d.angle*/Math.PI) + ")";
result += "translate(0," + - * (outerRadius + ) + ")";
if (d.angle > Math.PI * / && d.angle < Math.PI * / )
result += "rotate(180)";
return result;
})
.text(function(d) {
return d.name;
});

效果图:

注意上述有一句代码:

.attr("d",outer_arc)   // 此处调用了弧生成器

对于每个path都会根据这个函数来绘制,而这个函数对于对应源码里的d3.svg.arc,并且这里有个隐藏的东西:

.attr("d",out_arc),第二个参数执行的时候(他是一个函数),会将数据作为实参传给他,于是到了源码里:

d3.svg.arc = function() {
var innerRadius = d3_svg_arcInnerRadius,
    outerRadius = d3_svg_arcOuterRadius,
     cornerRadius = d3_zero,
     padRadius = d3_svg_arcAuto,
     startAngle = d3_svg_arcStartAngle,
     endAngle = d3_svg_arcEndAngle,
     padAngle = d3_svg_arcPadAngle;
function arc() {
var r0 = Math.max(, +innerRadius.apply(this, arguments)),
      r1 = Math.max(, +outerRadius.apply(this, arguments)),
      a0 = startAngle.apply(this, arguments) - halfπ,
      a1 = endAngle.apply(this, arguments) - halfπ,
      da = Math.abs(a1 - a0),
      cw = a0 > a1 ? : ;
if (r1 < r0) rc = r1, r1 = r0, r0 = rc;
if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, - cw) : "") + "Z";
var rc, cr, rp, ap, p0 = , p1 = , x0, y0, x1, y1, x2, y2, x3, y3, path = [];
  .....
}

这个arguments就是如下这样的单条数据:

最终根据传入的数据,加上一系列的逻辑处理返回了一个path节点的d属性值,具体的判断逻辑,我截图一张供各位欣赏,如果你有兴趣可以逐个去找他的函数:

最后,由这个属性值绘制了一个path节点:

后面的绘制文字就不多说了,注意一点,回调函数形参里面的d是data(数据)的意思,i是index(索引)的意思。

step4:绘制内弦chord

    // 弦生成器
var inner_chord = d3.svg.chord()
.radius(innerRadius);
console.log(inner_chord) // 添加g元素,接下来在这个元素里面绘制chord
var g_inner = svg.append("g")
.attr("class","chord"); g_inner.selectAll("path")
.data(chords)
.enter()
.append("path")
.attr("d",inner_chord) // 调用弦的路径值
.style("fill",function(d,i) {
return color20(d.source.index);
})
.style("opacity",)
;

为了大家看得清晰,我把之前绘制的外圆注释了:

解析:

同理,这里通过d3.svg.chord来绘制弦,依据的数据是:

同样贴上源码的主要部分:

d3.svg.chord = function() {
var source = d3_source,
     target = d3_target,
     radius = d3_svg_chordRadius,
     startAngle = d3_svg_arcStartAngle,
     endAngle = d3_svg_arcEndAngle;
function chord(d, i) {
var s = subgroup(this, source, d, i),
      t = subgroup(this, target, d, i);
return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z";
}

equals(s,t)判断两个端点是否相同来决定绘制的方式;

我们看到这里绘制路径,主要用到了两个函数arc()和curve();

   function arc(r, p, a) {
return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p;
}
function curve(r0, p0, r1, p1) {
return "Q 0,0 " + p1;
}

关于svg的path绘制中各参数的含义,下面给一张图,这里就不多说了:

所以弦主要就是由svg内置的弧绘制api来绘制的(普通的弧线/贝塞尔曲线)!

深入解析d3弦图的更多相关文章

  1. D3.js系列——布局:弦图和集群图/树状图

    一.弦图 1.弦图是什么 弦图(Chord),主要用于表示两个节点之间的联系的图表.两点之间的连线,表示谁和谁具有联系. 2.数据 初始数据为: var city_name = [ "北京& ...

  2. 【 D3.js 入门系列 --- 9.3 】 弦图生产

    我个人的博客: www.ourd3js.com csdn博客为: blog.csdn.net/lzhlzz 转载请注明出处,谢谢. 弦图( Chord ),主要用于表示两个节点之间的联系.例如以下图: ...

  3. D3.js 弦图的制作

    这是一种用于描述节点之间联系的图表. 1. 弦图是什么 弦图(Chord),主要用于表示两个节点之间的联系. 两点之间的连线,表示谁和谁具有联系: 线的粗细表示权重: 2. 数据 初始数据为: var ...

  4. ZOJ 1015 Fishing Net(弦图判定)

    In a highly modernized fishing village, inhabitants there make a living on fishery. Their major tool ...

  5. 【BZOJ1006】【HNOI2008】神奇的国度(弦图染色)

    1006: [HNOI2008]神奇的国度 Time Limit: 20 Sec  Memory Limit: 162 MBSubmit: 1467  Solved: 603[Submit][Stat ...

  6. 弦图的判定MCS算法(zoj1015)

    题意:裸的弦图的判定: 弦图定义:给出一个无向连通图,如果每个环中都存在至少一条弦(环中存在不相邻的两点直接相连)这样的图叫做弦图: 转载:http://blog.csdn.net/crux_d/ar ...

  7. ZOJ 1015 Fishing Net(判断弦图)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=15 题意:给定一个图.判断是不是弦图? 思路:(1)神马是弦图?对于一 ...

  8. BZOJ 1006 神奇的国度(弦图的染色数)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1006 题意:给定一个弦图,求最小染色数.就是用最小数目的颜色进行染色使得任意两个相邻的节 ...

  9. bzoj 1242: Zju1015 Fishing Net 弦图判定

    1242: Zju1015 Fishing Net弦图判定 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 214  Solved: 81[Submit ...

随机推荐

  1. RxJava——扩展的观察者模式

    在学习RxJava的时候,经常提到观察者与被观察者,这不就是JAVA的观察者模式的运用么?是的,但是跟传统意义的上观察者模式还不太一样,所以Rxjava实际上是一种扩展的观察者模式,所以有必要对这个扩 ...

  2. LVS Nginx和HAproxy的区别,怎么选择最好

    LVS Nginx和HAproxy有什么区别呢? LVS:Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统. Nginx:Nginx是一款轻量级的w ...

  3. python_连接MySQL数据库(未完)

    1.增 # 导入库 import pymysql # 创建连接 conn = pymysql.connect(host='localhost',user='root',password='fuqian ...

  4. poj2942 Knights of the Round Table[点双+二分图染色]

    首先转化条件,把无仇恨的人连边,然后转化成了求有哪些点不在任何一个奇环中. 一个奇环肯定是一个点双,所以想到处理出所有点双,但是也可能有的点双是一个偶环,有的可能是偶环和奇环混杂,不好判. 考察奇环性 ...

  5. git下载代码的两种方式以及eclipse集成git

    1.第一种使用tortoiseGit插件: 链接:https://pan.baidu.com/s/1ANDydwfaaVcUaqZDJWc_BQ 提取码:qgxt a.首先在setting中的Git中 ...

  6. P5057 [CQOI2006]简单题 前缀异或差分/树状数组

    好思路,好思路... 思路:前缀异或差分 提交:1次 题解:区间修改,单点查询,树状数组,如思路$qwq$ #include<cstdio> #include<iostream> ...

  7. Python GUI编程(Tkinter)(一)

    tk官网的教程学习: https://tkdocs.com/tutorial/firstexample.html 学习blog: https://www.cnblogs.com/aland-1415/ ...

  8. HGOI 20191105 题解

    Problem A Joker 老虎和蒜头是好朋友. 夏天过去了,凉爽的秋天来临,老虎和蒜头又有了新的娱乐项目.老虎有一个远房表亲是西伯利亚虎,那里流行着一个纸牌游戏:两位玩家参与游戏,道具是一副54 ...

  9. 【线性代数】2-7:转置与变换(Transposes and Permutation)

    title: [线性代数]2-7:转置与变换(Transposes and Permutation) toc: true categories: Mathematic Linear Algebra d ...

  10. codeforces865C

    Gotta Go Fast CodeForces - 865C You're trying to set the record on your favorite video game. The gam ...