簡體   English   中英

使用Cairo繪制夾緊的均勻立方B樣條

[英]Drawing a clamped uniform cubic B-spline using Cairo

我有一堆坐標,它們是2D平面上夾緊的均勻立方B樣條的控制點。 我想使用Cairo調用繪制此曲線(在Python中,使用Cairo的Python綁定),但據我所知,Cairo僅支持Bézier曲線。 我也知道可以使用Bézier曲線繪制兩個控制點之間的B樣條曲線,但我無法在任何地方找到精確的公式。 給定控制點的坐標,如何導出相應Bézier曲線的控制點? 那有什么有效的算法嗎?

好的,所以我用Google搜索了很多,我想我想出了一個適合我目的的合理解決方案。 我在這里張貼它 - 也許它對其他人也有用。

首先,讓我們從一個簡單的Point類開始:

from collections import namedtuple

class Point(namedtuple("Point", "x y")):
    __slots__ = ()

    def interpolate(self, other, ratio = 0.5):
        return Point(x = self.x * (1.0-ratio) + other.x * float(ratio), \
                     y = self.y * (1.0-ratio) + other.y * float(ratio))

立方B樣條只不過是Point對象的集合:

class CubicBSpline(object):
    __slots__ = ("points", )

    def __init__(self, points):
        self.points = [Point(*coords) for coords in points]

現在,假設我們有一個開放的均勻立方B樣條而不是夾緊的。 三次B樣條的四個連續控制點定義單個Bézier段,因此控制點0到3定義第一個Bézier段,控制點1到4定義第二個段,依此類推。 Bézier樣條的控制點可以通過以適當的方式在B樣條的控制點之間線性插值來確定。 設A,B,C和D為B樣條的四個控制點。 計算以下輔助點:

  1. 找到以2:1的比例划分AB線的點,讓它為A'。
  2. 找到以1:2的比例划分CD線的點,讓它為D'。
  3. 將BC線分成三個相等的部分,讓兩個點分別為F和G.
  4. 找到A'和F之間的點,這將是E.
  5. 找到G和D'之間的點,這將是H.

從E到H的Bézier曲線與控制點F和G相當於A,B,C和D點之間的開放B樣條。參見本優秀文件的 1-5節。 順便說一句,上述方法被稱為Böhm算法,如果用適當的數學方法表示,它也會更加復雜,這也解釋了非均勻或非立方B樣條。

我們必須對B樣條的每組4個連續點重復上述過程,因此最后我們將需要幾乎任何連續控制點對之間的1:2和2:1分割點。 這是繪制曲線之前的以下BSplineDrawer類:

class BSplineDrawer(object):
    def __init__(self, context):
        self.ctx = context

    def draw(self, bspline):
        pairs = zip(bspline.points[:-1], bspline.points[1:])
        one_thirds = [p1.interpolate(p2, 1/3.) for p1, p2 in pairs]
        two_thirds = [p2.interpolate(p1, 1/3.) for p1, p2 in pairs]

        coords = [None] * 6
        for i in xrange(len(bspline.points) - 3):
            start = two_thirds[i].interpolate(one_thirds[i+1])
            coords[0:2] = one_thirds[i+1]
            coords[2:4] = two_thirds[i+1]
            coords[4:6] = two_thirds[i+1].interpolate(one_thirds[i+2])

            self.context.move_to(*start)
            self.context.curve_to(*coords)
            self.context.stroke()

最后,如果我們想要繪制夾緊的B樣條而不是開放的B樣條,我們只需要重復三次夾緊B樣條的兩個端點:

class CubicBSpline(object):
    [...]
    def clamped(self):
        new_points = [self.points[0]] * 3 + self.points + [self.points[-1]] * 3
        return CubicBSpline(new_points)

最后,這是代碼應該如何使用:

import cairo

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 600, 400)
ctx = cairo.Context(surface)

points = [(100,100), (200,100), (200,200), (100,200), (100,400), (300,400)]
spline = CubicBSpline(points).clamped()

ctx.set_source_rgb(0., 0., 1.)
ctx.set_line_width(5)
BSplineDrawer(ctx).draw(spline)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM