Coriolis.py
From GeoMod
To run the code, once you have VPython installed, you can either.
- Download this file: coriolis_16.py
- or, copy and paste the following code to a file (named "coriolis_16.py" for example) using your favorite text editor.
from visual import *
from visual.controls import *
from math import *
from commands import *
# Program to demonstrate the effects of coriolis
# Written by Lensyl Urbano
# Department of Earth Sciences
# University of Memphis
class camera_move:
"event details"
def __init__(self, st, ed, center, forward, range):
self.start = st #event starttime
self.end = ed #event end time
# self.stpos = ang
self.center = center #ending center position
self.forward = forward
self.range = range
self.type = "move"
def world_space_pos(frame, local):
"""Returns the position of local in world space."""
x_axis = norm(frame.axis)
z_axis = norm(cross(frame.axis, frame.up))
y_axis = norm(cross(z_axis, x_axis))
return frame.pos+local.x*x_axis+local.y*y_axis+local.z*z_axis
def radi(vec):
return sqrt(vec.x*vec.x + vec.y*vec.y)
def cannon_elev(by, bzero):
z = (6 - (by - bzero)) / 5
#print z
return z
#For screen capture
scene.width=640+4+4
scene.height=480+24+4
scene.x = 0
scene.y = 0
scene.title = "Coriolis"
#set Camtasia capture region to 4, 24 and 640x480
#scene.background = (1,1,1)
l_slomo = 0 #set l_slomo = 1 for slow motion and l_slomo = 0 for no slow motion
l_movie_capture = 1 #set to 1 to capture movie from output
slomo = 10.0
scene.autoscale = 0
scene.range = 36
disk = frame(pos=(0,0,1))
turntable = cylinder(frame=disk, pos=(0,0,-2), radius = 20, axis = (0,0,1), color=(.25,.5,1))
centerpt = cone(frame=disk, pos=(0,0,-1), axis=(0,0,1), radius=1, color=(1,0,0))
spoke1 = cylinder(frame=disk, axis = (0,40,0), pos=(0,-20,-1), radius=0.2, color=(1,1,0))
spoke2 = cylinder(frame=disk, axis = (40,0,0), pos=(-20,0,-1), radius=0.2, color=(1,1,0))
hash1 = cylinder(pos=(0,20,0),radius=0.25, axis=(0,2,0),color=(1,1,0))
hash2 = cylinder(pos=(20,0,0),radius=0.25, axis=(1,0,0),color=(1,1,0))
hash3 = cylinder(pos=(0,-20,0),radius=0.25, axis=(0,-2,0),color=(1,1,0))
hash2 = cylinder(pos=(-20,0,0),radius=0.25, axis=(-2,0,0),color=(1,1,0))
#centlabel = label(pos=centerpt.pos, text='Center', xoffset=20, yoffset=12, height=10, border=6)
butrad = 1.5
resetbut = sphere(pos=(20,-20,0), radius = butrad)
resetlabel = label(pos=resetbut.pos, text="Reset All",height=10, border=6, xoffset=20, yoffset=12)
velzero = 10
velline = cylinder(pos=(23,velzero,0), axis = (0,5,0), radius = 0.5)
velbut = sphere(pos=(23,velzero+5,0), radius = butrad, color=(0,1,0))
velstop = sphere(pos=(26,velzero,0), radius = butrad, color=(1,0,0))
vellabel = label(pos=velline.pos+vector(0,5,0), text="Marble velocity (y)",height=10, border=2, xoffset=6, yoffset=5, space = 2)
rotzero = 1
rotateline = cylinder(pos=(23,rotzero-5,0), axis = (0,10,0), radius = 0.5)
rotatebut = sphere(pos=(23,rotzero,0), radius = butrad, color=(0,1,0))
rotatestop = sphere(pos=(26,rotzero,0), radius = butrad, color=(1,0,0))
rotatelabel = label(pos=rotateline.pos+rotateline.axis/4, text="Rotation",height=10, border=6, xoffset=10, yoffset=0, space = 2)
frictzero = -12
frictline = cylinder(pos=(23,frictzero,0), axis = (0,5,0), radius = 0.5)
frictbut = sphere(pos=(23,frictzero,0), radius = butrad, color=(0,1,0))
frictstop = sphere(pos=(26,frictzero,0), radius = butrad, color=(1,0,0))
frictlabel = label(pos=frictline.pos+frictline.axis/2, text="Friction",height=10, border=6, xoffset=10, yoffset=0, space = 2)
circ1 = ring(frame=disk, pos=(0,0,-1), radius = 10, thickness = 0.1, axis = (0,0,1), color=(1,1,0))
cannon = frame(pos=(0,-24,cannon_elev(frictbut.pos.y, frictzero)))
cannonlabel = label(pos=(cannon.pos.x-4,cannon.pos.y,0), text="Cannon",height=10, border=2)
cannonlabel.pos.z = 0
barrel = cylinder(frame=cannon, radius=1, axis=(0,5,0), color=(1,1,0))
loader = box(frame=cannon, length=2.5, height=2.5, width=2.5, color=(1,1,0))
ball = sphere(frame = cannon, pos=(loader.x, loader.y, loader.z+2), radius=1., color=(1,0,0))
targline = cylinder(axis = (0,40,0), visible=0)
#********************************
#NOTE: ball2 IS THE SPHERE THAT IS MOVED OVER THE TURNTABLE
ball2 = sphere(radius = 1.0, color = (1,0,0))
ball2_vel = norm(barrel.axis)
ball2_viewbut = sphere(pos=(16,16,0), radius = butrad, color=(1,1,0))
ball2_view = 0
#vel2 = 5 * norm(barrel.axis)
ball2.visible = 0
#ball2_label = label(pos=ball2.pos, text="ball2",height=10, border=2, xoffset=0, yoffset=8)
#set up a target
targ = sphere(pos=(-16,16,0),radius = 2, color=(1,1,0))
targlabel = label(pos=targ.pos, text="Target",height=10, border=2, xoffset=0, yoffset=8, space= targ.radius)
pick = None
#userzoom = 0
#userspin = 0
frict_vel = vector(0.,0,0)
# affecting animation speed
framerate = 60.0
disk_angvel = 2.0 * framerate / 1000.0
vel2_fac = 0.5 * framerate / 1000.0
ball2_trail = 0
npts = 0
langle = 0
rotdir = 1
ball2_trail = []
ntrails = -1
runtime = 0.0
#camera motion events
events = []
ball2_camera_event = 0
lc_move = 0
counter = 0
frm = 0
orig_center = scene.center
orig_forward = scene.forward
orig_range = scene.range
while 1:
rate(framerate)
runtime += 1.0/framerate
if l_slomo == 1:
rate(slomo)
if scene.mouse.events:
m1 = scene.mouse.getevent() # obtain drag or drop event
if m1.drag and (m1.pick == barrel or
m1.pick == rotatebut or
m1.pick == frictbut or
m1.pick == velbut or
m1.pick == targ):
drag_pos = m1.pickpos
pick = m1.pick
scene.cursor.visible = 0 # make cursor invisible
elif m1.drop:
pick = None # end dragging
scene.cursor.visible = 1 # cursor visible
if m1.pick == ball: #fire ball
ball.color = (1,0,0)
if m1.release:
ball.visible = 0
ball2.visible = 1
ball2.pos = world_space_pos(cannon,barrel)
ball2.y = ball2.y + 5
ball2.visible = 1
#ball2.trail = curve(frame=disk, color=ball2.color, radius = 0.2)
ball2_trail.append(curve(frame=disk, color=(1,0,0), radius = 0.2))
ntrails = ntrails + 1
targline = cylinder(axis = (0,40,0), pos=ball2.pos, radius=0.1, color=(0,1,1))
if m1.pick == ball2 and m1.release: #reset
## scene.center = orig_center
## scene.forward = orig_forward
## scene.range = orig_range
events.append(camera_move(runtime, runtime + 2.0,
orig_center, orig_forward, orig_range))
lc_move = 0
ball2_view = 0
if m1.pick == loader:
ball.visible = 1
ball2.visible = 0
#ball2_trail[ntrails].color = (0,1,0)
if targline:
targline.visible = 0
if m1.pick == resetbut and m1.release:
ball.visible = 1
ball2.pos = world_space_pos(cannon,loader)
ball2.visible = 0
if ntrails > -1:
for a in ball2_trail:
a.visible = 0
if targline:
targline.visible = 0
targ.color = (1,1,0)
if m1.pick == rotatestop and m1.release:
rotatebut.pos = (23,rotzero,0)
if m1.pick == frictstop and m1.release:
frictbut.pos = (23,frictzero,0)
if m1.pick == velstop and m1.release:
velbut.pos = (23,velzero,0)
if m1.pick == ball2_viewbut and m1.release:
events.append(camera_move(runtime, runtime + 2.0,
ball2.pos + vector(0,10,0),
vector(0,1,-.2), vector(10.0 , 10, 10)))
ball2_viewbut.color = (1,0,0)
ball2_view = 1
ball2_camera_event = 1
lc_move = 0
if pick:
new_pos = scene.mouse.project(normal=(0,0,1))
if new_pos != drag_pos:
pick.pos += new_pos - drag_pos
drag_pos = new_pos
if pick == barrel:
pick.pos.z = cannon_elev(frictbut.pos.y, frictzero)
if pick == rotatebut:
pick.pos.x = rotateline.pos.x
pick.pos.z = rotateline.pos.z
if new_pos.y > 5+rotzero:
pick.pos.y = 5+rotzero
elif new_pos.y < -5+rotzero:
pick.pos.y = -5+rotzero
pick.pos.y = round(pick.pos.y,2)
rotatelabel.text = str(round((pick.pos.y - rotzero)/5,1))
if pick == frictbut:
pick.pos.x = frictline.pos.x
pick.pos.z = frictline.pos.z
if new_pos.y > 5+frictzero:
pick.pos.y = 5+frictzero
elif new_pos.y < frictzero:
pick.pos.y = frictzero
pick.pos.y = round(pick.pos.y,2)
frictlabel.text = str(round((pick.pos.y - frictzero)/5,1))
cannon.pos.z = cannon_elev(frictbut.pos.y, frictzero)
if pick == velbut:
pick.pos.x = velline.pos.x
pick.pos.z = velline.pos.z
if new_pos.y > 5+velzero:
pick.pos.y = 5+velzero
elif new_pos.y < velzero:
pick.pos.y = velzero
pick.pos.y = round(pick.pos.y,2)
vellabel.text = str(round((pick.pos.y - velzero)/5,1))
if pick == targ:
pick.pos.z = 2.0
else:
vellabel.text = "Marble velocity (y)"
rotatelabel.text = "Rotation"
frictlabel.text = "Friction"
loader.pos = barrel.pos
ball.pos = (loader.x, loader.y, loader.z+2)
#rotate turntable
rotdir = rotatebut.pos.y - rotzero
angvel = rotdir*disk_angvel*2*pi/360
disk.rotate(angle=angvel, axis=(0,0,1))
#rotate target if it is on turntable
rad = sqrt(targ.pos.x * targ.pos.x + targ.pos.y * targ.pos.y)
#centlabel.text = `rad`
if rad < 20 and pick <> targ:
targ.rotate(angle=angvel, axis=(0,0,1), origin = (0,0,0))
#target hit
prox = targ.pos - ball2.pos
if radi(prox) < (targ.radius + ball2.radius): #then we have hit
targ.color = (1,0,0)
#add friction
frict = (frictbut.pos.y - frictzero) * 0.2 # to get a number between 0 and 1
#add trail to moving ball
if ball2.visible:
#********************************
# ball2_vel IS THE VELOCITY OF ball2 BASED ON THE VALUE SET BY THE SLIDER (velbut.pos.y - velzero)
# ITS DIRECTION IS THE SAME AS THE AXIS OF THE CANNON (ball2.axis)
# THE (framerate*vel2_fac) is to be able to show things in slow motion (for screen captures)
ball2_vel = (velbut.pos.y - velzero) * norm(barrel.axis) * (framerate*vel2_fac)
rad = sqrt(ball2.pos.x * ball2.pos.x + ball2.pos.y * ball2.pos.y)
dist = (angvel * rad)
if angvel <> 0:
if ball2.pos.x == 0.0:
frict_vel = (angvel/abs(angvel)) *(frict * vector(1,0,0)) * dist * framerate
elif ball2.pos.y == 0.0:
frict_vel = (angvel/abs(angvel)) *(frict * vector(0,1,0)) * dist * framerate
else:
frict_vel = (frict * norm(cross(vector(0,0,1),ball2.pos))) * dist * framerate
#frictlabel.text = `frict_vel`
vel2 = ball2_vel + frict_vel
#********************************
#UPDATE THE POSITION OF ball2 IN INERTIAL SPACE
ball2.pos = (vel2 / framerate) + ball2.pos
#rotate trail
if rad < 20:
if (disk.axis.y <> 0):
langle = atan(disk.axis.x/disk.axis.y)
if disk.axis.y < 0:
langle = langle - pi
lpos = rotate(ball2.pos, angle=langle-(pi/2), axis=(0,0,1))
else:
lpos = ball2.pos
#print `ball2_trail[ntrails]`
#********************************
# lpos IS THE POSITION OF ball2 RELATIVE TO THE ROTATING AXIS OF THE TURNTABLE.
lpos = lpos - disk.pos
ball2_trail[ntrails].append(pos=lpos)
else:
ball2.pos = world_space_pos(cannon,barrel)
#change view if necessary
if ball2.visible and ball2_view and ball2_camera_event == 0:
scene.center = ball2.pos + vector(0,10,0)
scene.range = 10
scene.forward = vector(0,1,-.2)
if ball2.visible and radi(ball2.pos) > 20 and ball2_view:
ball2_view = 0
ball2_viewbut.color = (1,1,0)
scene.center = (0,0,0)
scene.forward = (0,0,-1)
scene.range = 36
for e in events:
#earthlabel.text = `e.start` + `e.end` + `runtime` + `e.type`
if runtime >= e.start and runtime <= e.end:
#earthlabel.text = `e.type`
if e.type == "rotate":
#print "scene.forward " + `scene.forward`
scene.forward = rotate(scene.forward, angle=(1.0/framerate)*e.angle*2*pi/360, axis=e.axis)
scene.up = rotate(scene.up, angle=(1.0/framerate)*e.angle*2*pi/360, axis=e.axis)
if e.type == "move":
if lc_move == 0:
ns = (e.end - runtime) * framerate
#print "ns =", ns
#print "scene.center", scene.center
#print "e.endpos", e.center
distan = e.center - scene.center
#print "dist = ", distan
distan = distan / ns
#print "dist 2 = ", distan
rote = e.forward - scene.forward
rote = rote / ns
#print "e.range", e.range
#print "scene.range", scene.range
crang = (e.range - scene.range) / ns
lc_move = 1
counter = 0
counter += 1
#print counter
scene.center = scene.center + (distan )
scene.forward = scene.forward + rote
scene.range = scene.range + crang
#print scene.center
if abs(runtime - e.end) < (framerate/ns):
ball2_camera_event = 0

