From: Skullheadx Date: Thu, 26 Jan 2023 20:50:12 +0000 (-0500) Subject: particle collisions #1 X-Git-Url: http://git.skullheadx.com/nixos/static/tech/index.html?a=commitdiff_plain;h=a91dda2c1d54c731bdf9f3cadbadf24d5400f6be;p=Collision-Simulation.git particle collisions #1 --- diff --git a/box.py b/box.py index 2e14630..d43b2bb 100644 --- a/box.py +++ b/box.py @@ -20,4 +20,4 @@ class Box: pass def draw(self, surf): - pygame.draw.rect(surf, BLACK, pygame.Rect(self.left, self.top, self.width, self.height), self.line_thickness) + pygame.draw.rect(surf, GRAY, pygame.Rect(self.left, self.top, self.width, self.height), self.line_thickness) diff --git a/collision.py b/collision.py index 492a576..d35547a 100644 --- a/collision.py +++ b/collision.py @@ -1,4 +1,5 @@ from pygame import Vector2 +from itertools import combinations def handleBoxCollision(particle, box): # Discrete Collision Detection @@ -36,3 +37,50 @@ def handleParticleCollision(particle1, particle2): # https://www.vobarian.com/c particle1.velocity = v1_prime particle2.velocity = v2_prime + + +def sweepAndPrune(particle_list): + particles = particle_list.copy() + + particles.sort(key=lambda x: x.position.x) + x_checks = [] + active = [particles[0]] + for particle in particles: + if particle == active[0]: + continue + + start_x = active[-1].position.x - active[-1].radius + end_x = active[-1].position.x + active[-1].radius + if (start_x <= particle.position.x - particle.radius <= end_x or + start_x <= particle.position.x <= end_x or + start_x <= particle.position.x + particle.radius <= end_x): + active.append(particle) + if len(active) > 1: + x_checks.extend(tuple(combinations(active, 2))) + active = [particle] + + particles.sort(key=lambda x: x.position.y) + y_checks = [] + active = [particles[0]] + for particle in particles: + if particle == active[0]: + continue + start_y = active[-1].position.y - active[-1].radius + end_y = active[-1].position.y + active[-1].radius + if (start_y <= particle.position.y - particle.radius <= end_y or + start_y <= particle.position.y <= end_y or + start_y <= particle.position.y + particle.radius <= end_y): + active.append(particle) + if len(active) > 1: + y_checks.extend(tuple(combinations(active, 2))) + active = [particle] + + return remove_duplicates(x_checks, y_checks) + + +def intersection(arr1, arr2): + return [value for value in arr1 if value in set(arr2)] + + +def remove_duplicates(arr1, arr2): + return list(set(arr1 + arr2)) diff --git a/display.py b/display.py index 4aed9b9..cf30ebf 100644 --- a/display.py +++ b/display.py @@ -4,6 +4,7 @@ import random from box import Box from colours import * from particle import Particle +from collision import sweepAndPrune, handleParticleCollision pygame.init() @@ -24,20 +25,25 @@ class Display: self.collision_objects = {layer: [] for layer in range(self.COLLISION_LAYERS)} self.particles = [] - n = 5 - for i in range(1, n): - for j in range(1, n): - m = random.randint(35, 45) + rows = 10 + cols = 10 + + w, h = self.WIDTH / cols, self.HEIGHT / rows + + for i in range(1, 1 + cols): + for j in range(1, 1 + rows): + r = random.randint(10, 30) speed = 0.1 - self.particles.append(Particle((self.WIDTH / n * i, self.HEIGHT / n * j), + self.particles.append(Particle((w * i - w / 2, h * j - h / 2), ((random.random() - 0.5) * speed, (random.random() - 0.5) * speed), - (0, 0), m, 100, self.collision_objects[0])) + (0, 0), r, r ** 2 * 3.14, self.collision_objects[0])) self.box = Box((0, 0), self.WIDTH, self.HEIGHT) - self.collision_objects[0] += self.particles self.collision_objects[0].append(self.box) + self.collided_last_frame = set() + def show(self): screen = pygame.display.set_mode(self.DIMENSIONS) clock = pygame.time.Clock() @@ -46,9 +52,9 @@ class Display: while self.is_running: self.update(delta) self.draw(screen) - pygame.display.flip() - delta = clock.tick(self.FPS) - print(delta) + pygame.display.update() + delta = clock.tick() + # print(delta) pygame.quit() def update(self, delta): @@ -61,10 +67,26 @@ class Display: for particle in self.particles: particle.update(delta) + for particle1, particle2 in sweepAndPrune(self.particles): + if (particle1, particle2) not in self.collided_last_frame and ( + particle2, particle1) not in self.collided_last_frame: + handleParticleCollision(particle1, particle2) + + self.collided_last_frame.add((particle1, particle2)) + self.collided_last_frame.add((particle2, particle1)) + + if particle1.position.distance_to(particle2.position) > particle1.radius + particle2.radius and \ + particle1 != particle2: + self.collided_last_frame.remove((particle1, particle2)) + self.collided_last_frame.remove((particle2, particle1)) + def draw(self, surf): - surf.fill(WHITE) + surf.fill(BLACK) self.box.draw(surf) for particle in self.particles: particle.draw(surf) + + for p1, p2 in sweepAndPrune(self.particles): + pygame.draw.line(surf, GREEN, p1.position, p2.position, 3) diff --git a/particle.py b/particle.py index 45961cc..8cb7390 100644 --- a/particle.py +++ b/particle.py @@ -24,6 +24,9 @@ class Particle: self.collision_layer = collision_layer + self.colour = RED + + def get_next_frame(self, position, velocity, delta): vel = pygame.Vector2() pos = pygame.Vector2() @@ -46,11 +49,7 @@ class Particle: self.top = self.position.y - self.radius self.bottom = self.position.y + self.radius - for thing in self.collision_layer: - if isinstance(thing, Box): - handleBoxCollision(self, thing) - elif isinstance(thing, Particle): - handleParticleCollision(self, thing) + handleBoxCollision(self, self.collision_layer[0]) def draw(self, surf): - pygame.draw.circle(surf, RED, self.position, self.radius) + pygame.draw.circle(surf, self.colour, self.position, self.radius)