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
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
# clock object that ensure that animation has the same
# on all machines, regardless of the actual machine speed.
clock = pygame.time.Clock()
def load_image(name):
    image = pygame.image.load(name)
    return image
class MyCircle(pygame.sprite.Sprite):
    def __init__(self, color, width, height):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([width, height])
        self.rect = self.image.get_rect()
        self.image.fill(WHITE)
        cx = self.rect.centerx
        cy = self.rect.centery
        pygame.draw.circle(self.image, color, (width//2, height//2), cx, cy)
        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'''
    x += 10
    y += 10
    return x, win_height - y
def main():
    # initializing pygame
    pygame.init()
    # top left corner is (0,0)
    win_width = 640
    win_height = 640
    screen = pygame.display.set_mode((win_width, win_height))
    pygame.display.set_caption('2D projectile motion')
    # setting up a sprite group, which will be drawn on the
    # screen
    my_sprite = MyCircle(RED, 5, 5)
    my_group = pygame.sprite.Group(my_sprite)
    # setting up simulation
    sim = Simulation()
    sim.setup(75., 45)
    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
        clock.tick(30)
        # update sprite x, y position using values
        # returned from the simulation
        my_sprite.rect.x, my_sprite.rect.y = sim_to_screen(win_height, sim.pos[0], sim.pos[1])
        event = pygame.event.poll()
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit(0)
        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()
    plt.figure(1)
    plt.plot(sim.trace_x, sim.trace_y)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.axis('equal')
    plt.title('2D projectile trajectory')
    plt.show()
if __name__ == '__main__':
    main()Also available at here.
Via Canvas.
2d-projectile-simulation.py2d-projectile-table.pdf