Skip to content

Commit 132f792

Browse files
committed
NewtonGravitationalForce can have a line connecting the bodies as mobject
1 parent efb4c5b commit 132f792

File tree

4 files changed

+89
-32
lines changed

4 files changed

+89
-32
lines changed

example_scenes.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -743,15 +743,29 @@ def construct(self) -> None:
743743
tracer=tracing_lines[i]
744744
) for i in range(n_masses)
745745
]
746-
# forces: list[Force] = []
747-
# for i, body1 in enumerate(bodies):
748-
# for _, body2 in enumerate(bodies[i+1:],start=i+1):
749-
# forces.append(
750-
# NewtonGravitationalForce((body1, body2))
751-
# )
752-
# system: PhysicalSystem = PhysicalSystem(bodies, forces)
753-
system: GravitationalSystem = GravitationalSystem(bodies)
754-
system.fill_forces()
746+
force_colors: list[str] = [YELLOW, PURPLE, LIGHT_BROWN]
747+
k: int = 0
748+
forces: list[Force] = []
749+
for i, body1 in enumerate(bodies):
750+
for _, body2 in enumerate(bodies[i+1:],start=i+1):
751+
line = Line(
752+
body1.position,
753+
body2.position,
754+
stroke_color=force_colors[k],
755+
stroke_width=5
756+
)
757+
self.add(line)
758+
forces.append(
759+
NewtonGravitationalForce(
760+
(body1, body2),
761+
mobjects=(line,)
762+
)
763+
)
764+
k += 1
765+
system: PhysicalSystem = PhysicalSystem(bodies, forces)
766+
# system: GravitationalSystem = GravitationalSystem(bodies)
767+
# system.fill_forces()
768+
self.bring_to_front(*mass_circles) # put them on top of the force mobjects
755769

756770
# Wait a couple seconds
757771
self.wait(2)

manimlib/animation/physics.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,13 @@ def interpolate_mobject(self, alpha: float) -> None:
107107
body.set_position(pos, update_mobject_position=True)
108108
body.update_tracer()
109109
if body.tracer is not None:
110-
self.scene.bring_to_front(body.tracer)
110+
self.scene.bring_to_back(body.tracer)
111111
if body.mobj is not None:
112112
self.scene.bring_to_front(body.mobj)
113-
# # Handle background and foreground mobjects
113+
# Update mobjects in forces
114+
for force in self.mobject.forces:
115+
force.update_mobjects()
116+
# Handle background and foreground mobjects
114117
if self.background_mobjects:
115118
self.scene.bring_to_back(*self.background_mobjects)
116119
if self.foreground_mobjects:

manimlib/physics/force.py

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import numpy as np
66

77
from typing import Union, Callable
8+
from manimlib.mobject.mobject import Mobject
89

910
from manimlib.physics.body import Body
1011
from manimlib.mobject.geometry import Line
@@ -20,26 +21,35 @@ class Force(metaclass=ABCMeta):
2021
def __init__(
2122
self,
2223
bodies: tuple[Body, ...],
23-
line: Union[Line, Line3D]=None
24+
mobjects: tuple[Mobject, ...]=()
2425
) -> None:
2526
"""
2627
Initialize a new Force object
2728
2829
Keyword arguments
2930
-----------------
30-
bodies (list[Body]): the bodies to which the force applies
31-
line: (Line | Line3D): line representing the force and follows
32-
the bodies, must already be set to have the bodies at its
33-
extremes (default None)
31+
bodies (tuple[Body, ...]): the bodies to which the force applies
32+
mobjects: tuple[Mobject, ...]: mobject(s) representing the force, should
33+
be already set to a desired position (subclasses know
34+
how to update them). Regular shapes should be used for 2D
35+
simulations and 3D shapes for 3D simulations! Otherwise
36+
things MAY BREAK! (default: empty tuple)
3437
"""
35-
self.bodies: tuple[Body] = bodies
38+
self.bodies: tuple[Body, ...] = bodies
3639
if not self.bodies:
3740
raise Exception("No bodies have been provided!")
38-
self.line = line
41+
self.mobjects: tuple[Mobject, ...] = mobjects
3942

4043
def __str__(self) -> str:
4144
body_info: str = ','.join([f"body{i}_index={body.index}" for i, body in enumerate(self.bodies, start=1)])
42-
return (f"{body_info},line={self.line}")
45+
return (f"{body_info},mobjects={self.mobjects}")
46+
47+
def update_mobjects(self) -> None:
48+
"""
49+
Update the foce mobject(s)
50+
(to be implemented by each subclass)
51+
"""
52+
pass
4353

4454
@abstractmethod
4555
def apply(self, forces: np.ndarray) -> None:
@@ -63,30 +73,55 @@ class NewtonGravitationalForce(Force):
6373

6474
def __init__(
6575
self,
66-
bodies: tuple[Body],
67-
line: Union[Line, Line3D]=None,
76+
bodies: tuple[Body, Body],
77+
mobjects: tuple[Union[Line, Line3D]]=(),
6878
G: float=1.0
6979
) -> None:
7080
"""
7181
Initialize a new NewtonGravitationalForce instance
7282
7383
Keyword arguments
7484
-----------------
75-
bodies (list[Body]): the bodies to which the force applies
76-
line (Line | Line3D): line representing the force and follows
85+
bodies (tuple[Body, Body]): the bodies to which the force applies
86+
mobjects (tuple[Line | Line3D]): line representing the force and follows
7787
the bodies, must already be set to have the bodies at its
78-
extremes (default None)
88+
extremes (start=bodies[0], end=bodies[1]) (default: empty tuple)
7989
G (float): gravitational constant (default: 1.0)
8090
"""
8191
if len(bodies) != 2:
8292
raise Exception("You must provide 2 bodies!")
83-
super().__init__(bodies)
93+
super().__init__(bodies, mobjects)
8494
self.G: float = G
8595

8696
def __str__(self) -> str:
8797
return (f"{self.__class__.__name__}<{super().__str__()},"
8898
f"G={self.G}>")
8999

100+
def update_mobjects(self) -> None:
101+
"""
102+
Update the line mobject to follow the bodies
103+
"""
104+
if not self.mobjects or self.mobjects[0] is None:
105+
return
106+
line: Union[Line, Line3D] = self.mobjects[0]
107+
body1, body2 = self.bodies[0], self.bodies[1]
108+
delta_pos: np.ndarray = body2.position - body1.position
109+
distance: float = np.linalg.norm(delta_pos)
110+
if isinstance(line, Line): # for 2D simulations
111+
line.set_length(distance)
112+
line.set_angle(np.arctan2(delta_pos[1], delta_pos[0]))
113+
line.move_to((body1.position+body2.position)/2)
114+
elif isinstance(line, Line3D): # TODO: implement this
115+
print(
116+
f"({self.__class__.__name__}) WARNING: updating "
117+
"is not implemented yet."
118+
)
119+
else:
120+
print(
121+
f"({self.__class__.__name__}) WARNING: the mobject "
122+
"is not a line. Cannot update it."
123+
)
124+
90125
def apply(self, forces: np.ndarray) -> None:
91126
body1, body2 = self.bodies[0], self.bodies[1]
92127
delta_pos: np.ndarray = body2.position - body1.position

manimlib/physics/physical_system.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,16 @@ def __init__(self, bodies: list[Body]=[], forces: list[Force]=[], **kwargs):
2929
forces (list[Force]): forces to be added to the system (default: empty list)
3030
"""
3131
# Aggregate all submobjects and pass them to the superclass constructor
32-
mobjects: list[Mobject] = [
33-
body.mobj for body in bodies if body.mobj is not None
34-
] + [
35-
body.tracer for body in bodies if body.tracer is not None
36-
] + [
37-
force.line for force in forces if force.line is not None
38-
]
32+
mobjects: list[Mobject] = []
33+
for body in bodies:
34+
if body.mobj is not None:
35+
mobjects.append(body.mobj)
36+
if body.tracer is not None:
37+
mobjects.append(body.tracer)
38+
for force in forces:
39+
for mobj in force.mobjects:
40+
if mobj is not None:
41+
mobjects.append(mobj)
3942
super().__init__(*mobjects, **kwargs)
4043
self.bodies: list[Body] = bodies
4144
for i, body in enumerate(self.bodies): # set indices for the bodies
@@ -136,6 +139,8 @@ def fill_forces(self, **kwargs) -> None:
136139
"""
137140
Add a NewtonGravitationalForce to every pair of bodies
138141
TODO: add an option in 'kwargs' to automatically add line mobjects for the force
142+
(they will have to be added with the self.add() method as well, since this is
143+
a mobject itself too)
139144
140145
Keyword arguments
141146
-----------------

0 commit comments

Comments
 (0)