德卡斯特里奥算法

来自testwiki
imported>InternetArchiveBot2022年10月20日 (四) 09:22的版本 (补救1个来源,并将0个来源标记为失效。) #IABot (v2.0.9.2)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转到导航 跳转到搜索

Template:NoteTA

数学子领域数值分析中的德卡斯特里奥算法Template:Lang-en),以发明者保尔·德·卡斯特里奥命名,是计算伯恩斯坦形式的多项式或貝茲曲線递归方法。

虽然对于大部分的体系结构,该算法和直接方法相比较慢,但它在数值上更为稳定

定义

贝兹曲线B(角度为n,控制点β0,,βn)可用以下方式运用德卡斯特里奥算法

B(t)=i=0nβibi,n(t),

其中bTemplate:Link-en

bi,n(t)=(ni)(1t)niti.

曲线在t0点上可以用遞迴關係式运算

βi(0):=βi , i=0,,n
βi(j):=βi(j1)(1t0)+βi+1(j1)t0 , i=0,,nj , j=1,,n

然后,Bt0点上的计算可以此算法的n步计算。B(t0)的结果为:

B(t0)=β0(n).

再者,贝兹曲线B可在t0分成带有各种控制点的两段曲线:

β0(0),β0(1),,β0(n)
β0(n),β1(n1),,βn(0)

注意事项

进行手算时把系数写成三角形形式很有用。

β0=β0(0)β0(1)β1=β1(0)β0(n)βn1=βn1(0)βn1(1)βn=βn(0)

当选择一点t0来计算波恩斯坦多项式时,我们可以用三角形形式的两个对角线来构造多项式的分段表示。

B(t)=i=0nβi(0)bi,n(t) , t[0,1]

把它变成

B1(t)=i=0nβ0(i)bi,n(tt0) , t[0,t0]

以及

B2(t)=i=0nβi(ni)bi,n(tt01t0) , t[t0,1]

例子

我们要计算2次波恩斯坦多项式,其伯恩斯坦系数为

β0(0)=β0
β1(0)=β1
β2(0)=β2

t0点计算。

我们有下式开始递归

β0(1)=β0(0)(1t0)+β1(0)t=β0(1t0)+β1t0
β1(1)=β1(0)(1t0)+β2(0)t=β1(1t0)+β2t0

递归的第二次重复结束于

β0(2)=β0(1)(1t0)+β1(1)t0 =β0(1t0)(1t0)+β1t0(1t0)+β1(1t0)t0+β2t0t0 =β0(1t0)2+2β1t0(1t0)+β2t02

这就是我们所预料的n阶伯恩斯坦多项式。

贝塞尔曲線

在计算带n+1个控制点Pi的三维空间中的n贝塞尔曲線 (Bézier curve) 时

𝐁(t)=i=0n𝐏ibi,n(t) , t[0,1]

其中

𝐏i:=(xiyizi).

我们把Bézier曲线分成三个分立的方程

B1(t)=i=0nxibi,n(t) , t[0,1]
B2(t)=i=0nyibi,n(t) , t[0,1]
B3(t)=i=0nzibi,n(t) , t[0,1]

然后我们用de Casteljau算法分别计算。

伪代码例子

这是一个递归的画出一条从点P1P4,弯向P2P3的曲线的伪代码例子。级数参数是递归的次数。该过程用增加了的级数参数来递归的调用它自己。当级别达到最大级别这个全局变量时,在P1P4之间就画上直线。函数中点(midpoint)去两个点,并返回这两点间的线段的中点。

    global max_level = 5
    procedure draw_curve(P1, P2, P3, P4, level)
        if (level > max_level)
            draw_line(P1, P4)
        else
            L1 = P1
            L2 = midpoint(P1, P2)
            H  = midpoint(P2, P3)
            R3 = midpoint(P3, P4)
            R4 = P4
            L3 = midpoint(L2, H)
            R2 = midpoint(R3, H)
            L4 = midpoint(L3, R2)
            R1 = L4
            draw_curve(L1, L2, L3, L4, level + 1)
            draw_curve(R1, R2, R3, R4, level + 1)
    end procedure draw_curve

代码实现

用线性插值计算P和Q之间的一点R,插值参数为t
用法:linearInterp P Q t
          P = 代表一个点的表
          Q = 代表一个点的表
          t = 线性插值的参数值, t<-[0..1]
返回:代表点(1-tP + tQ的表

>	linearInterp :: [Float]->[Float]->Float->[Float]
>	linearInterp [] [] _ = []
>	linearInterp (p:ps) (q:qs) t = (1-t)*p + t*q : linearInterp ps qs t

计算一对控制点间的线性插值的中间结果
用法:eval t b
          t = 线性插值的参数值, t<-[0..1]
          b = 控制点的表
返回:对n个控制点,返回n-1个插值点的表

>	eval :: Float->[[Float]]->[[Float]]
>	eval tbi:bj:[]= [linearInterp bi bj t]
>	eval t (bi:bj:bs) = (linearInterp bi bj t) : eval t (bj:bs)

de Casteljau算法计算Bezier曲线上一点
用法:deCas t b
          t = 线性插值的参数值, t<-[0..1]
          b = 控制点的表
返回:代表Bezier曲线上一个点的列表

>	deCas :: Float->[[Float]]->[Float]
>	deCas tbi:[]= bi
>	deCas t bs = deCas t (eval t bs)

de Casteljau算法计算沿着Bezier曲线的一系列点。点用一个列表返回。
用法:bezierCurve n b
         n = 要计算的点的个数
         b = Bezier控制点列表
返回:Bezier曲线上n1个点
例子:bezierCurve 50 <nowiki>[[0,0][1,1][0,1][1,0]]</nowiki>

>	bezierCurve :: Int->[[Float]]->[[Float]]
>	bezierCurve n b = [deCas (fromIntegral x / fromIntegral n) b | x<-[0 .. n] ]

(该代码用到Python图像库Template:Wayback

import Image
import ImageDraw

SIZE=128
image = Image.new("RGB", (SIZE, SIZE))
d = ImageDraw.Draw(image)

def midpoint((x1, y1), (x2, y2)):
    return ((x1+x2)/2, (y1+y2)/2)

MAX_LEVEL = 5
def draw_curve(P1, P2, P3, P4, level=1):
    if level == MAX_LEVEL:
        d.line((P1, P4))
    else:
        L1 = P1
        L2 = midpoint(P1, P2)
        H  = midpoint(P2, P3)
        R3 = midpoint(P3, P4)
        R4 = P4
        L3 = midpoint(L2, H)
        R2 = midpoint(R3, H)
        L4 = midpoint(L3, R2)
        R1 = L4
        draw_curve(L1, L2, L3, L4, level+1)
        draw_curve(R1, R2, R3, R4, level+1)

draw_curve((10,10),(100,100),(100,10),(100,100))

image.save(r"c:\DeCasteljau.png", "PNG")

print "ok."

参考

  • Farin, Gerald & Hansford, Dianne (2000). The Essentials of CAGD. Natic, MA: A K Peters, Ltd. ISBN 1-56881-123-3

参看