3

2D平面上にクランプされた均一な3次Bスプラインの制御点である座標がたくさんあります。Cairo呼び出しを使用して(PythonではCairoのPythonバインディングを使用して)この曲線を描きたいのですが、私が知る限り、Cairoはベジェ曲線のみをサポートしています。また、2つの制御点間のBスプラインのセグメントは、ベジェ曲線を使用して描画できることも知っていますが、正確な式はどこにも見つかりません。制御点の座標が与えられた場合、対応するベジェ曲線の制御点をどのように導出できますか?そのための効率的なアルゴリズムはありますか?

4

2 に答える 2

7

さて、私はグーグルを使ってたくさん検索しました、そして私は自分の目的に適した合理的な解決策を思いついたと思います。私はそれをここに投稿しています-多分それは他の誰かにも役立つでしょう。

まず、簡単な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))

3次Bスプラインは、Pointオブジェクトのコレクションにすぎません。

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

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

ここで、クランプされたものではなく、開いた均一な3次Bスプラインがあると仮定します。3次Bスプラインの4つの連続する制御点は、単一のベジエセグメントを定義します。したがって、制御点0〜3は最初のベジエセグメントを定義し、制御点1〜4は2番目のセグメントを定義します。ベジエスプラインの制御点は、適切な方法でBスプラインの制御点間を線形補間することによって決定できます。A、B、C、およびDをBスプラインの4つの制御点とします。次の補助点を計算します。

  1. ABラインを2:1の比率で分割するポイントを見つけ、それをA'とします。
  2. CDラインを1:2の比率で分割するポイントを見つけ、それをD'とします。
  3. BCラインを3つの等しい部分に分割し、2つのポイントをFとGとします。
  4. A'とFの中間点を見つけます。これはEになります。
  5. GとD'の中間点を見つけます。これはHになります。

制御点FおよびGを使用したEからHへのベジェ曲線は、点A、B、C、およびD間の開いたBスプラインに相当します。この優れたドキュメントのセクション1〜5を参照してください。ちなみに、上記の方法はベームのアルゴリズムと呼ばれ、不均一または非立方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スプラインの両方の端点をさらに3回繰り返す必要があります。

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)
于 2010-03-29T16:53:24.023 に答える
2

Bスプライン曲線をベジェスプライン曲線に変換することは役に立ちますか?

于 2010-03-29T00:02:31.657 に答える