贝塞尔曲线(面)二三维可视化(Three+d3)

在学完 games101 几何后开始实践,可视化贝塞尔曲线

我想实现三维的贝塞尔曲线,用 threejs,但是 threejs 控制太麻烦了,因此,我使用了 d3js 实现二维贝塞尔曲线的控制,threejs 实现三维贝塞尔曲线的可视化

展示一下二三维贝塞尔曲线的样子

功能一:重现二维和三维的贝塞尔曲线;功能二:可对二维贝塞尔曲线进行控制

理论基础

首先我们看三个控制点的贝塞尔曲线是如何画出来的

b0, b1, b2 分别为三个控制点,设置一个自变量 t,t 的取值范围 [0, 1],0 对应一段线条的起始点,1 对应线段的终点。对于第一条线段 b0 b1,t=0 代表 b0 处,t=1 代表 b1 处。设 t 此时为定义域内的某个值,如 0.3,在 b0 b1,b1 b2,上分别取这个比例位置的点 b10,b11。连接 b10,b11,再取 0.3 的点 b20。最终得到的 b20 就是当 t=0.3 时点的位置。

令 t 从 0 变化到 1,就能够得到一个贝塞尔曲线。

控制点为四个时,取点方式原理一样

也是设置一个变量 t,第一次选择 t 位置的点就能将四个控制点的情况转换为三个控制点的情况,如此,我们就能计算任意控制点的贝塞尔曲线了,因为了解了过程原理,最终的计算也就能够理解了,公式如下所示

可以看到,bi 前面的系数为 (1-t+t)^2 的展开式,总结规律得

\[b^n(t)=b^n_0(t)=\sum^n_{i=0} C^i_n \times t^i \times (1-t)^{n-i} \times b_i
\]

有了这个公式后就可以开始做实践了,在做贝塞尔曲面时需要两个自变量,u,v 替换 t,实质是将 u 取代了 t,然后再让 v 成为新的 t,再次遍历。这里以 16 个点为例,每四个点先做一次贝塞尔曲线转换

这里可以看到,每四个点都做了一次贝塞尔曲线的转换,再这个基础上,把得到的四个贝塞尔曲线上同 u 的点再次进行贝塞尔曲线的转换。

  1. u 从 0 到 1 ,得到了四条曲线
  2. 当 u 为某个值时,得到四条曲线上四个顶点作为控制点
  3. 以控制点为基础,v 从 0 到 1,得到一条曲线
  4. 遍历下去,由一条条曲线也就得到了类似曲面的图形

代码实践

在代码中,最重要得是算法的重现,首先看三个控制点如何实现

// 求得 t 时刻点的位置
function getFinalPosition(t, controls, length){
const n = length-1;
const sum = {x:0, y:0};
for (let i = 0; i <= n; i++) {
const Bjn = getCombination(n, i)*Math.pow(t, i)*Math.pow(1-t, n-i);
sum.x += controls[i].x * Bjn;
sum.y += controls[i].y * Bjn;
}
return sum;
} // 求阶乘
function getFactorial(n){
let result = 1;
for (let i = 2; i <= n; i++) {
result *= i;
}
return result;
} // 求组合 C
function getCombination(n, m){
if(m === 0 || m === n){
return 1;
}
return getFactorial(n)/(getFactorial(m)*getFactorial(n-m))
} function fillPoints(){
const resultPoints = [];
for (let i = 0; i < NUMBER; i++) {
const v1 = getFinalPosition(i/NUMBER, data, 4);
resultPoints.push(v1);
}
path.attr('d', line(resultPoints))
}
fillPoints()

注意点

  • getFinalPosition 方法就是计算公式的转换
  • 求组合 C 的公式是 n!/(m!*(n-m)!),这里有加速的方法,比如C0n=Cnn
  • fillPoints 方法是将所有点收集起来,最后连成线

这块是使用了 d3js 进行展示,球和线均手动创建,由于是自定义的图形,因此使用 d3js 自由绘画,十分方便,画点和线的代码如下

// 点
const points = canvas.selectAll('.point')
.data(data)
.enter()
.append('circle')
.attr('cx', d=>d.x)
.attr('cy', d=>d.y)
.attr('r', d=>10)
.attr('fill', '#f9d2bb')
.call(
d3.drag()
.on("drag", (event, d)=>{
d.x = event.x;
d.y = event.y;
})
.on("end", ()=>{
console.log(3)
})
.on('drag.update', ()=>{
points.attr('cx', d=>d.x)
.attr('cy', d=>d.y);
fillPoints();
})
) const line = d3.line()
.x(d => {
return d.x})
.y(d => d.y); // 线
const path = canvas
.append('path')
.attr("stroke", "red")
.attr("stroke-width", 3)
.attr("fill", "none")
.attr("stroke-opacity", 0.4);

给四个点添加拖动事件,每次拖动时重现计算线条

在三维情况下复杂一点,但是核心不变

const NUMBER = 1000;
for (let u = 0; u < NUMBER; u++) {
const resultPoints = [];
const resultColor = [];
for (let v = 0; v < NUMBER; v++) {
const acrossPoints = [];
for (let i = 0; i < 4; i++) {
const v1 = getFinalPosition(v/NUMBER, points[i], 4);
acrossPoints.push(v1.clone());
}
const v2 = getFinalPosition(u/NUMBER, acrossPoints, 4);
resultPoints.push(v2.clone());
const color = new THREE.Color(
u/NUMBER, 1-u/NUMBER, u/NUMBER)
resultColor.push(color.r, color.g, color.b);
}
const lineGeometry = new THREE.BufferGeometry().setFromPoints(resultPoints);
lineGeometry.setAttribute('color', new THREE.Float32BufferAttribute(resultColor, 3))
const lineMaterial = new THREE.LineBasicMaterial({
vertexColors: true
})
const line = new THREE.Line(lineGeometry, lineMaterial);
scene.add(line)
}

注意点

  1. getFinalPosition 方法和二维类似,重现方程
  2. 将 u 和 v 进行循环,u 为外层,v 为里层,v 遍历后相当于产生了四条控制线;u 遍历后产生最终的点位
  3. 里面用到了一些 threejs 中的方法,如果感兴趣可以相互交流

总结

实现贝塞尔曲线最重要的是理解公式,实现公式的语言和可视化方法有很多种,我采用了 three 展示三维和 d3js 展示二维。代码太凌乱,有兴趣可以交流。

贝塞尔曲线目前采用了以线取代控制点的方式,但是由于时间关系,没有使用 d3js 实现这个功能,如果点赞够多,给个鼓励,我就有精力实现一下,

贝塞尔曲线(面)二三维可视化(Three+d3)的更多相关文章

  1. canvas绘制二次贝塞尔曲线----演示二次贝塞尔四个参数的作用

    canvas中绘制二次贝塞尔曲线的方法为ctx.quadraticCurveTo(x1,y1,x2,y2); 四个参数分别为两个控制点的坐标.开始点即当前canvas中目前的点,如果想从指定的点开始, ...

  2. 深度掌握SVG路径path的贝塞尔曲线指令

    一.数字.公式.函数.变量,哦,NO! 又又一次说起贝塞尔曲线(英语:Bézier curve,维基百科详尽中文释义戳这里),我最近在尝试实现复杂的矢量图形动画,发现对贝塞尔曲线的理解馒头那么厚,是完 ...

  3. OpenGL超级宝典笔记——贝塞尔曲线和曲面(转)

    http://my.oschina.net/sweetdark/blog/183721 参数方程表现形式 在中学的时候,我们都学习过直线的参数方程:y = kx + b;其中k表示斜率,b表示截距(即 ...

  4. 【Unity】贝塞尔曲线关于点、长度、切线计算在 Unity中的C#实现

    原文:[Unity]贝塞尔曲线关于点.长度.切线计算在 Unity中的C#实现 写在前面 最近给项目做了个路径编辑,基本思路是满足几个基本需求: [额外说明]其实本篇和这个没关系,可以跳过" ...

  5. canvas基础[二]教你编写贝塞尔曲线工具

    贝塞尔曲线 bezierCurveTo 在线工具 https://canvature.appspot.com/ [感觉这个好用一些] https://blogs.sitepointstatic.com ...

  6. 二次、三次贝塞尔曲线demo(演示+获取坐标点)

    二次贝塞尔曲线demo: See the Pen quadraticCurveDemo by hanyanjun (@hanyanjun) on CodePen. 我的demo地址(二次) 推荐点击以 ...

  7. [js高手之路] html5 canvas系列教程 - arcTo(弧度与二次,三次贝塞尔曲线以及在线工具)

    之前,我写了一个arc函数的用法:[js高手之路] html5 canvas系列教程 - arc绘制曲线图形(曲线,弧线,圆形). arcTo: cxt.arcTo( cx, cy, x2, y2, ...

  8. Android 利用二次贝塞尔曲线模仿购物车加入物品抛物线动画

    Android 利用二次贝塞尔曲线模仿购物车加入物品抛物线动画 0.首先.先给出一张效果gif图. 1.贝塞尔曲线原理及相关公式參考:http://www.jianshu.com/p/c0d7ad79 ...

  9. Android仿苹果版QQ下拉刷新实现(二) ——贝塞尔曲线开发"鼻涕"下拉粘连效果

    前言 接着上一期Android仿苹果版QQ下拉刷新实现(一) ——打造简单平滑的通用下拉刷新控件 的博客开始,同样,在开始前我们先来看一下目标效果: 下面上一下本章需要实现的效果图: 大家看到这个效果 ...

随机推荐

  1. pf4j及pf4j-spring

    什么是PF4J 一个插件框架,用于实现插件的动态加载,支持的插件格式(zip.jar). 核心组件 Plugin:是所有插件类型的基类.每个插件都被加载到一个单独的类加载器中以避免冲突. Plugin ...

  2. [BUUCTF]REVERSE——[FlareOn6]Overlong

    [FlareOn6]Overlong 附件 步骤: 例行检查,32位程序,不懂是个啥 32位ida载入,main函数很简单 处理函数 sub_401000 程序只对unk_402008的28位进行了处 ...

  3. LuoguP6861 [RC-03] 难题 题解

    Update \(\texttt{2020.10.21}\) 删除了不需要的 \(n=1\) 的特判,并在符号与字母之间添加了空格. Content 给定一个数 \(n\),试找到一对数 \(a,b( ...

  4. 遍历显示自定义的widget

    需求 列表展示: 列表项都是同一格式,列表项数据从List里取 解决方案 使用map map源码 Iterable<T> map<T>(T f(E e)) => Mapp ...

  5. Hibernate 限制查询数目,使用limit功能

    在hql语句中,不能使用limit来限制显示的条数. 如果要限制查询的数目,要使用setMaxResults(e)方法来解决. query.setFirstResult(e);  //e是int值,要 ...

  6. office2007(word2007)另存为pdf文档

    默认office2007(word2007)是没有另存为pdf文档的功能的,需要安装插件 插件地址:https://yvioo.lanzous.com/iO5myedoceh 下载之后直接安装,安装成 ...

  7. 痞子衡嵌入式:把玩i.MXRT1062 TencentOS Tiny EVB_AIoT开发板(1) - 开发环境搭建与点灯

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT1062 TencentOS Tiny EVB_AIoT开发板环境搭建与点灯. 腾讯 TencentOS 团队于2021年1 ...

  8. 【LeetCode】536. Construct Binary Tree from String 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 统计字符串出现的次数 日期 题目地址:https:// ...

  9. 【LeetCode】338. Counting Bits 解题报告(Python & Java & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目描述 Given a non negati ...

  10. Local Relation Networks for Image Recognition

    目录 概 主要内容 Hu H., Zhang Z., Xie Z., Lin S. Local relation networks for image recognition. In Internat ...