Skip to content

Commit b51a289

Browse files
committed
Add functioning particle system
1 parent 17b0ca3 commit b51a289

File tree

4 files changed

+109
-1
lines changed

4 files changed

+109
-1
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/Export/
2+
!/Export/example.gif

Export/example.gif

2.44 MB
Loading

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
# SimpleParticles
1+
# SimpleParticles
2+
3+
Creates a simple particle system and periodically changes the force field to keep things interesting.
4+
5+
![Example](./Export/example.gif)

SimpleParticles.py

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
from scipy.ndimage import gaussian_filter
4+
5+
BOUNDS = 1024
6+
FILTER_SIGMA = 12
7+
FRAMES = 30
8+
#np.random.seed(2)
9+
10+
class SimpleParticles():
11+
''' Creates a grid filled with particles an a force field with gaussian blur applied to it
12+
'''
13+
def __init__(self, bounds, filter_sigma):
14+
self.particleForceX_unblurred = np.random.random(size=[bounds,bounds])
15+
self.particleForceX = gaussian_filter(self.particleForceX_unblurred, sigma=filter_sigma)
16+
self.particleForceY_unblurred = np.random.random(size=[bounds,bounds])
17+
self.particleForceY = gaussian_filter(self.particleForceY_unblurred, sigma=filter_sigma)
18+
self.particles = (np.random.random(size=[bounds,bounds]) - 0.4).round()
19+
20+
self.bounds = bounds
21+
self.filter_sigma = filter_sigma
22+
23+
def change_force_field(self):
24+
# for x, particleRow in enumerate(self.particleForceX_unblurred):
25+
# for y, particle in enumerate(particleRow):
26+
# particle = 1 - particle
27+
# self.particleForceX = gaussian_filter(self.particleForceX_unblurred, sigma=FILTER_SIGMA)
28+
# with np.nditer(self.particleForceY_unblurred, op_flags=['readwrite']) as it:
29+
# for y in it:
30+
# y[...] = 1 - y
31+
# self.particleForceY = gaussian_filter(self.particleForceY_unblurred, sigma=FILTER_SIGMA)
32+
self.particleForceX_unblurred = np.random.random(size=[BOUNDS,BOUNDS])
33+
self.particleForceX = gaussian_filter(self.particleForceX_unblurred, sigma=FILTER_SIGMA)
34+
self.particleForceY_unblurred = np.random.random(size=[BOUNDS,BOUNDS])
35+
self.particleForceY = gaussian_filter(self.particleForceY_unblurred, sigma=FILTER_SIGMA)
36+
37+
def move_particle(self, x, y, recursion=False):
38+
''' Moves a particle according to the force exhibited by the force field.
39+
If the destination is not free the function gets called recursively and "pushes" the particles forward along the force grid.
40+
particle values:
41+
* 1 -> normal value, not changed since last frame was rendered
42+
* 1.2 -> value once the particle has changed position
43+
* 1.3 -> value for loops within the system, no movement for this particles
44+
'''
45+
vector = self.get_force_vector(x, y)
46+
particle_dest = [x + vector[0], y + vector[1]]
47+
if particle_dest[0] > BOUNDS - 1:
48+
particle_dest[0] -= BOUNDS
49+
if particle_dest[1] > BOUNDS - 1:
50+
particle_dest[1] -= BOUNDS
51+
52+
particle_dest_value = self.particles[particle_dest[0], particle_dest[1]]
53+
54+
if particle_dest_value == 1.2:
55+
self.particles[x, y] = 1.2
56+
return False
57+
58+
if particle_dest_value == 1 or particle_dest_value == 1.3:
59+
self.particles[x, y] = 1.2
60+
if self.move_particle(particle_dest[0], particle_dest[1], True) is False:
61+
self.particles[x, y] = 1.2
62+
return False
63+
64+
self.particles[x, y] = 0
65+
self.particles[particle_dest[0], particle_dest[1]] = 1.2 if recursion else 1.3
66+
67+
def get_force_vector(self, x, y):
68+
''' Calculates the force vector based on the force grid.
69+
'''
70+
vector = [0, 0]
71+
if self.particleForceX[x, y] > 0.5:
72+
vector[0] = 1
73+
elif self.particleForceX[x, y] <= 0.5:
74+
vector[0] = -1
75+
if self.particleForceY[x, y] > 0.5:
76+
vector[1] = 1
77+
elif self.particleForceY[x, y] <= 0.5:
78+
vector[1] = -1
79+
return vector
80+
81+
def render(self, frames):
82+
''' Renders the simulation and exports it into "./Export/".
83+
'''
84+
for i in range(frames):
85+
for x, particleRow in enumerate(self.particles):
86+
for y, particle in enumerate(particleRow):
87+
if particle != 0 and particle != 1.3:
88+
self.move_particle(x, y)
89+
90+
self.particles = self.particles.round()
91+
plt.imshow(self.particles)
92+
ax = plt.gca()
93+
ax.get_yaxis().set_visible(False)
94+
ax.get_xaxis().set_visible(False)
95+
plt.savefig(fname=f"./Export/Frame-{i}")
96+
print(f"Done with Frame {i}")
97+
if i % 5 == 0:
98+
self.change_force_field()
99+
100+
if __name__ == "__main__":
101+
particles = SimpleParticles(BOUNDS, FILTER_SIGMA)
102+
particles.render(FRAMES)

0 commit comments

Comments
 (0)