Bezier Curve ExampleI was planning to use bezier curves to create some interactive visuals using pygame. I ran into this instructive example code by Victor Blomquist. While it is instructive, it is not actually working for me. There are a bunch of problems like capitals where they should not be, and using pointers to objects instead of copies of objects etc. Maybe some of that is just due to a time of 5 years that has passed since that code was written, but who knows where, why, and so forth.

Below is a code that is running for me.

"""
bezier.py - Calculates a bezier curve from control points. 

Depends on the 2d vector class found here: http://www.pygame.org/wiki/2DVectorClass

2007 Victor Blomqvist
Released to the Public Domain

Updates by Lennart Hilbert
2012
"""
import pygame
from pygame.locals import *

import copy 

from vec2d import *

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 calculate_bezier(in_points, steps = 30):
    """
    Calculate a bezier curve from 4 control points and return a 
    list of the resulting points.

    The function uses the forward differencing algorithm described here: 
    http://www.niksula.cs.hut.fi/~hkankaan/Homepages/bezierfast.html
    """

    t = 1.0 / steps
    temp = t*t

    in_points = [in_points[1],in_points[0],in_points[3],in_points[2]]

    f = in_points[0]
    fd = 3 * (in_points[1] - in_points[0]) * t
    fdd_per_2 = 3 * (in_points[0] - 2 * in_points[1] + in_points[2]) * temp
    fddd_per_2 = 3 * (3 * (in_points[1] - in_points[2]) + in_points[3] - in_points[0]) * temp * t

    fddd = 2 * fddd_per_2
    fdd = 2 * fdd_per_2
    fddd_per_6 = fddd_per_2 / 3.0

    points = []
    for x in range(steps):
        points.append(copy.copy(f))
        f += fd + fdd_per_2 + fddd_per_6
        fd += fdd + fddd_per_2
        fdd += fddd
        fdd_per_2 += fddd_per_2
    points.append(f)
    return points

def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 600))

    ### 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_ind = 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 nn,p in enumerate(control_points):
                    if abs(p.x - event.pos[X]) < 10 and abs(p.y - event.pos[Y]) < 10 :
                        selected_ind = nn
            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_ind = None

        ### Draw stuff
        screen.fill(gray)

        if selected_ind is not None:
            selected_point = control_points[selected_ind]
            selected_point.x, selected_point.y = pygame.mouse.get_pos()
            pygame.draw.circle(screen, green, selected_point, 10)

        ### Draw control points
        for p in control_points:
            pygame.draw.circle(screen, blue, p, 4)
        ### Draw control "lines"
        pygame.draw.lines(screen, lightgray, False, control_points)

        ### Draw bezier curve
        for xx in range(0,len(control_points)-3):
            # Create a copy of the control points before doing calculations with
            # them. Otherwise, they will be changed during vector operations. If
            # copy is executed not element by element, only the pointers to the
            # vectors are copied, but not the vectors themselves
            input_copy = [copy.copy(pp) for pp in control_points[xx:xx+4]]
            b_points = calculate_bezier(input_copy)
            pygame.draw.lines(screen, red, False, b_points)

        ### Flip screen
        pygame.display.flip()
        clock.tick(100)
        #print clock.get_fps()

if __name__ == '__main__':
    main()

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s