gpt4 book ai didi

python - 使用 Cairo 绘制一个固定的均匀三次 B 样条曲线

转载 作者:太空宇宙 更新时间:2023-11-03 12:42:52 25 4
gpt4 key购买 nike

我有一堆坐标,它们是 2D 平面上夹紧均匀三次 B 样条的控制点。我想使用 Cairo 调用(在 Python 中,使用 Cairo 的 Python 绑定(bind))绘制这条曲线,但据我所知,Cairo 仅支持贝塞尔曲线。我也知道可以使用贝塞尔曲线绘制两个控制点之间的 B 样条线段,但我无法在任何地方找到确切的公式。给定控制点的坐标,如何推导出相应贝塞尔曲线的控制点?有什么有效的算法吗?

最佳答案

好的,所以我使用 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 样条曲线的四个连续控制点定义单个贝塞尔曲线段,因此控制点 0 到 3 定义第一个贝塞尔曲线段,控制点 1 到 4 定义第二个曲线段,依此类推。贝塞尔样条的控制点可以通过以适当的方式在B样条的控制点之间进行线性插值来确定。设A、B、C、D为B样条的四个控制点。计算以下辅助点:

  1. 求以2:1的比例划分A-B线的点,设A'。
  2. 求以1:2的比例划分C-D线的点,设D'。
  3. 将B-C线分成三等份,设两点为F和G。
  4. 找到 A' 和 F 中间的点,这就是 E。
  5. 找到 G 和 D' 中间的点,这就是 H。

从 E 到 H 和控制点 F 和 G 的贝塞尔曲线等效于点 A、B、C 和 D 之间的开放 B 样条曲线。参见 this excellent document 的第 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)

关于python - 使用 Cairo 绘制一个固定的均匀三次 B 样条曲线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2534786/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com