python starter
python starter

Reputation: 183

Trouble with drawing Bezier curve in Python

Hi people i need some help.I wrote a code in Python (Tkinter) that is drawing a 3 degree Bezier curve,and it works...kinda.My problems are:How can i directly input from keybord x and y coordinates for control points,and not first x then y (like in my code).Second issue is regarding parametar (u or t) value.Usually it is normalised (value 0 to 1) but for loop wouldnt work with decimal values for steps (i know its obvious :)).If i put u=1->1000 and then divide value by 1000 it is numericaly unstable.Third and least important is how to draw curve,you will se that I used small lines which is really stupid but dont know other way here is my code,sorry its in Word format,had some troubles:

import tkinter

from tkinter import*

master=Tk()

w=Canvas(master,width=800,height=800)


w.pack()

def kriva():

        P0=[]
        P1=[]
        P2=[]
        P3=[]
        P0.append (float(input(" x for P0")))  #HERE IS THE PLACE FOR INPUT COORDINATES OF CONTROL POINTS
        P0.append (float(input(" y for P0")))
        P1.append(float(input(" x for P1")))
        P1.append(float(input(" yfor P1")))    
        P2.append(float(input(" x for za P2")))
        P2.append(float(input(" y for za P2")))

        P3.append(float(input(" x for P3")))
        P3.append(float(input(" y for P3")))


        for u  in range (0,1000,1):
            u=(u/1000) # PARAMETAR FOR CURVE
               x=(P0[0]*(1-u)**3+P1[0]*3*u*(1-u)**2+P2[0]*3*u**2*(1-u)+P3[0])*u**3#BERNSTAIN    POLYNOMS FOR X AND Y         
               y=(P0[1]*(1-u)**3+P1[1]*3*u*(1-u)**2+P2[1]*3*u**2*(1-u)+P3[1]*u**3)
            x1=x+1 #THIS IS END OF THE LINE
            y1=y+1
            print (x)
            print (y)
            w.create_line(x,y,x1,y1) #IM DOOING THIS THIS WAY BECAUSE I DONT KNOW ANY OTHER    WAY TO DRAW CURVE :)

kriva()
mainloop()

Fixed all issues..here is the final code...special thanks for help

import tkinter

from tkinter import*

master=Tk()

w=Canvas(master,width=800,height=800)

w.pack()

def kriva():

P0=[]

P1=[]

P2=[]

P3=[]

#p0:

unosp0=input("unesi koordinate za P0")

koordinatep0=unosp0.split(',')

print (koordinatep0)

P0.append(float((koordinatep0[0])))

P0.append(float((koordinatep0[1])))

print (P0)

#p1:

unosp1=input("unesi koordinate za P1")

koordinatep1=unosp1.split(',')

print (koordinatep1)

P1.append(float((koordinatep1[0])))

P1.append(float((koordinatep1[1])))

print (P1)

#p2:

unosp2=input("unesi koordinate za P2")

koordinatep2=unosp2.split(',')

print (koordinatep2)

P2.append(float((koordinatep2[0])))

P2.append(float((koordinatep2[1])))

print (P2)

#p3:

unosp3=input("unesi koordinate za P3")

koordinatep3=unosp3.split(',')

print (koordinatep3)

P3.append(float((koordinatep3[0])))

P3.append(float((koordinatep3[1])))

print (P3)

x1=P0[0]

y1=P0[1]

for u  in range (0,1001,1):

    u=(u/1000)

    x=(P0[0]*(1-u)**3+P1[0]*3*u*(1-u)**2+P2[0]*3*u**2*(1-u)+P3[0]*u**3)

    y=(P0[1]*(1-u)**3+P1[1]*3*u*(1-u)**2+P2[1]*3*u**2*(1-u)+P3[1]*u**3)

print (x)

print (y)

print (x1)

print(y1)

x1=str(x+000001)

y1=str(x+0,00001)

    linija=w.create_line(x,y,x1,y1)

    x1=x

    y1=y

kriva()

mainloop()

Upvotes: 2

Views: 2424

Answers (1)

brianmearns
brianmearns

Reputation: 9967

To get both X and Y coordinates from the same input, you'll just have to decide on a simple syntax and parse it. For instance, you can say that X and Y will be entered in that order, separated by a comma, and then do something like this

inp = input('Enter X,Y coordinates:')
parts = inp.split(',')
if len(parts) != 2:
    raise ValueErrror("You entered the wrong number of coordinates!")
x = float(parts[0].strip())
y = float(parts[1].strip())

Or much more compactly (but without the error handling):

x, y = (float(p.strip()) for p in input('Enter X,Y coordinates:').split(','))

For iterating over a floats, your idea to divide by 1000 is fine. What do you mean its numerically unstable? I would do it exactly like this:

num_of_points = 1000
for i in xrange(num_of_points+1):
    u = float(i) / float(num_of_points)

Notice I set the range to num_of_points+1, so that num_of_points will actually be included: that means the loop will actually go from 0 to 1.0, inclusive.

Drawing a line between the points is probably the correct answer. Yes, it is an approximation, but it's limited only by the resolution of the image and the number of points you want to draw. If you want a more accurate depiction, just iterate over a larger num_of_points.

To do anything more accurate than a line, you would need to figure out some approximation of what the curve looks like in between those two points. But that's exactly what your script is doing, it's approximating a Bezier. So you could recursively approximate the curve between every pair of points you find, but that's the same as just adding more points to your approximation.

That said, the specific way in which you're drawing lines is baffling me. As far as I can tell, you're calculating a point (x,y) on the curve (more specifically (x(u), y(u)) for parameter u), and then drawing a line from that point to a point at (x+1, y+1). In other words, you're always just drawing a little 45-degree "tick" off of the point on the curve. Not really sure why, unless you wanted to just make a little mark and weren't sure how to just fill in a single pixel.

Whatever the reason, the correct way to do it is to determine two "adjacent" points on the curve, and connect them with a line. Something like:

last_point = calculate_point_on_curve(u=0)
for i in xrange(1, num_of_points+1):
    x, y = calculate_point_on_curve( float(i) / float(num_of_points) )
    last_x, last_y = last_point
    w.create_line(last_x, last_y, x, y)
    last_point = (x, y)

Here the actual parametric equation for calculating the points on the line is hidden inside the calculate_point_on_curve function, just to simplify the code snippet.

To deal with your apparent decimation issue, try this:

u = 0.0
for t in xrange(10001):
    calculate_point_on_curve(u)
    u = u + 0.0001

Upvotes: 2

Related Questions