From GeoMod
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