Skip to content

Commit

Permalink
Implemented ParticleManager class + program now instantiates a random…
Browse files Browse the repository at this point in the history
… number of particles with random velocities, positions sizes and masses
  • Loading branch information
Jason-B-Jiang committed Jan 21, 2022
1 parent 76bbd28 commit 9d80da7
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 48 deletions.
31 changes: 16 additions & 15 deletions Particle.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,31 @@
import numpy as np
from typing import Tuple, List

MASS = 1
RADIUS = 15
MIN_DIST = RADIUS // 2 # minimul allowed pixels between particles, and between
# particles and container edges
COLOR = (0, 0, 255) # default color of blue for particles
X_LIMITS = [75, 525]
Y_LIMITS = [75, 525]
X_LIMITS = (75, 525)
Y_LIMITS = (75, 525)

# minimum allowed pixels between particles, and between particles and container
# edges
MIN_DIST = 2

class Particle:
def __init__(self, vel: np.array, position: np.array, r: int = RADIUS,
mass: int = MASS, color: Tuple[int] = COLOR):
def __init__(self, vel: np.array, position: np.array, r: int, mass: int,
color: Tuple[int]):
self.vel = vel
self.pos = position # centre of the particle
self.r = r # radius around the centre
self.mass = mass
self.color = color

# keep track of particles this particle has already had collisions
# accounted for with
# this is a temporary hacky workaround until I find something better
# accounted for with in this frame
# ensures that each interparticle collision is only simulated once
# per frame
self._collided_with = []

def update_position(self, other_particles: List[Particle]) -> None:
# reset list of particles this particle has collided with
# reset list of particles this particle has collided with, as we are
# in a new frame
self._collided_with = []

# check for collision with other particles and update velocity
Expand Down Expand Up @@ -59,7 +60,7 @@ def _check_wall_collision(self) -> bool:
"""Checks if the particle has collided with any of the container walls,
and adjusts the particle's velocity to collide elastically if so.
A collision counts as the particle being within 5 pixels of a wall,
A collision counts as the particle being within 2 pixels of a wall,
including the radius of the particle.
"""
# check for collision with left/right container borders
Expand Down Expand Up @@ -149,7 +150,7 @@ def _check_go_past_wall(self) -> None:
if dx_left_future <= 0 or dx_right_future <= 0:
# particle's velocity is perpendicular to left/right border, so
# reverse its velocity
print('predicted collided with side')
print('predicted collision with side')
if np.dot(self.vel, np.array([0, 1])) == 0:
self.vel = -self.vel
print('predicted head-on collision with side')
Expand All @@ -160,7 +161,7 @@ def _check_go_past_wall(self) -> None:

elif dy_top_future <= 0 or dy_bottom_future <= 0:
# particle velocity perpendicular to top/bottom border
print('predicted collided with top/bottom')
print('predicted collision with top/bottom')
if np.dot(self.vel, np.array([1, 0])) == 0:
self.vel = -self.vel
print('predicted head-on collision with top/bottom')
Expand Down
69 changes: 69 additions & 0 deletions ParticleManager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import random
from Particle import Particle
from typing import List, Tuple

# Minimum and maximum speeds, masses and radii for simulated particles
MIN_SPEED = 2
MAX_SPEED = 10
MIN_MASS = 12
MAX_MASS = 20
MIN_RADIUS = 12
MAX_RADIUS = 20

# Minimum and maximum values for both x and y coordinates of a particle, defined
# by the square holding the particles
MIN_POS = 75 + 25
MAX_POS = 525 - 25

# default color of blue for particles
COLOR = (0, 0, 255)

################################################################################

class ParticleManager:
"""Stores simulated particles, and controls adding/removing particles from
the screen.
"""
def __init__(self):
self.particles = []

def simulate_n_particles(self, n: int) -> None:
"""Initialize n particles of random velocities, positions, sizes and
mass, storing the particles in self.particles.
"""
for _ in range(n):
rand_vel = np.random.uniform(low = MIN_SPEED, high = MAX_SPEED,
size = (2,))
rand_pos = np.random.uniform(low = MIN_POS, high = MAX_POS,
size = (2,))
rand_radius = random.randint(MIN_RADIUS, MAX_RADIUS)
rand_mass = random.randint(MIN_MASS, MAX_MASS)

rand_particle = Particle(rand_vel, rand_pos, rand_radius, rand_mass,
COLOR)

self.particles.append(rand_particle)

def update_particles(self) -> None:
"""Update velocities and positions for all particles in self.particles
in a single frame, based on particle velocities and collisions.
"""
for p in self.particles:
p.update_position([par for par in self.particles if par != p])

def get_updated_particle_info(self) -> List[Tuple]:
"""Return a list of tuples, where each tuple contains the new color
(scaled by the particle's speed), position and radius for a particle.
"""
particle_tuples = []
for p in self.particles:
new_col = (np.clip(20 * np.linalg.norm(p.vel), 0, 255), 0, 255)
new_pos = p.get_position()
r = p.r

particle_tuples.append((new_col, new_pos, r))

return particle_tuples
52 changes: 19 additions & 33 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygame
import numpy as np
from typing import List
from Particle import Particle
import random
from ParticleManager import ParticleManager
pygame.init()

################################################################################

# Define constants

# Define game window
WIDTH = 600
WIDTH = 800
HEIGHT = 600
WINDOW = pygame.display.set_mode((WIDTH, HEIGHT)) # set main window
pygame.display.set_caption("Many body interactions!") # set window name
Expand All @@ -27,64 +26,51 @@
}

# Simulation constants
NUM_PARTICLES = 25
MAX_PARTICLES = 30
FPS = 60

################################################################################

# Functions
def draw_window(particles):
def draw_window(manager: ParticleManager):
# create white background
WINDOW.fill((255, 255, 255))

# create particle container
pygame.draw.rect(WINDOW,
(0, 0, 0),
pygame.Rect((0.25 * WIDTH) // 2,
(0.25 * HEIGHT) // 2, 0.75 * WIDTH, 0.75 * HEIGHT),
(75, 75, 450, 450),
2)

draw_particles(particles) # update all onscreen particles
pygame.display.update() # refresh the display
# update particle velocities and positions
manager.update_particles()

# update positions and colors of all onscreen particles
new_particle_info = manager.get_updated_particle_info()
[pygame.draw.circle(WINDOW, p[0], p[1], p[2]) for p in new_particle_info]

def simulate_n_particles(n: int) -> list:
"""Simulate n particles of uniform size and mass, with random initial
velocities and starting positions.
"""
particles = []
for i in range(n):
rand_vel = np.random.uniform(low = 1, high = 10, size = (2,))
rand_pos = np.random.uniform(low = 75 + 25, high = 525 - 25, size = (2,))
particles.append(Particle(rand_vel, rand_pos))
# refresh the display
pygame.display.update()

return particles


def draw_particles(particles) -> None:
# update all particle velocities and positions, based on collisions with
# walls and othr particles
for p in particles:
p.update_position([par for par in particles if par != p])

# draw new particle positions on screen based on their new velocities
[pygame.draw.circle(WINDOW, (np.clip(20 * np.linalg.norm(p.vel), 0, 255), 0, 255),
p.get_position(), p.r) for p in particles]

def main():
run = True
clock = pygame.time.Clock()
manager = ParticleManager()

particles = simulate_n_particles(NUM_PARTICLES)
# Initially have n random particles simulated on the screen, from 1 to
# MAX_PARTICLES
manager.simulate_n_particles(random.randint(1, MAX_PARTICLES))

while run:
clock.tick(FPS) # run while loop 60x per second consistently

for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pygame.quit()

draw_window(particles)
draw_window(manager)

main() # recursively re-run the game loop

Expand Down

0 comments on commit 9d80da7

Please sign in to comment.