Skip to content

Commit

Permalink
Merge pull request 3b1b#2106 from 3b1b/video-work
Browse files Browse the repository at this point in the history
Video work
  • Loading branch information
3b1b authored Mar 7, 2024
2 parents 2c11079 + 226d649 commit 4729e44
Show file tree
Hide file tree
Showing 21 changed files with 627 additions and 400 deletions.
6 changes: 3 additions & 3 deletions example_scenes.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def construct(self):

# This ensures that the method deicmal.next_to(square)
# is called on every frame
always(label.next_to, brace, UP)
label.always.next_to(brace, UP)
# You could also write the following equivalent line
# label.add_updater(lambda m: m.next_to(brace, UP))

Expand All @@ -302,7 +302,7 @@ def construct(self):
# should be functions returning arguments to that method.
# The following line ensures thst decimal.set_value(square.get_y())
# is called every frame
f_always(number.set_value, square.get_width)
number.f_always.set_value(square.get_width)
# You could also write the following equivalent line
# number.add_updater(lambda m: m.set_value(square.get_width()))

Expand Down Expand Up @@ -359,7 +359,7 @@ def construct(self):
# Alternatively, you can specify configuration for just one
# of them, like this.
y_axis_config=dict(
numbers_with_elongated_ticks=[-2, 2],
big_tick_numbers=[-2, 2],
)
)
# Keyword arguments of add_coordinate_labels can be used to
Expand Down
9 changes: 6 additions & 3 deletions manimlib/animation/composition.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
from manimlib.utils.iterables import remove_list_redundancies
from manimlib.utils.simple_functions import clip

from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Union, Iterable
AnimationType = Union[Animation, _AnimationBuilder]

if TYPE_CHECKING:
from typing import Callable, Optional
Expand All @@ -26,14 +27,16 @@


class AnimationGroup(Animation):
def __init__(self,
*animations: Animation | _AnimationBuilder,
def __init__(
self,
*args: AnimationType | Iterable[AnimationType],
run_time: float = -1, # If negative, default to sum of inputed animation runtimes
lag_ratio: float = 0.0,
group: Optional[Mobject] = None,
group_type: Optional[type] = None,
**kwargs
):
animations = args[0] if isinstance(args[0], Iterable) else args
self.animations = [prepare_animation(anim) for anim in animations]
self.build_animations_with_timings(lag_ratio)
self.max_end_time = max((awt[2] for awt in self.anims_with_timings), default=0)
Expand Down
5 changes: 3 additions & 2 deletions manimlib/animation/numbers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from manimlib.animation.animation import Animation
from manimlib.mobject.numbers import DecimalNumber
from manimlib.utils.bezier import interpolate
from manimlib.utils.simple_functions import clip

from typing import TYPE_CHECKING

Expand Down Expand Up @@ -55,9 +56,9 @@ def __init__(
source_number: float | complex = 0,
**kwargs
):
start_number = decimal_mob.number
start_number = decimal_mob.get_value()
super().__init__(
decimal_mob,
lambda a: interpolate(source_number, start_number, a),
lambda a: interpolate(source_number, start_number, clip(a, 0, 1)),
**kwargs
)
2 changes: 1 addition & 1 deletion manimlib/animation/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def begin(self) -> None:
self.target_copy = self.target_mobject.copy()
self.mobject.align_data_and_family(self.target_copy)
super().begin()
if not self.mobject.has_updaters:
if not self.mobject.has_updaters():
self.mobject.lock_matching_data(
self.starting_mobject,
self.target_copy,
Expand Down
45 changes: 37 additions & 8 deletions manimlib/camera/camera_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(
center_point: Vect3 = ORIGIN,
# Field of view in the y direction
fovy: float = 45 * DEGREES,
euler_axes: str = "zxz",
**kwargs,
):
super().__init__(**kwargs)
Expand All @@ -35,6 +36,7 @@ def __init__(
self.default_orientation = Rotation.identity()
self.view_matrix = np.identity(4)
self.camera_location = OUT # This will be updated by set_points
self.euler_axes = euler_axes

self.set_points(np.array([ORIGIN, LEFT, RIGHT, DOWN, UP]))
self.set_width(frame_shape[0], stretch=True)
Expand Down Expand Up @@ -62,7 +64,7 @@ def get_euler_angles(self) -> np.ndarray:
orientation = self.get_orientation()
if all(orientation.as_quat() == [0, 0, 0, 1]):
return np.zeros(3)
return orientation.as_euler("zxz")[::-1]
return orientation.as_euler(self.euler_axes)[::-1]

def get_theta(self):
return self.get_euler_angles()[0]
Expand Down Expand Up @@ -126,21 +128,44 @@ def set_euler_angles(
if all(eulers == 0):
rot = Rotation.identity()
else:
rot = Rotation.from_euler("zxz", eulers[::-1])
rot = Rotation.from_euler(self.euler_axes, eulers[::-1])
self.set_orientation(rot)
return self

def increment_euler_angles(
self,
dtheta: float | None = None,
dphi: float | None = None,
dgamma: float | None = None,
units: float = RADIANS
):
angles = self.get_euler_angles()
for i, value in enumerate([dtheta, dphi, dgamma]):
if value is not None:
angles[i] += value * units
self.set_euler_angles(*angles)
return self

def set_euler_axes(self, seq: str):
self.euler_axes = seq

def reorient(
self,
theta_degrees: float | None = None,
phi_degrees: float | None = None,
gamma_degrees: float | None = None,
center: Vect3 | tuple[float, float, float] | None = None,
height: float | None = None
):
"""
Shortcut for set_euler_angles, defaulting to taking
in angles in degrees
"""
self.set_euler_angles(theta_degrees, phi_degrees, gamma_degrees, units=DEGREES)
if center is not None:
self.move_to(np.array(center))
if height is not None:
self.set_height(height)
return self

def set_theta(self, theta: float):
Expand All @@ -152,16 +177,20 @@ def set_phi(self, phi: float):
def set_gamma(self, gamma: float):
return self.set_euler_angles(gamma=gamma)

def increment_theta(self, dtheta: float):
self.rotate(dtheta, OUT)
def increment_theta(self, dtheta: float, units=RADIANS):
self.increment_euler_angles(dtheta=dtheta, units=units)
return self

def increment_phi(self, dphi: float, units=RADIANS):
self.increment_euler_angles(dphi=dphi, units=units)
return self

def increment_phi(self, dphi: float):
self.rotate(dphi, self.get_inverse_camera_rotation_matrix()[0])
def increment_gamma(self, dgamma: float, units=RADIANS):
self.increment_euler_angles(dgamma=dgamma, units=units)
return self

def increment_gamma(self, dgamma: float):
self.rotate(dgamma, self.get_inverse_camera_rotation_matrix()[2])
def add_ambient_rotation(self, angular_speed=1 * DEGREES):
self.add_updater(lambda m, dt: m.increment_theta(angular_speed * dt))
return self

@Mobject.affects_data
Expand Down
Loading

0 comments on commit 4729e44

Please sign in to comment.