中国石凳联盟 登录注册

【原创】采用贝塞尔函数圆滑曲线

rockage··1 回复·477 浏览

发表回复



4点了,睡不着,今天终于把这个遗忘已久的算法搞定了,哈哈,真是山穷水尽疑无路,柳暗花明又一村啊!!










贝塞尔曲线相信大家一定不会陌生,从最古老的Windows 3.1开始至今,它一直是系统的默认屏保。其实这个屏保就是由许许多多的点经过贝塞

尔函数的运算连接而成的,贝塞尔函数由德国数学家F.W.Bezier在1824年首次发表,广泛应用于建筑、工业设计和计算机图形学等等等等方面

。好了闲话少说,今天说一个比较简单的应用:二维坐标曲线的圆滑处理。









如果要纯粹使用算法手动搭建一个贝塞尔曲线,是相当困难的,这里面设计许多数学问题,贝塞尔函数本身并不复杂,可将它实际应用起来却

需要衍生一大堆的概念!所幸的是微软自Windows 98开始提供了一个API函数:PolyBezier,经由这个函数,我们可以相对简单地绘制贝塞尔曲

线。









1. 基本概念
一条最简单贝塞尔曲线由4个点构成,分别是:起始点、控制点1、控制点2、结束点。其中控制点并不显示,它只起到控制曲线走向的作用,但

如何选取控制点,却是整个贝塞尔曲线绘制的关键所在。控制点的选取可以通过许多算法来实现,常见的有德卡斯特里奥算法等,今天介绍这

个方法就是以这个算法为基础的简化版本(三次贝塞尔插值法),这个算法因为计算量不大,所以效率很高哦!









2. 实战演习





http://stoneren.com/rockage/bezier/s0.jpg




2.1 首先在屏幕上置三个点P0 P1 P2








http://stoneren.com/rockage/bezier/s1.jpg



2.2 将P0,P1,P2相连,其中P2-P0为辅助线(红色)








http://stoneren.com/rockage/bezier/s2.jpg



2.3 求出各线段的中点B0 B1 B2








http://stoneren.com/rockage/bezier/s3.jpg



2.4 再将B0 B1 B2依次相连








http://stoneren.com/rockage/bezier/s4.jpg



2.5 按照比例确定K点K0,K1,K2
比例关系是:(P2-P0):(P0-P1) = (B2-K0):(K0-B0)








http://stoneren.com/rockage/bezier/s5.jpg



2.6 坐标平移,K0平移至P0,K1平移至P1,K2平移至P2,得到6个控制点C0至C5








http://stoneren.com/rockage/bezier/s6.jpg



2.7 利用PolyBezier函数绘制贝塞尔曲线,以P0-P1为例,其中:
起始点:P0
控制点1:C1
控制点2:C2
结束点:P1




用同样的方法连接P1-P2










http://stoneren.com/rockage/bezier/s7.jpg




2.8 让我们换一个坐标,让“曲线”的感觉来得更强烈些吧!









3. 实战效果








http://stoneren.com/rockage/bezier/before.jpg



3.1 未使用圆滑处理前的曲线








http://stoneren.com/rockage/bezier/after.jpg



3.2 采用本方案圆滑后的曲线

点赞0 收藏
Option Explicit

定义相关API函数,其中包括最重要的PolyBezier
Private Declare Function GetStockObject Lib "gdi32" (ByVal nIndex As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function PolyBezier Lib "gdi32" (ByVal hdc As Long, lppt As POINTAPI, ByVal cPoints As Long) As Long
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Const BLACK_PEN = 7

Private Sub Command1_Click()

Dim P(0 To 2) As POINTAPI
Dim PTS(0 To 3) As POINTAPI
Dim B(0 To 2) As POINTAPI
Dim Len1(0 To 2) As Integer
Dim K(0 To 2) As Single
Dim Ki As Single
Dim KM(0 To 2) As POINTAPI
Dim C(0 To 5) As POINTAPI
Dim hPen As Long, hOldPen As Long, RetVal As Long

以像素为绘图单位,API绘图函数只支持像素单位
Me.ScaleMode = vbPixels

起始点定义:
P(0).X = 100
P(0).Y = 200
P(1).X = 200
P(1).Y = 100
P(2).X = 300
P(2).Y = 200

求各中点
B(0).X = (P(0).X + P(1).X) / 2
B(0).Y = (P(0).Y + P(1).Y) / 2
B(1).X = (P(1).X + P(2).X) / 2
B(1).Y = (P(1).Y + P(2).Y) / 2
B(2).X = (P(2).X + P(0).X) / 2
B(2).Y = (P(2).Y + P(0).Y) / 2

用欧式距离公式,计算起始点所连成的三条边(长边)的长度
Len1(0) = Sqr((P(1).X - P(0).X) * (P(1).X - P(0).X) + (P(1).Y - P(0).Y) * (P(1).Y - P(0).Y))
Len1(1) = Sqr((P(2).X - P(1).X) * (P(2).X - P(1).X) + (P(2).Y - P(1).Y) * (P(2).Y - P(1).Y))
Len1(2) = Sqr((P(0).X - P(2).X) * (P(0).X - P(2).X) + (P(0).Y - P(2).Y) * (P(0).Y - P(2).Y))

算出比例关系
K(0) = Len1(2) / (Len1(0) + Len1(2))
K(1) = Len1(0) / (Len1(0) + Len1(1))
K(2) = Len1(1) / (Len1(1) + Len1(2))

按上面的比例关系切分内部的三条短边
KM(0).X = B(2).X + (B(0).X - B(2).X) * K(0)
KM(0).Y = B(2).Y + (B(0).Y - B(2).Y) * K(0)
KM(1).X = B(0).X + (B(1).X - B(0).X) * K(1)
KM(1).Y = B(0).Y + (B(1).Y - B(0).Y) * K(1)
KM(2).X = B(1).X + (B(2).X - B(1).X) * K(2)
KM(2).Y = B(1).Y + (B(2).Y - B(1).Y) * K(2)

圆滑系数Ki,取值范围0
点赞0 收藏
发表回复
发表回复