Hi
I usually never go for the expert quiz because it's too time consuming, but
this one sounded like fun (and didn't take that long to make :D )
I'm no physics major, so i'm still a bit confused as to the whole kinetic ->
velocity calculation, but at least the cart gains speed when falling, and
looses speed when climbing :)
I used python for the implementation.
/Jimmy
--------------------------------------
#!/usr/bin/env python
#############################################
# File name : rollercoaster.py
# Date : Fri Oct 22 04:41:40 2004
# Author : Jimmy Selgen Nielsen
# Email : spamtrap1-x/claU4MwXpaa/9Udqfwiw@xxxxxxxxxxxxxxxx
# Description :
# Changelog :
# Fri Oct 22 04:41:40 2004 : Initial version
#############################################
from Tkinter import *
import tkMessageBox,tkFileDialog,sys,os,math,time,tkSimpleDialog
class rollercoaster:
'''
Perl QOTW expert quiz #26 solution
Opens file specified in argv[1], or file opened from menu.
'''
def __init__(self,args):
self.rc_tag = None
self.height = 400
self.width = 600
self.gravity = 1
self.mass = 1
self.time_mult = 100
self.debug = 0
self.wnd = None
self.init_display()
if(len(args)):
self.read_rc(args[0])
self.root.mainloop()
def init_display(self):
'''
Initialize main window
'''
self.root = Tk()
self.speedvar = StringVar()
self.main_frame = Frame(self.root)
self.main_frame.pack(fill=BOTH,expand=1)
self.run_button = Button(self.main_frame,text="Run",command=self.run)
self.canvas =
Canvas(self.main_frame,width=self.width,height=self.height)
speed_label = Label(self.main_frame,text="Velocity")
self.speedometer = Entry(self.main_frame,textvariable=self.speedvar)
self.run_button.pack()
self.canvas.pack()
speed_label.pack()
self.speedometer.pack()
self.mainmenu = Menu(self.root)
self.root.config(menu=self.mainmenu)
self.filemenu = Menu(self.mainmenu)
self.filemenu.add_command(label="Open",command=self.open_rc_handler)
self.filemenu.add_command(label="Run",command=self.run)
self.filemenu.add_command(label="Setup",command=self.config_dialog)
self.filemenu.add_command(label="Exit",command=self.root.destroy)
self.mainmenu.add_cascade(label="File",menu=self.filemenu)
def ok_pressed(self):
'''
Config dialog callback for OK
'''
self.gravity = float(self.grav_str.get())
self.mass = float(self.mass_str.get())
self.time_mult = float(self.time_mult_str.get())
self.debug = self.dbg_var.get()
print "new settings, gravity = %02f, mass = %02f, time_mult = %02f" %
(self.gravity,self.mass,self.time_mult)
self.wnd.destroy()
self.wnd = None
def cancel_pressed(self):
'''
Config dialog callback for cancel
'''
self.wnd.destroy()
self.wnd = None
def config_dialog(self):
'''
Configuration dialog for setting mass, gravity, time multiplier and
turning debugging on/off
'''
if(self.wnd==None):
self.wnd = Toplevel()
self.frm = Frame(self.wnd)
self.grav_str = StringVar()
self.mass_str = StringVar()
self.time_mult_str = StringVar()
self.dbg_var = IntVar()
self.grav_str.set(self.gravity)
self.mass_str.set(self.mass)
self.time_mult_str.set(self.time_mult)
self.dbg_var.set(self.debug)
grav_label = Label(self.frm,text="Gravity")
self.grav_entry = Entry(self.frm,textvariable=self.grav_str)
mass_label = Label(self.frm,text="Mass")
self.mass_entry = Entry(self.frm,textvariable=self.mass_str)
delay_label = Label(self.frm,text="Delay multiplier (dist/v *
mult)")
self.time_mult_entry =
Entry(self.frm,textvariable=self.time_mult_str)
debug_btn = Checkbutton(self.frm,text="Debug",variable=self.dbg_var)
ok_btn = Button(self.frm,text="OK",command=self.ok_pressed)
cancel_btn =
Button(self.frm,text="Cancel",command=self.cancel_pressed)
grav_label.pack()
self.grav_entry.pack()
mass_label.pack()
self.mass_entry.pack()
delay_label.pack()
self.time_mult_entry.pack()
debug_btn.pack()
ok_btn.pack()
cancel_btn.pack()
self.frm.pack()
def draw_cart(self,coord):
'''
Draws the "cart" from the given coordinate set
'''
#print "draw_cart, coord = ",coord
self.bbox = [coord[0]-2,self.height -
(coord[1]-2),coord[0]+2,self.height - (coord[1]+2)]
if(self.cart != None):
self.canvas.coords(self.cart,*self.bbox)
else:
self.cart = self.canvas.create_oval(self.bbox,fill="red")
def draw_rc(self):
'''
Draws the rollercoaster from the current coordinate set
'''
self.coords = []
self.cart = None
self.canvas.delete('all')
for p in range(len(self.rc)) :
if(p < len(self.rc)-1):
point = self.rc[p].rstrip('\n').split(' ')
point[0] = float(point[0])
point[1] = float(point[1])
self.coords.append(point)
if(self.rc_tag != None):
self.canvas.delete(self.rc_tag)
self.rc_tag = []
for p in range(len(self.coords)):
if p < len(self.coords)-1:
co = [self.coords[p][0],self.height -
self.coords[p][1],self.coords[p+1][0],self.height - self.coords[p+1][1]]
self.rc_tag.append(self.canvas.create_line(co))
self.canvas.pack(fill=BOTH)
def calc_vel(self, curr_state, next_point):
'''
Calculates new speed and direction for cart for a given point
curr_state : current state of cart
next_point : the current point of the cart
returns : new state with updated cart speed,direction and time to
travel distance
'''
new_state = dict()
dist = 0
nv = 0
co = self.coords[curr_state['coord']]
nco = self.coords[next_point]
new_state = curr_state
#calculate vertical and horizontal "grid" distance travelled
dist = co[0] - nco[0]
nh = co[1] - nco[1]
dist_travelled = math.sqrt(dist**2 + nh**2)
if(self.debug):
print "dist :",dist," height :",nh," dist_travelled
:",dist_travelled
pa = (curr_state['m'] * curr_state['g'] * ((nh/2)/10))
if(pa > 0):
nv = math.sqrt(pa)
else:
nv = -(math.sqrt(-pa))
if(self.debug):
print "nv = ",nv
new_state['v'] += nv
#Change cart direction if velocity is less than zero
if(new_state['v']<0):
new_state['direction'] = 0 - new_state['direction']
#change negative velocity to positive velocity going the other way
:)
new_state['v'] = -new_state['v']
if(self.debug):
print "new v :",new_state['v']
new_state['coord'] = next_point
#Set time to travel distance at new velocity
new_state['t'] = dist_travelled / (new_state['v']+1)
if(self.debug):
print "new t = ",new_state['t']
return(new_state)
def read_rc(self,fname):
'''
Read a .rc file from disk
'''
try:
f = open(fname)
self.rc = f.readlines()
if(self.debug):
print "read %d lines from %s" % (len(self.rc),fname)
except:
tkMessageBox.showerror('Error opening file','Error opening file
"'+fname+'"')
self.rc = None
self.draw_rc()
def open_rc_handler(self):
'''
Menu callback for "File->Open"
'''
fname = tkFileDialog.askopenfilename(filetypes=[(".rc", ".rc")],
title="Select the file")
if(fname != None):
self.read_rc(fname)
def tick(self):
'''
"Mainloop" of the simulation. Callback to the windowmanager for every
iteration of the simulation
'''
self.curr_state =
self.calc_vel(self.curr_state,self.curr_state['coord']+self.curr_state['direction'])
self.draw_cart(self.coords[self.curr_state['coord']])
self.speedvar.set(round(self.curr_state['v'],2))
nco = self.curr_state['coord'] + self.curr_state['direction']
if(self.curr_state['v'] != 0 and nco >= 0 and nco < len(self.coords)):
self.root.after(int(self.curr_state['t']*self.time_mult),self.tick)
else:
return(0)
def run(self):
'''
Initialize and start mainloop of simulation
'''
self.curr_state = {
'coord' : 0, #Index into rollercoaster coordinates
'v':0, #Cart Velocity in the current direction
'm':self.mass, #Mass
'g':self.gravity, #Gravity
't':0, #Time to travel distance
'direction':1 #Direction of cart, 1 = left to right, -1 =
right to left
}
self.calc_vel(self.curr_state,1)
self.root.after(int(self.curr_state['t']*self.time_mult),self.tick)
if __name__ == '__main__' :
rc = rollercoaster(sys.argv[1:])
|