Parabola.py

From GeoMod

Jump to: navigation, search
from visual import *


class xtic:

    def __init__(self, x, dy=0.1):
        self.tic = curve(pos=[(x,0),(x, dy)])
        self.label = label(pos=(x,-0.5), text=str(int(x)), box=False,
                           opacity = 0, height=10)

class ytic:

    def __init__(self, y, dx=0.1):
        self.tic = curve(pos=[(0,y),(dx,y)])
        self.label = label(pos=(-0.5, y), text=str(int(y)), box=False,
                           opacity = 0, height=10)


class x_axis:

    def __init__(self, xmin=-10, xmax=10, dx=1, tic_length = 0.2):
        self.dx = dx
        self.xmin = xmin
        self.xmax = xmax
        self.axis = curve(pos=[(xmin, 0, 0),(xmax, 0, 0)])

        self.tic_length = tic_length
        self.ticmarks = []
        
        for i in arange(-10.0, 10.0+self.dx, self.dx):
            if int(i) <> 0 and int(i) <> xmin and int(i) <> xmax:
                self.ticmarks.append(xtic(i, dy=self.tic_length))
            
class y_axis:

    def __init__(self, ymin=-10, ymax=10, dy=1, tic_length = 0.2):
        self.dy = dy
        self.ymin = ymin
        self.ymax = ymax
        self.axis = curve(pos=[(0, ymin),(0, ymax)])

        self.tic_length = tic_length
        self.ticmarks = []
        
        for i in arange(-10.0, 10.0+self.dy, self.dy):
            if int(i) <> 0 and int(i) <> ymin and int(i) <> ymax:
                self.ticmarks.append(ytic(i, dx=self.tic_length))



class parabola:

    def __init__(self, a, b, c, dx, xaxis, yaxis, squeeze_radius=3.0):
        self.a = a
        self.b = b
        self.c = c
        #self.set_vertex_form_coefficients()
        self.xaxis = xaxis
        self.yaxis = yaxis
        self.squeeze_radius = squeeze_radius
        #draw line
        self.line = curve(color=color.yellow, radius = 0.05,
                          x = arange(xaxis.xmin, xaxis.xmax, dx))
        self.line.y = a * self.line.x * self.line.x + b * self.line.x + c

        #self.write_equation2()
        self.write_equation()


        #controls
        self.inflection_control = sphere(radius=0.2, color = color.red,
                                         pos = self.inflection_pos())
        self.up_arrow= arrow(axis = (0,0.5,0), color=color.yellow,
                             pos = self.inflection_control.pos,
                             headwidth=0.5, visible=0)
        self.down_arrow= arrow(axis = (0,-0.5,0), color=color.yellow,
                             pos = self.inflection_control.pos,
                             headwidth=0.5, visible=0)

        
        self.horizontal_offset = 3.0
        self.horizontal_control = sphere(radius=0.2, color = color.green,
                 pos = self.inflection_pos()+vector(self.horizontal_offset,0,0))

        self.left_arrow= arrow(axis = (-0.5,0,0), color=color.yellow,
                             pos = self.horizontal_control.pos,
                             headwidth=0.5, visible=0)
        self.right_arrow= arrow(axis = (0.5,0,0), color=color.yellow,
                             pos = self.horizontal_control.pos,
                             headwidth=0.5, visible = 0)


        #squeeze control
        self.squeeze = sphere(radius=0.2, color = color.red,
                                         pos = self.squeeze_pos())

    def squeeze_pos(self, offset=vector(0,0,0)):
        a = self.a
        r = self.squeeze_radius
        xb = sqrt((sqrt(4*a*a*r*r+1) - 1)/(a*a))/sqrt(2)
        yb = a*xb*xb
        x = xb + self.inf_x
        y = yb + self.inf_y 
        return vector(x, y)

    def squeeze_a(self):
        '''adjust the a coefficient based on the position of the squeeze control'''
        xb = self.squeeze.pos.x - self.inf_x
        yb = self.squeeze.pos.y - self.inf_y
        if xb > 0:
            self.a = yb/(xb*xb)

        # calculate b and c from the vertical form of the parabola's equation
        self.b = -2.0*self.a*self.inf_x
        self.c = self.a*self.inf_x*self.inf_x + self.inf_y
        
        self.redraw_line()
        self.update_equation()

    def pos_on_circle(self, new_pos):
        ''' new_pos is usually the position of the cursor when moving the squeeze button
          finds the intersection point between the line from the origin to
            new_pos and the circle of the squeeze_radius '''
        if (new_pos.x - self.inf_x) > 0.0:
            m = (self.inf_y - new_pos.y) / (self.inf_x - new_pos.x)
            r = self.squeeze_radius
            xb = sqrt(r*r/(m*m+1))
            yb = sqrt(r*r-xb*xb)
            x = self.inf_x + xb
            if (new_pos.y - self.inf_y) > 0.0:
                y = self.inf_y + yb
            else:
                y = self.inf_y - yb                
        else:
            x = self.inf_x
            if (new_pos.y - self.inf_y) > 0.0:
                y = self.inf_y + self.squeeze_radius
            else:
                y = self.inf_y - self.squeeze_radius
        return vector(x,y,0.0)

    def move_inflection_control(self):
        self.up_arrow.pos = self.inflection_control.pos
        self.down_arrow.pos = self.inflection_control.pos

    def move_horizontal_control(self):
        self.left_arrow.pos = self.horizontal_control.pos
        self.right_arrow.pos = self.horizontal_control.pos

    def redraw_line(self):
        clip_factor = 2.0
        y = self.a * self.line.x * self.line.x + self.b * self.line.x + self.c
        self.line.y = clip(y, clip_factor*self.yaxis.ymin, clip_factor*self.yaxis.ymax)
        

    def squeeze1_pos(self):
        a = self.a
        b = self.b
        c = self.c
        x1 = (-b + sqrt(b*b - 4*a*(c-self.inf_y+5)))/(2*a)
        y1 = self.y(x1)
        return vector(x1, y1, 0.0)

    def inflection_pos(self, offset = 0.5):
        self.inf_x = -self.b / (2.0*self.a)
        self.inf_y = self.y(self.inf_x)
        return vector(self.inf_x, self.inf_y, 0.0)

    def move_via_inflection_point(self, pos):
        x = self.inf_x = pos[0]
        y = self.inf_y = pos[1]
        self.b = -2*self.a*x
        self.c = y + self.a*x*x
        self.redraw_line()
        self.update_equation()
        self.move_inflection_control()
        self.horizontal_control.pos = self.inflection_pos()+vector(self.horizontal_offset,0,0)
        self.move_horizontal_control()
        self.squeeze.pos = self.squeeze_pos(self.inflection_control.pos)
        


    def move_horizontal(self):
        x = self.inf_x = self.horizontal_control.pos.x - self.horizontal_offset
        y = self.inf_y
        self.b = -2*self.a*x
        self.c = y + self.a*x*x
        self.redraw_line()

        self.update_equation()
        #update other controls
        self.inflection_control.pos = vector(self.inf_x, self.inf_y,0)
        self.move_horizontal_control()
        self.move_inflection_control()
        self.squeeze.pos = self.squeeze_pos(self.inflection_control.pos)
        

        
       
        

    def y(self, x):
        return self.a * x * x + self.b * x + self.c
    

    def write_equation(self, pos=vector(5,-9,0)):

        self.equation_label = frame()

        text_height = 30
        text_x = -5
        text_y = -8
        self.lab_y = label(frame = self.equation_label, text='y = ',
                           pos = (text_x,text_y), box=False, opacity = 0,
                           height=text_height, xoffset = 1, line = 0)
        self.lab_x2 = label(frame = self.equation_label, text='x',
                            pos = (text_x+4.25,text_y), box=False, opacity = 0,
                           height=text_height, xoffset = 1, line = 0)
        self.square = label(frame = self.equation_label, text='2',
                            pos = self.lab_x2.pos+vector(1,0.3),
                            box=False, opacity = 0.0,
                            height=text_height*2/3, xoffset = 1, line = 0)
        self.eqnbox = box(pos = self.lab_y.pos+vector(7,-0.2,-0.2),
                             width=.1, length = 14, height=1.5,
                             color=color.blue)

        #a term
        self.set_a_text()
        #self.eqn += '{0} x^2 '.format(self.atxt)

        self.lab_a = label(frame = self.equation_label, text=str(self.atxt),
                           pos = (text_x+2,text_y), box=False, opacity = 0.0,
                           height=text_height, xoffset = 1, line = 0)
        self.lab_a_box = box(pos = self.lab_a.pos+vector(1.25,0,0),
                             width=.1,length = 2.25,
                             color=color.blue)
 
        # b term
        self.set_b_text()
        self.lab_b = label(frame = self.equation_label, text=str(self.btxt),
                           pos = (text_x+6,text_y), box=False, opacity = 0.0,
                           height=text_height, xoffset = 1, line = 0)
        self.lab_b_box = box(pos = self.lab_b.pos+vector(1.5,0,0),
                             width=.1,length = 2.75,
                             color=color.blue)
        self.lab_x = label(frame = self.equation_label, text=self.xtxt,
                            pos = (text_x+9.,text_y), box=False, opacity = .0,
                           height=text_height, xoffset = 1, line = 0)

        # c term
        self.set_c_text()
        self.lab_c = label(frame = self.equation_label, text=str(self.ctxt),
                           pos = (text_x+10,text_y), box=False, opacity = 0.0,
                           height=text_height, xoffset = 1, line = 0)
        self.lab_c_box = box(pos = self.lab_c.pos+vector(2.,0,0),
                             width=.1,length = 3.5,
                             color=color.blue)

##    def update_equation2(self, pos=vector(5,-9,0)):
##        self.equation.text = self.equation_text()

    def set_a_text(self):
        if round(self.a,1) == 1.0:
            self.atxt = ""
        elif round(self.a,1) == -1.0:
            self.atxt = "-"
        else:
            if abs(self.a) <= 10.0:
                self.atxt = round(self.a, 1)
            else:
                self.atxt = int(self.a)


    def set_b_text(self):
        if round(self.b,1) == 0.0:
            self.xtxt = ""
        else:
            self.xtxt = "x"

        if round(self.b,1) == 0.0:
            self.btxt = ""
        elif round(self.b,1) == 1.0:
            self.btxt = "x"
        elif round(self.b,1) == -1.0:
            self.btxt = "-"
        elif self.b >= 0:
            if abs(self.b) >= 10.0:
                self.btxt = '+ {0}'.format(int(self.b))
            else:
                self.btxt = '+ {0}'.format(round(self.b,1))
        elif self.b < 0:
            if abs(self.b) >= 10.0:
                self.btxt = '- {0}'.format(int(abs(self.b)))
            else:
                self.btxt = '- {0}'.format(round(abs(self.b),1))

        
        
    def set_c_text(self):
        #print self.c
        if round(self.c,1) == 0.0:
            self.ctxt = ""
        elif self.c >= 0.0:
            self.ctxt = '+ {0}'.format(round(self.c,1))
        elif self.c < 0.0:
            self.ctxt = '- {0}'.format(round(abs(self.c),1))

    def update_equation(self, pos=vector(5,-9,0)):
        #self.equation_txt = label(pos=pos, text = self.equation())

        #a term
        self.set_a_text()
        self.lab_a.text=str(self.atxt)
 
        # b term
        self.set_b_text()
        self.lab_b.text=str(self.btxt)
        self.lab_x.text = self.xtxt

        # c term
        self.set_c_text()
        self.lab_c.text=str(self.ctxt)

        
    def equation_text(self, pos=vector(5,-9,0)):
        
        self.eqn = 'y = '

        #a term
        if repr(self.a) == "1":
            self.atxt = ""
        elif repr(self.a) =="-1":
            self.atxt = "-"
        else:
            self.atxt = round(self.a, 1)
        self.eqn += '{0} x^2 '.format(self.atxt)

        # b term
        if round(self.b,1) == 0.0:
            self.btxt = ""
        elif round(self.b,1) == 1.0:
            self.btxt = "x"
        elif round(self.b,1) == -1.0:
            self.btxt = "- x"
        elif self.b >= 0:
            self.btxt = '+ {0} x '.format(round(self.b,1))
        elif self.b < 0:
            self.btxt = '- {0} x '.format(round(abs(self.b),1))

        self.eqn += self.btxt


        # c term
        if round(self.c) == 0.0:
            self.ctxt = ""
        elif self.c >= 0:
            self.ctxt = '+ {0}'.format(round(self.c,1))
        elif self.c < 0:
            self.ctxt = '- {0}'.format(round(abs(self.c),1))

        self.eqn += self.ctxt
    
            
        #self.eqn = 'y = {0:r} x^2 + {1} x + {2}'.format(self.a, self.b, self.c)
        return self.eqn
        
            


xmin = -10
xmax = 10
ymin = -10
ymax = 10


scene.range = (xmax - xmin ) / 2.0
scene.autoscale = 0
scene.width = 600
scene.height = 600

#create axes
xaxis = x_axis()
yaxis = y_axis()

# draw parabola: y = ax^2 + bx + c
dx = 0.01

a = 1
b = 2
c = 1

line = parabola(a,b,c, dx, xaxis, yaxis)
pick = None

while 1:
    if scene.mouse.events:
        m1 = scene.mouse.getevent() # obtain event
        if m1.drag and (m1.pick == line.inflection_control or
                        m1.pick == line.horizontal_control or
                        m1.pick == line.squeeze):
            drag_pos = m1.pickpos
            pick  = m1.pick
            #scene.cursor.visible = 0
            
        elif m1.drop: # released at end of drag
            drag = None # end dragging (None is False)
            scene.cursor.visible = 1
            pick = None

        if m1.click:
            print m1.pick
        if m1.click and (m1.pick == line.lab_a or
                           m1.pick == line.lab_b or
                           m1.pick == line.lab_c):
            clicked_label = m1.pick
            print clicked_label.text

    if pick:
        # project onto xy plane, even if scene rotated:
        new_pos = scene.mouse.project(normal=(0,0,1))
        if new_pos != drag_pos: # if mouse has moved
            if pick == line.inflection_control:
                pick.pos.y += new_pos.y - drag_pos.y
                drag_pos = new_pos # update drag position
                #adjust curve via inflection point
                line.move_via_inflection_point(pick.pos)
            elif pick == line.horizontal_control:
                pick.pos.x += new_pos.x - drag_pos.x
                drag_pos = new_pos # update drag position               
                #adjust curve via inflection point
                line.move_horizontal()

            elif pick == line.squeeze:
                #rc = mag(line.inflection_control.pos - new_pos)
                #xc = new_pos.x - line.inflection_control.x
                line.squeeze.pos = line.pos_on_circle(new_pos)
                line.squeeze_a()
                #line.squeeze.pos.x = line.inflection_control.x + (xc * line.squeeze_radius / rc)
                
                #line.squeeze.pos.y = sqrt(line.squeeze_radius*line.squeeze_radius-xc*xc)
                
            else:
                # offset for where the ball was clicked:
                pick.pos += new_pos - drag_pos
                drag_pos = new_pos # update drag position


     
            
Personal tools