Simulation and Modeling (CSCI 3010U)
Faculty of Science, Ontario Tech University
http://vclab.science.ontariotechu.ca
Check Canvas for Due Date
The goal of this lab is to simulate a 2D projectile simulation. You are asked to use these simulations to determine the angle at which the ball must be thrown to achieve the largest distance \(r\).
Projectile of mass \(m\) is thrown with an initial speed \(s\) at an angle \(\theta\). Mass \(m\) moves in a 2D plane under the influence of gravity (along the negative \(y\)-axis) and friction which is proportional to the current speed and acts against the direction of motion. The constant of proportionality is given by the coefficient of friction.
Complete the following table: Table.
For these experiments, we assume the following:
import pygame, sys
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import ode
# set up the colors
= (0, 0, 0)
BLACK = (255, 255, 255)
WHITE = (255, 0, 0)
RED = (0, 255, 0)
GREEN = (0, 0, 255)
BLUE
# clock object that ensure that animation has the same
# on all machines, regardless of the actual machine speed.
= pygame.time.Clock()
clock
def load_image(name):
= pygame.image.load(name)
image return image
class MyCircle(pygame.sprite.Sprite):
def __init__(self, color, width, height):
__init__(self)
pygame.sprite.Sprite.
self.image = pygame.Surface([width, height])
self.rect = self.image.get_rect()
self.image.fill(WHITE)
= self.rect.centerx
cx = self.rect.centery
cy self.image, color, (width//2, height//2), cx, cy)
pygame.draw.circle(self.rect = self.image.get_rect()
def update(self):
pass
class Simulation:
def __init__(self):
# TODO
self.paused = True # starting in paused mode
self.solver = ode(self.f)
self.solver.set_integrator('dop853')
self.solver.set_f_params(self.gamma, self.gravity)
def f(self, t, state, arg1, arg2):
# TO DO
pass
def setup(self, speed, angle_degrees):
# TO DO
self.trace_x = [self.pos[0]]
self.trace_y = [self.pos[1]]
def step(self):
self.cur_time += self.dt
# TODO
def pause(self):
self.paused = True
def resume(self):
self.paused = False
def sim_to_screen(win_height, x, y):
'''flipping y, since we want our y to increase as we move up'''
+= 10
x += 10
y
return x, win_height - y
def main():
# initializing pygame
pygame.init()
# top left corner is (0,0)
= 640
win_width = 640
win_height = pygame.display.set_mode((win_width, win_height))
screen '2D projectile motion')
pygame.display.set_caption(
# setting up a sprite group, which will be drawn on the
# screen
= MyCircle(RED, 5, 5)
my_sprite = pygame.sprite.Group(my_sprite)
my_group
# setting up simulation
= Simulation()
sim 75., 45)
sim.setup(
print('--------------------------------')
print('Usage:')
print('Press (r) to start/resume simulation')
print('Press (p) to pause simulation')
print('Press (space) to step forward simulation when paused')
print('--------------------------------')
while True:
# 30 fps
30)
clock.tick(
# update sprite x, y position using values
# returned from the simulation
= sim_to_screen(win_height, sim.pos[0], sim.pos[1])
my_sprite.rect.x, my_sprite.rect.y
= pygame.event.poll()
event if event.type == pygame.QUIT:
pygame.quit()0)
sys.exit(
if event.type == pygame.KEYDOWN and event.key == pygame.K_p:
sim.pause()continue
elif event.type == pygame.KEYDOWN and event.key == pygame.K_r:
sim.resume()continue
else:
pass
# clear the background, and draw the sprites
screen.fill(WHITE)
my_group.update()
my_group.draw(screen)
pygame.display.flip()
if sim.pos[1] <= -1.:
pygame.quit()break
# update simulation
if not sim.paused:
sim.step()else:
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
sim.step()
1)
plt.figure(
plt.plot(sim.trace_x, sim.trace_y)'x')
plt.xlabel('y')
plt.ylabel('equal')
plt.axis('2D projectile trajectory')
plt.title(
plt.show()
if __name__ == '__main__':
main()
Also available at here.
Via Canvas.
2d-projectile-simulation.py
2d-projectile-table.pdf