贝塞尔曲线有着很多特殊的性质, 在图形设计和路径规划中应用都非常广泛, 我就是想在路径规划中贝塞尔曲线完全由其控制点决定其形状, n个控制点对应着n-1阶的贝塞尔曲线,并且可以通过递归的方式来绘制.
画重点了啊: 递归 :2点确定一个点(随着t变化),3点确定2个点,4点确定3个点,5点确定4个点,无限延伸,就像二进制可以延伸出我们美丽计算机世界一样。
一阶曲线:



一阶曲线就是很好理解, 就是根据t来的线性插值. P0表示的是一个向量 [x ,y], 其中x和y是分别按照这个公式来计算的.
二阶贝塞尔:
既然重点是递归, 那么二阶贝塞尔必然和一阶有关系.





这时候DE又是一条直线了, 就可以按照一阶的贝塞尔方程来进行线性插值了, t= AD:AE
这时候就可以推出公式了.





三阶贝塞尔曲线:

二阶的贝塞尔通过在控制点之间再采点的方式实现降阶, 每一次选点都是一次的降阶.
四个点对应是三次的贝塞尔曲线. 分别在 AB BC CD 之间采EFG点, EFG三个点对应着二阶贝塞尔, 在EF FG之间采集HI点来降阶为一阶贝塞尔曲线.

高阶贝塞尔曲线:
高阶的贝塞尔可以通过不停的递归直到一阶
贝塞尔曲线 公式
可以通过递归的方式来理解贝塞尔曲线, 但是还是给出公式才方便计算的.


仔细看可以发现, 贝塞尔的参数B是二项式(t+(1-t))^n = (1)^n的展开公式. 划重点了: 系数是二项式的展开. 后面的很多的贝塞尔曲线的性质都可以用这个来解释
贝塞尔曲线的导数
变化一下贝塞尔公式:


控制点是独立的, 因此求导是直接对u就行求导, 就是仅仅对参数项B进行求导.


定义: Q0=n*(P1-P0), Q0=n*(P2-P1), Q0=n*(P3-P2),...Qn-1=n*(Pn-Pn-1), . 如果我们把Q当做一组新的控制点, 那么原贝塞尔的导数可以写成如下:

导数还是贝塞尔曲线, 只不过是控制点是原来控制点的组合而已.
以下是一个三阶贝塞尔曲线golang例子:
package main
import(
"fmt"
"image"
"image/color"
"image/png"
"log"
"os"
)
// Putpixel describes a function expected to draw a point on a bitmap at (x, y) coordinates.
type Putpixel func(x, y int)
func drawline(x0, y0, x3, y3 int, brush Putpixel) {
x1 := 500
y1 := 500
x2 := 0
y2 := 250
for i := 0; i < 1000; i++ {
t := float32(i) / 1000.0
x := int((1.0 - t) * (1.0 - t) * (1.0 - t) * float32(x0) + 3.0 * t * (1 - t) * (1 - t) * float32(x1) + 3.0 * t * t * (1 - t) * float32(x2) + t * t * t * float32(x3))
y := int((1.0 - t) * (1.0 - t) * (1.0 - t) * float32(y0) + 3.0 * t * (1 - t) * (1 - t) * float32(y1) + 3.0 * t * t * (1 - t) * float32(y2) + t * t * t * float32(y3))
brush(x,y)
}
}
func main() {
dx := 500
dy := 500
img := image.NewNRGBA(image.Rect(0, 0, dx, dy))
drawline(350, 250, 500, 250, func(x, y int) {
img.Set(x, y, color.Black)
})
// 左右都画一条竖线
for i := 0; i < dy; i++ {
img.Set(0, i, color.Black)
img.Set(dx - 1, i, color.Black)
}
imgcounter := 250
imgfile, _ := os.Create(fmt.Sprintf("%03d.png", imgcounter))
defer imgfile.Close()
// 以PNG格式保存文件
err := png.Encode(imgfile, img)
if err != nil{
log.Fatal(err)
}
}