-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathphysics.py
More file actions
119 lines (87 loc) · 3.71 KB
/
physics.py
File metadata and controls
119 lines (87 loc) · 3.71 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
import math
import scipy.constants
from collisions import find_first_collision
from objects import GameState
# How much a body is accelerated towards the body with mass 'm' at distance d
def gravity_acceleration(m: float, d: float) -> float:
# F = G * m1 * m2 / d^2,
# a = F / m1,
# a = G * m2 / d^2
return scipy.constants.G * m / max(scipy.constants.epsilon_0, d ** 2)
def apply_gravitational_forces(state: GameState) -> GameState:
if len(state.planets) < 2:
return state
new_state = state.copy()
for target_planet in new_state.planets:
if target_planet.fixed_position:
continue
forces = [target_planet.momentum]
for attracting_planet in new_state.planets:
if attracting_planet == target_planet:
continue
# HACKHACK: we multiply by time warp here to compensate for when we multiply
# the momentum by it in move_planets
gravity_magnitude = gravity_acceleration(
attracting_planet.mass(),
target_planet.distance_to(attracting_planet)
) * state.time_warp
forces.append(
(
gravity_magnitude * (target_planet.x - attracting_planet.x),
gravity_magnitude * (target_planet.y - attracting_planet.y)
)
)
total_force = (
sum([force[0] for force in forces]),
sum([force[1] for force in forces])
)
target_planet.momentum = total_force
return new_state
def move_planets(state: GameState) -> GameState:
new_state = state.with_no_planets()
for planet in state.planets:
moved_planet = planet.copy()
moved_planet.save_cur_pos_to_track()
moved_planet.x -= planet.momentum[0] * state.time_warp
moved_planet.y -= planet.momentum[1] * state.time_warp
new_state = new_state.with_append_planet(moved_planet)
return new_state
def check_collisions_absorb(state: GameState) -> GameState:
new_state = state.with_no_planets()
planets = state.planets.copy()
removed_planets = []
for planet in planets:
if planet in removed_planets:
continue
planets_to_consider = list(set(planets) - set(removed_planets) - {planet})
if len(planets_to_consider) == 0:
new_state = new_state.with_append_planet(planet)
continue
collision = find_first_collision(
planet,
planets_to_consider,
GameState.make_kdtree(planets_to_consider),
max(planets_to_consider, key=lambda p: p.radius).radius
)
if collision is None:
new_state = new_state.with_append_planet(planet)
continue
surviving_planet, dying_planet = (
(planet, collision)
if planet.mass() > collision.mass()
else (collision, planet)
)
dying_planet_mass_ratio = dying_planet.mass() / (surviving_planet.mass() + dying_planet.mass())
new_mass = surviving_planet.mass() + dying_planet.mass()
new_radius = math.sqrt(new_mass / surviving_planet.density / math.pi)
new_momentum = (
(0, 0) if surviving_planet.fixed_position else
(
surviving_planet.momentum[0] + (dying_planet.momentum[0] * dying_planet_mass_ratio),
surviving_planet.momentum[1] + (dying_planet.momentum[1] * dying_planet_mass_ratio)
)
)
surviving_with_absorbed = surviving_planet.copy(radius=new_radius, momentum=new_momentum)
new_state = new_state.with_append_planet(surviving_with_absorbed)
removed_planets.append(dying_planet)
return new_state