Multiple Degree Bezier Curve Drawing Program

This is the place to post any code that you want to share with the community. Only completed scripts should be posted here.
Note: posts here are not necessarily endorsed by the community, and may represent amateur or even bad practices.

Multiple Degree Bezier Curve Drawing Program

Bezier.py:
The program draws Bezier curves given up to 33 control points. There is also the option to draw every lower order curve. The window starts blank, just click anywhere to place a control point. After 3 points (and for every point after), a button will appear in the top left corner. Click the black button in the top right once to clear the list of points (allowing you to draw a separate set of points without erasing previous points) and a second time to clear the screen.

For your reviewing pleasure
Code: Select all
`from Tkinter import *from math import *colorsList = {0:'orange', 1:'yellow', 2:'green', 3:'blue', 4:'purple', 5:'red'}numColors = len(colorsList)# dimensions of windowwidth = 1300height = 700points = {0:[], 'draw':[]}# Dictionary of lists containing all points# Each value is a 2D list, so calling points follows the format#    points[degree][point number][x or y]#       degree - cubic Beziers have 2 degrees, etc#       point number is the point, points being ordered chronologically#       0 for x, 1 for y# The 'draw' entry will contain a 2D list of two sublists, ocntaining#    the two points that will actually be used to drawradius = 3 # radius of circles around pointsbuttonSize = minHeight = 40 # size of buttonsclass Point:    def __init__(self, x, y):        self.x = x        self.y = y        def drawButtons(numPoints):    for button in xrange(numPoints - 2):        if button >= numColors:            buttonValue = button - numColors * (button / numColors)            # appears redundant but integer division truncates        else:            buttonValue = button        canvas.create_rectangle(                button * buttonSize, 0,                (button + 1) * buttonSize, buttonSize,                fill = colorsList[buttonValue]                )def quadBezier(point1, point2, point3, percent):    '''Any degree bezier curve is simply nested quadratic bezier curves.    This function takes four parameters:       -Three lists/tuples of floats, representing the three points            the points should follow the form [x,y]       -One float, representing the percent across the resultant line    Returns the endpoints of the appropriate line'''    x1 = point1[0] + percent * (point2[0]-point1[0])    y1 = point1[1] + percent * (point2[1]-point1[1])    x2 = point2[0] + percent * (point3[0]-point2[0])    y2 = point2[1] + percent * (point3[1]-point2[1])    return ((x1,y1),(x2,y2)) def drawCurve (pointsList, drawLevel):     '''Does the drawing of the actual curve and degree handling'''    degree = len(pointsList[0])-2    numLines = 1000    for i in xrange(degree - drawLevel):        pointsList['draw'][2 * i] = pointsList[0][i]    # following iterations only append one point, making appending the starting    # point here necessary    # Loop iterates throgh drawing the lines and/or points making up the curve    for curvePoint in xrange(1,numLines+1):        percent = float(curvePoint)/numLines        for subLevel in xrange(degree):            numQuads = len(pointsList[subLevel]) - 2            # number of quadratic subcurves (3 consecutive points)            pointsList[subLevel+1] = []            for iteration in xrange(numQuads):                tempLine = quadBezier(                    pointsList[subLevel][iteration],                    pointsList[subLevel][iteration+1],                    pointsList[subLevel][iteration+2],                    percent                    )                                if iteration == 0: # removes duplicate writing of points                    pointsList[subLevel+1].append((                        tempLine[0][0],                        tempLine[0][1]                        ))                pointsList[subLevel+1].append((                    tempLine[1][0],                    tempLine[1][1]                    ))                if subLevel == drawLevel: # only draw for the selected level                     pointsList['draw'][2 * iteration + 1] = [                    tempLine[0][0] + percent * (tempLine[1][0]-tempLine[0][0]),                    tempLine[0][1] + percent * (tempLine[1][1]-tempLine[0][1])                    ] # calculate the point of interest, i.e. current percent                     if drawLevel >= numColors:                        colorFill = drawLevel - (                                numColors * (drawLevel / numColors))                        # integer division truncates                    else:                        colorFill = drawLevel                    canvas.create_line(                        pointsList['draw'][2 * iteration][0],                        pointsList['draw'][2 * iteration][1],                        pointsList['draw'][2 * iteration + 1][0],                        pointsList['draw'][2 * iteration + 1][1],                        fill = colorsList[colorFill]                        )                    pointsList['draw'][2 * iteration] = pointsList['draw'][                            2 * iteration + 1]def handleClick(event):    '''interprets a click as creating a point or clicking on a button'''    if event.y <= buttonSize:        if event.x >= width - buttonSize:             # Black button click            if points == {0:[], 'draw':['','']}: # second black button click                canvas.delete("all")                canvas.create_rectangle(width - buttonSize, 0, width,                        buttonSize, fill="black")             else:# first black button click                points.clear()                points[0] = []                points['draw'] = ['','']                canvas.create_rectangle(0, 0,                         width-buttonSize, buttonSize,                        fill="#FFFFFF", outline="#FFFFFF")        elif event.x/buttonSize <= len(points[0]) - 3:            drawLevel = event.x/buttonSize              numQuads = len(points[0]) - (2 + drawLevel)            for i in xrange(2 * numQuads-len(points['draw'])):                points['draw'].append(['',''])            drawCurve(points, drawLevel)    else: # click anywhere in the canvas, creating a point        canvas.create_oval(event.x-radius, event.y-radius,                           event.x+radius, event.y+radius)        points[0].append([float(event.x),float(event.y)])        drawButtons(len(points[0])) if __name__ == "__main__":    # Tkinter handling below    root = Tk()    window = Frame(root, width=width, height=height)    screenwidth = root.winfo_screenwidth()    screenheight = root.winfo_screenheight()    x = (screenwidth - width)/2    y = (screenheight - height)/2    root.geometry('%dx%d+%d+%d' % (width, height, x, y))    window.pack()    canvas = Canvas(window, height=width, width=width)     canvas.create_rectangle(            width - buttonSize, 0,            width, buttonSize,            fill="black"            )    canvas.bind("<Button-1>", handleClick)    canvas.pack()    root.call('wm', 'attributes', '.', '-topmost', True)    root.mainloop()`

BezierCircle.py
Same idea as above but prompts you for a two numbers: the number of points and the number of points to skip. The program will draw the specified number of points evenly spaced around a circle with two points on each space (makes it so every combination of points gets drawn). Black clears the screen but not the point list so you have to restart the program to input a different number of points / skip pair.

Code: Select all
`from Tkinter import *from math import *numberOfPoints = int(raw_input("how many points do you want to draw?\n:"))skip = int(raw_input("How many points do you want to skip?\n:"))colorsList = {0:'orange', 1:'yellow', 2:'green', 3:'blue', 4:'purple', 5:'red'}numColors = len(colorsList)# dimensions of windowwidth = 1300height = 700points = {0:[], 'draw':[]}# Dictionary of lists containing all points# Each value is a 2D list, so calling points follows the format#    points[degree][point number][x or y]#       degree - cubic Beziers have 2 degrees, etc#       point number is the point, points being ordered chronologically#       0 for x, 1 for y# The 'draw' entry will contain a 2D list of two sublists, ocntaining#    the two points that will actually be used to drawradius = 3 # radius of circles around pointsbuttonSize = minHeight = 40 # size of buttonsclass Point:    def __init__(self, x, y):        self.x = x        self.y = ydef evenlySpacedPoints(num, skip):    for i in xrange(num):        rad = skip * (i * (2 * pi) / num)        pointX = 650 + 250 * cos(rad)        pointY = 350 + 250 * sin(rad)        point = Point(pointX, pointY)        handleClick(point)def intToHexColor(num):    hexValue = hex(num)[2:]    if len(hexValue) < 6:        hexValue = (6 - len(hexValue)) * '0' + hexValue    return '#' + hexValue        def drawButtons(numPoints):    for button in xrange(numPoints - 2):        if button >= numColors:            buttonValue = button - numColors * (button / numColors)            # appears redundant but integer division truncates        else:            buttonValue = button        canvas.create_rectangle(                button * buttonSize, 0,                (button + 1) * buttonSize, buttonSize,                fill = colorsList[buttonValue]                )def quadBezier(point1, point2, point3, percent):    '''Any degree bezier curve is simply nested quadratic bezier curves.    This function takes four parameters:       -Three lists/tuples of floats, representing the three points            the points should follow the form [x,y]       -One float, representing the percent across the resultant line    Returns the endpoints of the appropriate line'''    x1 = point1[0] + percent * (point2[0]-point1[0])    y1 = point1[1] + percent * (point2[1]-point1[1])    x2 = point2[0] + percent * (point3[0]-point2[0])    y2 = point2[1] + percent * (point3[1]-point2[1])    return ((x1,y1),(x2,y2)) def drawCurve (pointsList, drawLevel):     '''Does the drawing of the actual curve and degree handling'''    degree = len(pointsList[0])-2    numLines = 1000    for i in xrange(degree - drawLevel):        pointsList['draw'][2 * i] = pointsList[0][i]    # following iterations only append one point, making appending the starting    # point here necessary    # Loop iterates throgh drawing the lines and/or points making up the curve    for curvePoint in xrange(1,numLines+1):        percent = float(curvePoint)/numLines        for subLevel in xrange(degree):            numQuads = len(pointsList[subLevel]) - 2            # number of quadratic subcurves (3 consecutive points)            pointsList[subLevel+1] = []            for iteration in xrange(numQuads):                tempLine = quadBezier(                    pointsList[subLevel][iteration],                    pointsList[subLevel][iteration+1],                    pointsList[subLevel][iteration+2],                    percent                    )                                if iteration == 0: # removes duplicate writing of points                    pointsList[subLevel+1].append((                        tempLine[0][0],                        tempLine[0][1]                        ))                pointsList[subLevel+1].append((                    tempLine[1][0],                    tempLine[1][1]                    ))                if subLevel == drawLevel: # only draw for the selected level                     pointsList['draw'][2 * iteration + 1] = [                    tempLine[0][0] + percent * (tempLine[1][0]-tempLine[0][0]),                    tempLine[0][1] + percent * (tempLine[1][1]-tempLine[0][1])                    ] # calculate the point of interest, i.e. current percent                     if drawLevel >= numColors:                        colorFill = drawLevel - (                                numColors * (drawLevel / numColors))                        # integer division truncates                    else:                        colorFill = drawLevel                    canvas.create_line(                        pointsList['draw'][2 * iteration][0],                        pointsList['draw'][2 * iteration][1],                        pointsList['draw'][2 * iteration + 1][0],                        pointsList['draw'][2 * iteration + 1][1],                        fill = colorsList[colorFill]                        )                    pointsList['draw'][2 * iteration] = pointsList['draw'][                            2 * iteration + 1]def handleClick(event):    '''interprets a click as creating a point or clicking on a button'''    if event.y <= buttonSize:        if event.x >= width - buttonSize:             # Black button click            points.clear()            points[0] = []            points['draw'] = ['','']            canvas.delete("all")            canvas.create_rectangle(width - buttonSize, 0, width,                    buttonSize, fill="black")             canvas.create_oval(400, 100, 900, 600)            for i in xrange(2):                evenlySpacedPoints(numberOfPoints, skip)             handleClick(Point(900,350))        elif event.x/buttonSize <= len(points[0]) - 3:            drawLevel = event.x/buttonSize              numQuads = len(points[0]) - (2 + drawLevel)            for i in xrange(2 * numQuads-len(points['draw'])):                points['draw'].append(['',''])            drawCurve(points, drawLevel)    else: # click anywhere in the canvas, creating a point        canvas.create_oval(event.x-radius, event.y-radius,                           event.x+radius, event.y+radius)        points[0].append([float(event.x),float(event.y)])        drawButtons(len(points[0])) if __name__ == "__main__":    # Tkinter handling below    root = Tk()    window = Frame(root, width=width, height=height)    screenwidth = root.winfo_screenwidth()    screenheight = root.winfo_screenheight()    x = (screenwidth - width)/2    y = (screenheight - height)/2    root.geometry('%dx%d+%d+%d' % (width, height, x, y))    window.pack()    canvas = Canvas(window, height=width, width=width)     canvas.create_rectangle(            width - buttonSize, 0,            width, buttonSize,            fill="black"            )    canvas.create_oval(400, 100, 900, 600)    for i in xrange(2):        evenlySpacedPoints(numberOfPoints, skip)     handleClick(Point(900,350))    canvas.bind("<Button-1>", handleClick)    canvas.pack()    root.call('wm', 'attributes', '.', '-topmost', True)    root.mainloop()`

Both programs can be downloaded directly from my github
notjrogge

Posts: 7
Joined: Sat Jun 22, 2013 4:09 pm