Simulation and Modeling (CSCI 3010U)
Faculty of Science, Ontario Tech University
http://vclab.science.ontariotechu.ca
Discuss in class
Consider a mass-spring system that lives in a flat (2D) world. One
end of the spring is connected to a fixed hinge sitting at location
\((0,0)\). The other end of the spring
is connected to a mass \(m\). The rest
length of this spring is \(l\), and the
Hooke’s coefficient is \(k\). Provide
the Simulation class that simulates this mass spring
system.

The following files are provided for you, which will use the
Simulation class provided by you. It assumes that your
class is available in file sim.py.
Includes routines used by mass-spring-2d.py file.
"""
author: Faisal Z. Qureshi
email: faisal.qureshi@uoit.ca
website: http://www.vclab.ca
license: BSD
"""
import pygame
# set up the colors
BLACK = (0, 0, 0, 255)
WHITE = (255, 255, 255, 0)
RED = (255, 0, 0, 255)
GREEN = (0, 255, 0, 255)
BLUE = (0, 0, 255, 255)
def load_image(name):
    image = pygame.image.load(name)
    return image
class MyCircle(pygame.sprite.Sprite):
    def __init__(self, color, width, height, alpha=255):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([width, height], flags=pygame.SRCALPHA)
        self.rect = self.image.get_rect()
        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()
        self.picked = False
    def set_pos(self, pos):
        self.rect.x = pos[0] - self.rect.width//2
        self.rect.y = pos[1] - self.rect.height//2
    def update(self):
        pass
class MyRect(pygame.sprite.Sprite):
    def __init__(self, color, width, height, alpha=255):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([width, height], flags=pygame.SRCALPHA)
        self.rect = self.image.get_rect()
        pygame.draw.rect(self.image, color, self.rect)
        self.picked = False
    def set_pos(self, pos):
        self.rect.x = pos[0] - self.rect.width//2
        self.rect.y = pos[1] - self.rect.height//2
    def update(self):
        pass
def to_screen(x, y, win_width, win_height):
    return win_width//2 + x, win_height//2 - y
def from_screen(x, y, win_width, win_height):
    return x - win_width//2, win_height//2 - y
class MyText():
    def __init__(self, color, background=WHITE, antialias=True, fontname="comicsansms", fontsize=16):
        pygame.font.init()
        self.font = pygame.font.SysFont(fontname, fontsize)
        self.color = color
        self.background = background
        self.antialias = antialias
    
    def draw(self, str1, screen, pos):
        text = self.font.render(str1, self.antialias, self.color, self.background)
        screen.blit(text, pos)Calls your Simulation class to do the heaving lifting.
"""
author: Faisal Z. Qureshi
email: faisal.qureshi@uoit.ca
website: http://www.vclab.ca
license: BSD
"""
import pygame, sys
import matplotlib.pyplot as plt
import numpy as np
# import sim_rk4 as Simulation
import sim as Simulation
import util
def main():
    # sim title
    title = 'Mass-Spring System'
    # initializing pygame
    pygame.init()
    # clock object that ensure that animation has the same
    # on all machines, regardless of the actual machine speed.
    clock = pygame.time.Clock()
    # fonts
    text = util.MyText(util.BLACK)
    # setting up a sprite group, which will be drawn on the
    # screen
    ball = util.MyRect(color=util.BLUE, width=32, height=32)
    center = util.MyRect(color=util.RED, width=4, height=4)
    x_axis = util.MyRect(color=util.BLACK, width=620, height=1)
    y_axis = util.MyRect(color=util.BLACK, width=1, height=460)
    my_group = pygame.sprite.Group([x_axis, y_axis, ball, center])
    # set up drawing canvas
    # top left corner is (0,0) top right (640,0) bottom left (0,480)
    # and bottom right is (640,480).
    win_width = 640
    win_height = 480
    screen = pygame.display.set_mode((win_width, win_height))
    pygame.display.set_caption(title)
    # setting up simulation
    sim = Simulation.Simulation(title)
    # sim.init(state=np.array([200,200,0,0], dtype='float32'), mass=100., k=.01, l=200.) Try some other values
    sim.init(state=np.array([200,200,0,0], dtype='float32'), mass=10., k=10, l=200.)
    sim.set_time(0.0)
    sim.set_dt(0.1)
    print ('--------------------------------')
    print ('Usage:')
    print ('Press (r) to start/resume simulation')
    print ('Press (p) to pause simulation')
    print ('Press (q) to quit')
    print ('Press (space) to step forward simulation when paused')
    print ('Use mouse left button down to move mass around (only when simulation paused)')
    print ('--------------------------------')
    # Transformation to screen coordinates
    # Here 0,0 refers to simulation coordinates
    center.set_pos(util.to_screen(0, 0, win_width, win_height))
    x_axis.set_pos(util.to_screen(0, 0, win_width, win_height))
    y_axis.set_pos(util.to_screen(0, 0, win_width, win_height))
    while True:
        # 30 fps
        clock.tick(30)
        # update sprite x, y position using values
        # returned from the simulation
        ball.set_pos(util.to_screen(sim.state[0], sim.state[1], win_width, win_height))
        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:
            if not ball.picked:
                sim.resume()
            continue
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_q:
            break
        elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: # LEFT=1
            if sim.paused:
                if ball.rect.collidepoint(event.pos):
                    ball.picked = True
        elif event.type == pygame.MOUSEMOTION:
            if ball.picked:
                x, y = util.from_screen(event.pos[0], event.pos[1], win_width, win_height)
                sim.set_state(np.array([x, y, 0, 0], dtype='float32'))
        elif event.type == pygame.MOUSEBUTTONUP and event.button == 1:
            if ball.picked:
                ball.picked = False
                sim.set_state(np.array([x, y, 0, 0], dtype='float32'))
        else:
            pass
        # clear the background, and draw the sprites
        screen.fill(util.WHITE)
        my_group.update()
        my_group.draw(screen)
        text.draw("Time = %f" % sim.cur_time, screen, (10,10))
        text.draw("x = %f" % sim.state[0], screen, (10,40))
        text.draw("y = %f" % sim.state[1], screen, (10,70))
        if ball.picked:
            text.draw("Picked. (Simulation disabled)", screen, (10,100))
        pygame.display.flip()
        # update simulation
        if not sim.paused:
            sim.step()
        elif not ball.picked and event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                sim.step()
        else:
            pass
    
    pygame.quit()
    sys.exit(0)
if __name__ == '__main__':
    main()Starter code for your simulation class.
"""
author: Faisal Qureshi
email: faisal.qureshi@uoit.ca
website: http://www.vclab.ca
license: BSD
"""
import numpy as np
class Simulation:
    def __init__(self, title):
        self.paused = True # starting in paused mode
        self.title = title
        self.cur_time = 0
        self.dt = 0.033 # 33 millisecond, which corresponds to 30 fps
        # Fix this
    def init(self, state, mass, k, l):
        # Fix this
        pass
    def set_state(self, state):
        # Fix this
        pass
    def set_time(self, cur_time=0):
        self.cur_time = cur_time
    def set_dt(self, dt=0.033):
        self.dt = dt
    def step(self):
        # Fix this 
        pass
    def pause(self):
        self.paused = True
    def resume(self):
        self.paused = False
    def save(self, filename):
        # Ignore this
        pass
    def load(self, filename):
        # Ignore this
        passWe will run your code as follows:
$ python mass-spring-2d.pyNothing to submit. Please show your work to the instructor. Due in class.