python 样条曲线生成面_关于python:使用开罗绘制一个钳制的均匀三次B样条曲线...

我有一堆坐标,它们是2D平面上固定的均匀三次B样条曲线的控制点。 我想使用Cairo调用绘制此曲线(在Python中,使用Cairo的Python绑定),但是据我所知,Cairo仅支持Bézier曲线。 我也知道可以使用贝塞尔曲线来绘制两个控制点之间的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样条曲线。三次B样条曲线的四个连续控制点定义单个Bézier线段,因此控制点0到3定义第一个Bézier线段,控制点1-4定义第二个Bézier线段,依此类推。 Bézier样条曲线的控制点可以通过以适当的方式在B样条曲线的控制点之间线性内插来令A,B,C和D为B样条曲线的四个控制点。计算以下辅助点:

找到将A-B线以2:1的比例分开的点,将其设为A'。

找到将C-D线以1:2的比例分开的点,将其设为D'。

将B-C线分成三个相等的部分,让两点为F和G。

找到A'和F之间的中间点,即E。

找到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)

您是否可以在Ive尝试的Python 3.x中使此代码工作,但是它有一些奇怪的异常现象。 xrange到范围。 此外,右对右括号括起来的one_thirds = [p1,p2成对的p1.interpolate(p2,1/3。)。

我目前没有Python 3.x,但是我认为这些是您需要进行的更改:1)用list(zip(...))替换zip(...),因为我们要遍历列表两次,2)替换与range(),3)使用方括号,您当前会在其中看到一个右圆括号,因为无论如何这都是错字(我现在将编辑我的答案以进行修正)

谢谢,我确实弄清楚了,并使它正常工作。 有用的代码。

将B样条曲线转换为Bezier样条曲线有帮助吗?

@ΤζΩΤ?ΙΟΥ+1谢谢,这有助于我朝正确的方向入手。 有关完整的解决方案和我发现的算法的简化说明,请参见上面的答案。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值