-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimulation.py
More file actions
151 lines (117 loc) · 4.56 KB
/
simulation.py
File metadata and controls
151 lines (117 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import random
from pyglm import glm
from body import Body
from spatial_grid import SpatialGrid
class Simulation:
"""
Simulation class for managing a collection of bodies and their interactions.
This class manages the setup and update of a physics simulation containing
multiple bodies. It handles body initialization, position tracking,
collision detection, and resolution.
"""
def __init__(self, N: int, aspect_ratio: float = 1.0) -> None:
"""
Initialize simulation with N bodies.
Args:
N: Number of bodies.
aspect_ratio: Display aspect ratio. Defaults to 1.0.
"""
self._N = N
# Internal lists to hold bodies and their properties
self._bodies = []
self._positions = []
self._sizes = []
self._colors = []
# Simulation properties from GUI
self._gravity_vector = glm.vec2(0.0, 0.0)
self._color_based_on_speed = True
self._aspect_ratio = aspect_ratio
self._color_data = [0.01, 0.4, [0, 0, 1], [1, 0, 0]]
# Parameters for spatial grid
self._max_radius, self._min_radius = Body.calculate_size_range(N)
self._grid = SpatialGrid(2 * self._max_radius)
@property
def positions(self) -> list[glm.vec2]:
return self._positions
@property
def sizes(self) -> list[float]:
return self._sizes
@property
def colors(self) -> list[glm.vec3]:
return self._colors
@property
def gravity_vector(self) -> glm.vec2:
return self._gravity_vector
@gravity_vector.setter
def gravity_vector(self, new: glm.vec2) -> glm.vec2:
self._gravity_vector = new
@property
def color_based_on_speed(self) -> bool:
return self._color_based_on_speed
@color_based_on_speed.setter
def color_based_on_speed(self, new: bool) -> None:
self._color_based_on_speed = new
@property
def aspect_ratio(self) -> float:
return self._aspect_ratio
@aspect_ratio.setter
def aspect_ratio(self, new: float) -> float:
self._aspect_ratio = new
@property
def color_data(self) -> list:
return self._color_data
@color_data.setter
def color_data(self, data: list) -> None:
self._color_data = data
def setup_simulation(self) -> None:
"""Initialize the simulation with N bodies with random properties"""
for _ in range(self._N):
pos = glm.vec2(
random.uniform(-0.9, 0.9) * self._aspect_ratio,
random.uniform(-0.9, 0.9),
)
vel = glm.vec2(random.uniform(-0.2, 0.2), random.uniform(-0.2, 0.2))
size = random.uniform(self._min_radius, self._max_radius)
self._sizes.append(size)
mass = random.uniform(self._max_radius, self._max_radius * 2)
color = glm.vec3(
random.uniform(0.3, 1), random.uniform(0.3, 1), random.uniform(0.3, 1)
)
body = Body(pos, vel, size, mass, color, self._aspect_ratio)
self._bodies.append(body)
def update_bodies(self, dT: float, body: Body) -> None:
"""Update individual body properties such as position, color, and acceleration"""
if self.color_based_on_speed:
body.set_color_based_on_speed(self._color_data)
else:
body.reset_color()
body.update(dT)
body.acc = self._gravity_vector
body.aspect_ratio = self._aspect_ratio
def update(self, dT: float) -> None:
"""Update the simulation state by handling collisions and updating body positions"""
for body in self._bodies:
self._grid.insert(body)
self.update_bodies(dT, body)
checked_pairs = set()
for body in self._bodies:
nearby = self._grid.get_nearby(body)
for other in nearby:
if body is other:
continue
pair = tuple(sorted([id(body), id(other)]))
if pair in checked_pairs:
continue
checked_pairs.add(pair)
if Body.is_colliding(body, other):
Body.resolve_collision(body, other)
self._grid.clear()
self._positions = [body.pos for body in self._bodies]
self._colors = [body.visibleColor for body in self._bodies]
def reset(self) -> None:
"""Reset the simulation to its initial state"""
self._bodies.clear()
self._positions.clear()
self._sizes.clear()
self._colors.clear()
self.setup_simulation()