深入解析d3弦图
记得上次看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弦图的更多相关文章
- D3.js系列——布局:弦图和集群图/树状图
一.弦图 1.弦图是什么 弦图(Chord),主要用于表示两个节点之间的联系的图表.两点之间的连线,表示谁和谁具有联系. 2.数据 初始数据为: var city_name = [ "北京& ...
- 【 D3.js 入门系列 --- 9.3 】 弦图生产
我个人的博客: www.ourd3js.com csdn博客为: blog.csdn.net/lzhlzz 转载请注明出处,谢谢. 弦图( Chord ),主要用于表示两个节点之间的联系.例如以下图: ...
- D3.js 弦图的制作
这是一种用于描述节点之间联系的图表. 1. 弦图是什么 弦图(Chord),主要用于表示两个节点之间的联系. 两点之间的连线,表示谁和谁具有联系: 线的粗细表示权重: 2. 数据 初始数据为: var ...
- ZOJ 1015 Fishing Net(弦图判定)
In a highly modernized fishing village, inhabitants there make a living on fishery. Their major tool ...
- 【BZOJ1006】【HNOI2008】神奇的国度(弦图染色)
1006: [HNOI2008]神奇的国度 Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 1467 Solved: 603[Submit][Stat ...
- 弦图的判定MCS算法(zoj1015)
题意:裸的弦图的判定: 弦图定义:给出一个无向连通图,如果每个环中都存在至少一条弦(环中存在不相邻的两点直接相连)这样的图叫做弦图: 转载:http://blog.csdn.net/crux_d/ar ...
- ZOJ 1015 Fishing Net(判断弦图)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=15 题意:给定一个图.判断是不是弦图? 思路:(1)神马是弦图?对于一 ...
- BZOJ 1006 神奇的国度(弦图的染色数)
题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1006 题意:给定一个弦图,求最小染色数.就是用最小数目的颜色进行染色使得任意两个相邻的节 ...
- bzoj 1242: Zju1015 Fishing Net 弦图判定
1242: Zju1015 Fishing Net弦图判定 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 214 Solved: 81[Submit ...
随机推荐
- RxJava——扩展的观察者模式
在学习RxJava的时候,经常提到观察者与被观察者,这不就是JAVA的观察者模式的运用么?是的,但是跟传统意义的上观察者模式还不太一样,所以Rxjava实际上是一种扩展的观察者模式,所以有必要对这个扩 ...
- LVS Nginx和HAproxy的区别,怎么选择最好
LVS Nginx和HAproxy有什么区别呢? LVS:Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统. Nginx:Nginx是一款轻量级的w ...
- python_连接MySQL数据库(未完)
1.增 # 导入库 import pymysql # 创建连接 conn = pymysql.connect(host='localhost',user='root',password='fuqian ...
- poj2942 Knights of the Round Table[点双+二分图染色]
首先转化条件,把无仇恨的人连边,然后转化成了求有哪些点不在任何一个奇环中. 一个奇环肯定是一个点双,所以想到处理出所有点双,但是也可能有的点双是一个偶环,有的可能是偶环和奇环混杂,不好判. 考察奇环性 ...
- git下载代码的两种方式以及eclipse集成git
1.第一种使用tortoiseGit插件: 链接:https://pan.baidu.com/s/1ANDydwfaaVcUaqZDJWc_BQ 提取码:qgxt a.首先在setting中的Git中 ...
- P5057 [CQOI2006]简单题 前缀异或差分/树状数组
好思路,好思路... 思路:前缀异或差分 提交:1次 题解:区间修改,单点查询,树状数组,如思路$qwq$ #include<cstdio> #include<iostream> ...
- Python GUI编程(Tkinter)(一)
tk官网的教程学习: https://tkdocs.com/tutorial/firstexample.html 学习blog: https://www.cnblogs.com/aland-1415/ ...
- HGOI 20191105 题解
Problem A Joker 老虎和蒜头是好朋友. 夏天过去了,凉爽的秋天来临,老虎和蒜头又有了新的娱乐项目.老虎有一个远房表亲是西伯利亚虎,那里流行着一个纸牌游戏:两位玩家参与游戏,道具是一副54 ...
- 【线性代数】2-7:转置与变换(Transposes and Permutation)
title: [线性代数]2-7:转置与变换(Transposes and Permutation) toc: true categories: Mathematic Linear Algebra d ...
- codeforces865C
Gotta Go Fast CodeForces - 865C You're trying to set the record on your favorite video game. The gam ...