<p>我在<a href="https://stackoverflow.com/users/190597/unutbu">unutbu</a>的<a href="https://stackoverflow.com/questions/246525/how-can-i-draw-a-bezier-curve-using-pythons-pil">different post</a>中找到了我的大部分答案(第二个答案)。在</p>
<p>这是我对他的基本功能的修改,还有一些额外的功能来解决我上面的问题。我将不得不为直线段编写一个类似的函数,但这显然要容易得多,它们之间可以拼凑出曲线和直线段的任何组合,以实现上述目标。在</p>
<pre><code>def pascal_row(n):
# This is over my designer's brain, but unutbu says:
# "This returns the nth row of Pascal's Triangle"
result = [1]
x, numerator = 1, n
for denominator in range(1, n//2+1):
# print(numerator,denominator,x)
x *= numerator
x /= denominator
result.append(x)
numerator -= 1
if n&1 == 0:
# n is even
result.extend(reversed(result[:-1]))
else:
result.extend(reversed(result))
return result
def bezier_interpolation(origin, destination, control_o, control_d=None):
points = [origin, control_o, control_d, destination] if control_d else [origin, control_o, destination]
n = len(points)
combinations = pascal_row(n - 1)
def bezier(transitions):
# I don't really understand any of this math, but it works!
result = []
for t in transitions:
t_powers = (t ** i for i in range(n))
u_powers = reversed([(1 - t) ** i for i in range(n)])
coefficients = [c * a * b for c, a, b in zip(combinations, t_powers, u_powers)]
result.append(
list(sum([coef * p for coef, p in zip(coefficients, ps)]) for ps in zip(*points)))
return result
def line_segments(points, size):
# it's more convenient for my purposes to have the pairs of x,y
# coordinates that eventually become the very small line segments
# that constitute my "curves
for i in range(0, len(points), size):
yield points[i:i + size]
# unutbu's function creates waaay more points than needed, and
# these extend well through the "destination" point I want to end at, so,
# I keep inspecting the line segments until one of them passes through
# my intended stop point (ie. "destination") and then manually stop
# collecting, returning the subset I want; probably not efficient,
# but it works
break_next = False
segments = []
for pos in line_segments(bezier([0.01 * t for t in range(101)]), 2):
if break_next:
segments.append([break_next, destination])
break
try:
if [int(i) for i in pos[0]] == destination:
break_next = pos[0]
continue
segments.append(pos)
except IndexError:
# not guaranteed to get an even number of points from bezier()
break
return segments
</code></pre>