This script contains a function, calculate_bezier to calculate a bezier curve and a short demo to display how it can be used. The calculate_bezier function is a direct port of the forward differencing bezier curve algorithm described here.
It depends on the 2d vector class here in the pygame cookbook: [[2DVectorClass]]
Controls for the demo:
Right mouse button - Add a control point
Left mouse button - Move a control point
The original code didn't work for me. I found an implementation of forward differentiating bezier algorithm at DrDobbs and pretty much copy-pasted it to python. I also removed adding new points with RMB since I couldn't figure out how to make this to work, but for the purpose of demonstrating the algorithm it isn't really that important. 2DVectorClass dependency was also removed.
"""
bezier.py - Calculates a bezier curve from control points.
2007 Victor Blomqvist
Released to the Public Domain
"""
import pygame
from pygame.locals import *
class vec2d(object):
def __init__(self, x, y):
self.x = x
self.y = y
gray = (100,100,100)
lightgray = (200,200,200)
red = (255,0,0)
green = (0,255,0)
blue = (0,0,255)
X,Y,Z = 0,1,2
def compute_bezier_points(vertices, numPoints=None):
if numPoints is None:
numPoints = 30
if numPoints < 2 or len(vertices) != 4:
return None
result = []
b0x = vertices[0][0]
b0y = vertices[0][1]
b1x = vertices[1][0]
b1y = vertices[1][1]
b2x = vertices[2][0]
b2y = vertices[2][1]
b3x = vertices[3][0]
b3y = vertices[3][1]
# Compute polynomial coefficients from Bezier points
ax = -b0x + 3 * b1x + -3 * b2x + b3x
ay = -b0y + 3 * b1y + -3 * b2y + b3y
bx = 3 * b0x + -6 * b1x + 3 * b2x
by = 3 * b0y + -6 * b1y + 3 * b2y
cx = -3 * b0x + 3 * b1x
cy = -3 * b0y + 3 * b1y
dx = b0x
dy = b0y
# Set up the number of steps and step size
numSteps = numPoints - 1 # arbitrary choice
h = 1.0 / numSteps # compute our step size
# Compute forward differences from Bezier points and "h"
pointX = dx
pointY = dy
firstFDX = ax * (h * h * h) + bx * (h * h) + cx * h
firstFDY = ay * (h * h * h) + by * (h * h) + cy * h
secondFDX = 6 * ax * (h * h * h) + 2 * bx * (h * h)
secondFDY = 6 * ay * (h * h * h) + 2 * by * (h * h)
thirdFDX = 6 * ax * (h * h * h)
thirdFDY = 6 * ay * (h * h * h)
# Compute points at each step
result.append((int(pointX), int(pointY)))
for i in range(numSteps):
pointX += firstFDX
pointY += firstFDY
firstFDX += secondFDX
firstFDY += secondFDY
secondFDX += thirdFDX
secondFDY += thirdFDY
result.append((int(pointX), int(pointY)))
return result
def main():
pygame.init()
screen = pygame.display.set_mode((1024, 768))
### Control points that are later used to calculate the curve
control_points = [vec2d(100,100), vec2d(150,500), vec2d(450,500), vec2d(500,150)]
### The currently selected point
selected = None
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type in (QUIT, KEYDOWN):
running = False
elif event.type == MOUSEBUTTONDOWN and event.button == 1:
for p in control_points:
if abs(p.x - event.pos[X]) < 10 and abs(p.y - event.pos[Y]) < 10 :
selected = p
# elif event.type == MOUSEBUTTONDOWN and event.button == 3:
# x,y = pygame.mouse.get_pos()
# control_points.append(vec2d(x,y))
elif event.type == MOUSEBUTTONUP and event.button == 1:
selected = None
### Draw stuff
screen.fill(gray)
if selected is not None:
selected.x, selected.y = pygame.mouse.get_pos()
pygame.draw.circle(screen, green, (selected.x, selected.y), 10)
### Draw control points
for p in control_points:
pygame.draw.circle(screen, blue, (int(p.x), int(p.y)), 4)
### Draw control "lines"
pygame.draw.lines(screen, lightgray, False, [(x.x, x.y) for x in control_points])
### Draw bezier curve
b_points = compute_bezier_points([(x.x, x.y) for x in control_points])
pygame.draw.lines(screen, pygame.Color("red"), False, b_points, 2)
### Flip screen
pygame.display.flip()
clock.tick(100)
#print clock.get_fps()
if __name__ == '__main__':
main()