Coriolis.py

From GeoMod

Jump to: navigation, search

To run the code, once you have VPython installed, you can either.

  1. Download this file: coriolis_16.py
  2. 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
                   
 
Personal tools