d3可视化实战03:神奇的superformula
需求驱动实现
前文讲过了D3的数据驱动机制,中间所举的例子都很简单。例如那个demo里面,绑定的数据是一个简单的数组,实现的图元也仅仅是一堆用SVG画的circle。但是现实世界中我们往往会遇到复杂的需求,例如我就遇到了这样一个需求:数据是一个复杂的对象数组,而与之绑定的图元是一个可变图形。该图形可以根据与他绑定的数据中的具体参数,在圆形、方块、三角之间切换,并且要求过渡自然。
面对这个需求,最直接的做法是把圆形、方块、三角用SVG的<circle>圆形标签,<rect>矩形标签以及<polygon>多边形标签来分别实现。具体用D3实现,就是创建一个<g>集合标签作为数据绑定对象,根据数据参数的变化,remove里面的原有图形,add新的图形,从而实现数据驱动的图形更新。但是这种方法有一个很难解决的问题,就是图形切换时的过渡动画很难平滑。因为每个图形都是独立的标签,除了采用淡入淡出之类的过渡方法外,很难想到有什么更好的过渡效果。并且出于对代码简化的考虑,g标签内包裹其他的图形标签的方式,也增加了复杂度。
那么还有其他方案吗?如果能有一个万能图形标签来来实现各种图形,把数据直接绑定给它,那就再好不过了。事实上SVG的<path>路径标签就可以实现这一点。在我之前的文章”d3可视化实战01:理解SVG元素特性“中已经提到,路径功能非常强大几乎可以描绘任何图形。唯一的问题是,如何指定Path的具体参数。对于一般情况,我们可以自己设定参数。而在这里,我打算用一个数学模型来指定path的具体参数,那就是superfomula超级方程式。最终实现的效果果然非常好,动画过渡非常平滑,图元本身也很简单。下面让我们看看这个superfomula究竟是何方神圣吧。
about超级方程式
我们知道,很多数学函数都可以用解析式的方式表示,亦可根据变量和自变量的值在不同坐标系下绘成各种图形。Johan Gielis博士提出了一个函数,可以描述自然界中发现的众多复杂图形和曲线,它就是超级方程式superfomula。
超级方式的解析式如下:
在极坐标系下,r代表半径,代表角度,a,b,m,n1,n2,n3是可变参数。通过调整参数的值,就可以绘出各种图形。下图展示了a=b=1的情况下,m,n1,n2,n3取不同值的时候superfomula所展示的图形:
只通过控制这些参数,就能在极坐标系下绘制如此不同的各种2D图形,是不是很神奇?这就是数学这一自然科学的王冠学科的魅力。
关于superfomula的更多介绍:
superfomula的发表者Johan Gielis博士(1962-)原本的研究方向是园艺工程和生物学。在他的早期研究阶段他就感兴趣于使用数学模型表征生物生长性状。1994年发表的论文中他就开始开始使用曲线描述自然形状,在1997年的论文中他发现广义的曲线模型适用于任何对称形状。在2003年发表于美国植物学杂志上的论文A generic geometric transformation that unifies a large range of natural and abstract shapes中,他提出了superfomula。superfomula是超级椭圆公式superellipse的扩展,但具有更广泛的实用性。此后数百篇论文都引用了superfomula,并且以superfomula为指导的计算机绘图程序也随之出现。2004年johan Gielis博士收到了InterTech技术创新卓越奖,该奖主要颁发给对平面艺术及相关产业产生重大影响的技术发明。superfomula从生物技术研究中诞生,在数学领域中升华,并最终应用到计算机、平面设计等领域。可谓是跨学科研究应用的最好案例之一。
superfomula也可以扩展到3维,4维甚至更多维度。例如3维情况下,图形可以通过两个superfomula r1, r2来生成。其极坐标系与笛卡尔坐标系之间转换关系为:
其中,
变量值域为[ -π/2 , π/2] (latitude维度), θ变量值域为 [ -π, π] (longitude精度).
案例及代码实现
在github上,已经有人用D3实现了基于superfomula的图形绘制程序。大家请点击这里:http://bl.ocks.org/mbostock/1021103. 该程序的关键是基于D3的superfomula开源插件。本着学习的目的,这里保存了该插件的源码,你可以复制它然后保存为d3-superfomula.js来使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
(function() {
var _symbol = d3.svg.symbol(),
_line = d3.svg.line();
d3.superformula = function() {
var type = _symbol.type(),
size = _symbol.size(),
segments = size,
params = {};
function superformula(d, i) {
var n, p = _superformulaTypes[type.call(this, d, i)];
for (n in params) p[n] = params[n].call(this, d, i);
return _superformulaPath(p, segments.call(this, d, i), Math.sqrt(size.call(this, d, i)));
}
superformula.type = function(x) {
if (!arguments.length) return type;
type = d3.functor(x);
return superformula;
};
superformula.param = function(name, value) {
if (arguments.length < 2) return params[name];
params[name] = d3.functor(value);
return superformula;
};
// size of superformula in square pixels
superformula.size = function(x) {
if (!arguments.length) return size;
size = d3.functor(x);
return superformula;
};
// number of discrete line segments
superformula.segments = function(x) {
if (!arguments.length) return segments;
segments = d3.functor(x);
return superformula;
};
return superformula;
};
function _superformulaPath(params, n, diameter) {
var i = -1,
dt = 2 * Math.PI / n,
t,
r = 0,
x,
y,
points = [];
while (++i < n) {
t = params.m * (i * dt - Math.PI) / 4;
t = Math.pow(Math.abs(Math.pow(Math.abs(Math.cos(t) / params.a), params.n2)
+ Math.pow(Math.abs(Math.sin(t) / params.b), params.n3)), -1 / params.n1);
if (t > r) r = t;
points.push(t);
}
r = diameter * Math.SQRT1_2 / r;
i = -1; while (++i < n) {
x = (t = points[i] * r) * Math.cos(i * dt);
y = t * Math.sin(i * dt);
points[i] = [Math.abs(x) < 1e-6 ? 0 : x, Math.abs(y) < 1e-6 ? 0 : y];
}
return _line(points) + "Z";
}
var _superformulaTypes = {
asterisk: {m: 12, n1: .3, n2: 0, n3: 10, a: 1, b: 1},
bean: {m: 2, n1: 1, n2: 4, n3: 8, a: 1, b: 1},
butterfly: {m: 3, n1: 1, n2: 6, n3: 2, a: .6, b: 1},
circle: {m: 4, n1: 2, n2: 2, n3: 2, a: 1, b: 1},
clover: {m: 6, n1: .3, n2: 0, n3: 10, a: 1, b: 1},
cloverFour: {m: 8, n1: 10, n2: -1, n3: -8, a: 1, b: 1},
cross: {m: 8, n1: 1.3, n2: .01, n3: 8, a: 1, b: 1},
diamond: {m: 4, n1: 1, n2: 1, n3: 1, a: 1, b: 1},
drop: {m: 1, n1: .5, n2: .5, n3: .5, a: 1, b: 1},
ellipse: {m: 4, n1: 2, n2: 2, n3: 2, a: 9, b: 6},
gear: {m: 19, n1: 100, n2: 50, n3: 50, a: 1, b: 1},
heart: {m: 1, n1: .8, n2: 1, n3: -8, a: 1, b: .18},
heptagon: {m: 7, n1: 1000, n2: 400, n3: 400, a: 1, b: 1},
hexagon: {m: 6, n1: 1000, n2: 400, n3: 400, a: 1, b: 1},
malteseCross: {m: 8, n1: .9, n2: .1, n3: 100, a: 1, b: 1},
pentagon: {m: 5, n1: 1000, n2: 600, n3: 600, a: 1, b: 1},
rectangle: {m: 4, n1: 100, n2: 100, n3: 100, a: 2, b: 1},
roundedStar: {m: 5, n1: 2, n2: 7, n3: 7, a: 1, b: 1},
square: {m: 4, n1: 100, n2: 100, n3: 100, a: 1, b: 1},
star: {m: 5, n1: 30, n2: 100, n3: 100, a: 1, b: 1},
triangle: {m: 3, n1: 100, n2: 200, n3: 200, a: 1, b: 1}
};
d3.superformulaTypes = d3.keys(_superformulaTypes);
})();
|
d3可视化实战03:神奇的superformula的更多相关文章
- d3可视化实战00:d3的使用心得和学习资料汇总
最近以来,我使用d3进行我的可视化工具的开发已经3个月了,同时也兼用其他一些图表类库,自我感觉稍微有点心得.之前我也写过相关文章,我涉及的数据可视化的实现技术和工具,但是那篇文章对于项目开发而言太浅了 ...
- d3可视化实战02:理解d3数据驱动的真正含义
前文中已经提到,SVG从诞生之初起就可以非常方便地使用javascript脚本语言来进行其DOM对象的控制.当然,控制的方法有很多,有直接控制SVG对象的方法,例如使用原生js:有帮你封装一下图形接口 ...
- d3可视化实战04:事件绑定机制
首先说明,d3支持所有的JS事件——甚至其他代码的自定义事件.这里有一个列表,The MDN Event Reference, 包含了几乎所有浏览器创建的事件类型.大家有需要可以去查看. D3的事件绑 ...
- d3可视化实战01:理解SVG元素特性
一. SVG简介 ————————————————————————————————————————————————————————————————— SVG是一种和图像分辨率无关的矢量图形格式,它使用 ...
- Pentaho6.1中D3可视化库的集成及数据联动的实现
1.软件环境 操作系统版本:Win 10 64位 可视化图形库:D3 Pentaho版本: biserver-ce-6.1.0.1-196 2.对D3的简单介绍 D3允许你将任意的数据绑定到文档对象模 ...
- Expression Blend 的点滴(2)--利用可视化状态创建神奇翻转动画
原文:Expression Blend 的点滴(2)--利用可视化状态创建神奇翻转动画 首先,来看下实现后的效果: 关于VisulaState VisualState 指定控件处于特定状态时的外观.例 ...
- Java并发编程实战 03互斥锁 解决原子性问题
文章系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 摘要 在上一篇文章02Java如何解决可见性和有序性问题当中,我们解决了可见性和 ...
- Python 3爬虫、数据清洗与可视化实战PDF高清完整版免费下载|百度云盘
百度云盘:Python 3爬虫.数据清洗与可视化实战PDF高清完整版免费下载 提取码: 内容简介 <Python 3爬虫.数据清洗与可视化实战>是一本通过实战教初学者学习采集数据.清洗和组 ...
- D3 数据可视化实战 笔记
学习真是件奇妙的事情.这本书我之前都看过,有些的知识点却完全没有印象. 总结:把用到的知识好好研究:平时可以了解其他技术的基础,把相关的资料和难点记录下来. javascript陷阱 1.变量类型 v ...
随机推荐
- Java---常用基础面试知识点
综合网上的一点资源,给大家整理了一些Java常用的基础面试知识点,希望能帮助到刚开始学习或正在学习的学员. 1.抽象 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方 ...
- gcc编译命令
g++ demo.cpp -o demo.exe
- openstack 使用集锦
- openStack telemetry/ceilometry 云平台资源监控度量
- Selenium各种工具比较
Selenium 1.0 Selenium 1.0是第一个基于浏览器的开源自动化测试工具.它可以使用所有支持http库的编程语言,也可以运行在所有支持javascript的浏览器上.当然它 也有它的缺 ...
- OpenStack入门之初步认识
一.OpenStack 入门 之 基础知识 二.OpenStack 入门 之 基本组件 三.OpenStack 入门 之 各组件解析(基础) 四.OpenStack 入门 之 各组件解析(进阶) 五. ...
- F - Count the Colors - zoj 1610(区间覆盖)
有一块很长的画布,现在想在这块画布上画一些颜色,不过后面画的颜色会把前面画的颜色覆盖掉,现在想知道画完后这块画布的颜色分布,比如 1号颜色有几块,2号颜色有几块.... *************** ...
- java笔记15之this
this:是当前类的对象引用,记为该类的一个对象 注意:谁调用这个方法,在这个方法内部的this就是代表谁 解决场景: 解决局部变量隐藏成员变量 class Student { private Str ...
- javascript对链接进行动态处理化
// $(function(){// var aLink=$('.nav-content .nav li a'); // 选中所有a// var aText=['在线搭配','星网学院','推客联盟' ...
- Docker 初级实践
Docker 应用 优势 与虚拟相比Docker更加轻量高效,更加方便移植.虚拟机提供的是完整的操作系统环境,包含了大量类似硬件驱动.虚拟处理器.网络接口等等并不需要的信息,也需要比较长时间的启动,同 ...