1
1
from __future__ import annotations
2
2
3
3
import numpy as np
4
- from scipy .integrate import odeint
5
4
6
5
from typing import TYPE_CHECKING
7
6
8
7
from manimlib .mobject .mobject import Mobject
9
8
from manimlib .scene .scene import Scene
10
9
from manimlib .animation .animation import Animation
11
10
from manimlib .physics .physical_system import PhysicalSystem
11
+ from manimlib .physics .integrator import symplectic_euler
12
12
from manimlib .constants import DIMENSIONS , DEFAULT_FPS
13
13
14
14
if TYPE_CHECKING :
@@ -26,13 +26,11 @@ def __init__(
26
26
mobject : PhysicalSystem ,
27
27
integrator : Callable [
28
28
[
29
- Callable [[np .ndarray , float , PhysicalSystem ], np .ndarray ],
30
- np .ndarray ,
31
- np .ndarray ,
32
- tuple
29
+ PhysicalSystem ,
30
+ np .ndarray
33
31
],
34
- np .ndarray
35
- ]= odeint ,
32
+ tuple [ np .ndarray , np . ndarray ]
33
+ ]= symplectic_euler ,
36
34
t : np .ndarray = np .linspace (0 , 10 , DEFAULT_FPS * 100 ),
37
35
scene : Scene = None ,
38
36
background_mobjects : tuple [Mobject ]= (),
@@ -44,26 +42,18 @@ def __init__(
44
42
45
43
Keyword arguments
46
44
-----------------
47
- integrator (Callable[[], np.ndarray]):
48
45
mobject (PhysicalSystem): the PhysicalSystem mobject
49
46
integrator (Callable[
50
47
[
51
- Callable[[np.ndarray, float, PhysicalSystem], np.ndarray],
52
- np.ndarray,
53
- np.ndarray,
54
- tuple
48
+ PhysicalSystem,
49
+ np.ndarray
55
50
],
56
- np.ndarray
57
- ]): function in terms of (dydt, y0, t, args) returning a matrix where each row
58
- is the state at every point in the t vector.
59
- dydt (provided by this class) is a function in terms of the state y0 at a
60
- certain time and the physical system (coming from args[0]), returning the
61
- derivative (vector) of the state with respect to time.
62
- y0 is the initial state (vector).
63
- t is the vector of time-points in which to get the state.
64
- args is a tuple containing any extra arguments dydt may need.
65
- For an example of the 'integrator' function, see odeint in scipy.integrate, which
66
- is the default value.
51
+ tuple[np.ndarray, np.ndarray]
52
+ ]): function in terms of (system, t) returning two tensors (pos, vel) where
53
+ pos[i,j,k] represents the component along the k-th axis of the position of
54
+ body j at time-point i (same holds por vel).
55
+ For an example of the 'integrator' function, see symplectic_euler in
56
+ manimlib.physics.integrator, which is the default value.
67
57
t (np.ndarray[N]): vector containing all time-points of integration, must be monotonic
68
58
(default np.linspace(0, 10, DEFAULT_FPS*100))
69
59
scene (Scene): scene is needed if we want to keep the body mobject, force mobject,
@@ -89,73 +79,19 @@ def __init__(
89
79
self .scene : Scene = scene
90
80
self .background_mobjects : tuple [Mobject ] = background_mobjects
91
81
self .foreground_mobjects : tuple [Mobject ] = foreground_mobjects
92
- # Assemble initial state (positions and velocities)
93
- y0 : np .ndarray = np .concatenate (
94
- [
95
- body .position for body in mobject .bodies
96
- ] + [
97
- body .velocity for body in mobject .bodies
98
- ]
99
- )
100
- # Integrate (single element tuples must have a comma at the end)
101
- self .y : np .ndarray = integrator (self .dydt , y0 , t , (self .mobject ,))
82
+ # Integrate the system
83
+ self .positions , self .velocities = integrator (self .mobject , t )
102
84
103
85
def interpolate_mobject (self , alpha : float ) -> None :
104
86
row_index : int = min (
105
- int (np .floor (alpha * self .y .shape [0 ])),
106
- self .y .shape [0 ]- 1
87
+ int (np .floor (alpha * self .positions .shape [0 ])),
88
+ self .positions .shape [0 ]- 1
107
89
)
108
- state : np .ndarray = self .y [row_index ,:]
109
- for i , body in enumerate (self .mobject .bodies ):
110
- pos = state [i * DIMENSIONS :(i + 1 )* DIMENSIONS ]
111
- vel = state [(i + self .n_bodies )* DIMENSIONS :(i + self .n_bodies + 1 )* DIMENSIONS ]
112
- body .set_velocity (vel )
113
- body .set_position (pos , update_mobject_position = False ) # Will update mobject later
90
+ self .mobject .update_positions (self .positions [row_index ])
91
+ self .mobject .update_velocities (self .velocities [row_index ])
114
92
# Update mobjects in the system
115
93
self .mobject .update_mobjects (
116
94
self .scene ,
117
95
self .background_mobjects ,
118
96
self .foreground_mobjects
119
97
)
120
-
121
- @staticmethod
122
- def dydt (
123
- y : np .ndarray ,
124
- t : float ,
125
- system : PhysicalSystem
126
- ) -> np .ndarray :
127
- """
128
- Compute the derivative of the state a physical system
129
-
130
- Keyword arguments
131
- -----------------
132
- y (np.ndarray[nbodies*2*DIMENSIONS]): state containing
133
- the position of each body and then its velocity
134
- t (float): current time
135
- system (PhysicalSystem): the physical system instance
136
-
137
- Returns
138
- -----------------
139
- The derivative of the state vector
140
- (np.ndarray[nbodies*2*DIMENSIONS]) containing the
141
- velocities and then accelerations
142
- """
143
- n_bodies : int = system .get_n_bodies ()
144
- masses : np .ndarray = system .get_masses ()
145
- # Split info from state vector
146
- velocities : np .ndarray = y [DIMENSIONS * n_bodies :].reshape ((n_bodies , DIMENSIONS ))
147
- # Reshape positions so that every row contains the location of that mass
148
- positions : np .ndarray = y [:DIMENSIONS * n_bodies ].reshape ((n_bodies , DIMENSIONS ))
149
- # Update the positions and velocities of the bodies in the system
150
- system .update_positions (positions )
151
- system .update_velocities (velocities )
152
- # Create structure for forces
153
- forces : np .ndarray = np .zeros ((n_bodies , DIMENSIONS ))
154
- # Apply the forces in the system
155
- for force in system .forces :
156
- force .apply (forces )
157
- print (f"{ forces } \n " )
158
- # Get the accelerations (a=F/m)
159
- accelerations : np .ndarray = forces / masses .reshape ((n_bodies , 1 ))
160
- # Return derivative of the state
161
- return np .concatenate ((velocities .flatten (), accelerations .flatten ()))
0 commit comments