mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
commit
15e5d8a07a
15 changed files with 234 additions and 63 deletions
|
@ -4,6 +4,7 @@ from copy import deepcopy
|
||||||
|
|
||||||
from manimlib.mobject.mobject import _AnimationBuilder
|
from manimlib.mobject.mobject import _AnimationBuilder
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
from manimlib.utils.iterables import remove_list_redundancies
|
||||||
from manimlib.utils.rate_functions import smooth
|
from manimlib.utils.rate_functions import smooth
|
||||||
from manimlib.utils.rate_functions import squish_rate_func
|
from manimlib.utils.rate_functions import squish_rate_func
|
||||||
from manimlib.utils.simple_functions import clip
|
from manimlib.utils.simple_functions import clip
|
||||||
|
@ -37,7 +38,10 @@ class Animation(object):
|
||||||
remover: bool = False,
|
remover: bool = False,
|
||||||
# What to enter into the update function upon completion
|
# What to enter into the update function upon completion
|
||||||
final_alpha_value: float = 1.0,
|
final_alpha_value: float = 1.0,
|
||||||
suspend_mobject_updating: bool = True,
|
# If set to True, the mobject itself will have its internal updaters called,
|
||||||
|
# but the start or target mobjects would not be suspended. To completely suspend
|
||||||
|
# updating, call mobject.suspend_updating() before the animation
|
||||||
|
suspend_mobject_updating: bool = False,
|
||||||
):
|
):
|
||||||
self.mobject = mobject
|
self.mobject = mobject
|
||||||
self.run_time = run_time
|
self.run_time = run_time
|
||||||
|
@ -65,12 +69,6 @@ class Animation(object):
|
||||||
self.mobject.set_animating_status(True)
|
self.mobject.set_animating_status(True)
|
||||||
self.starting_mobject = self.create_starting_mobject()
|
self.starting_mobject = self.create_starting_mobject()
|
||||||
if self.suspend_mobject_updating:
|
if self.suspend_mobject_updating:
|
||||||
# All calls to self.mobject's internal updaters
|
|
||||||
# during the animation, either from this Animation
|
|
||||||
# or from the surrounding scene, should do nothing.
|
|
||||||
# It is, however, okay and desirable to call
|
|
||||||
# the internal updaters of self.starting_mobject,
|
|
||||||
# or any others among self.get_all_mobjects()
|
|
||||||
self.mobject_was_updating = not self.mobject.updating_suspended
|
self.mobject_was_updating = not self.mobject.updating_suspended
|
||||||
self.mobject.suspend_updating()
|
self.mobject.suspend_updating()
|
||||||
self.families = list(self.get_all_families_zipped())
|
self.families = list(self.get_all_families_zipped())
|
||||||
|
@ -105,23 +103,19 @@ class Animation(object):
|
||||||
def update_mobjects(self, dt: float) -> None:
|
def update_mobjects(self, dt: float) -> None:
|
||||||
"""
|
"""
|
||||||
Updates things like starting_mobject, and (for
|
Updates things like starting_mobject, and (for
|
||||||
Transforms) target_mobject. Note, since typically
|
Transforms) target_mobject.
|
||||||
(always?) self.mobject will have its updating
|
|
||||||
suspended during the animation, this will do
|
|
||||||
nothing to self.mobject.
|
|
||||||
"""
|
"""
|
||||||
for mob in self.get_all_mobjects_to_update():
|
for mob in self.get_all_mobjects_to_update():
|
||||||
mob.update(dt)
|
mob.update(dt)
|
||||||
|
|
||||||
def get_all_mobjects_to_update(self) -> list[Mobject]:
|
def get_all_mobjects_to_update(self) -> list[Mobject]:
|
||||||
# The surrounding scene typically handles
|
# The surrounding scene typically handles
|
||||||
# updating of self.mobject. Besides, in
|
# updating of self.mobject.
|
||||||
# most cases its updating is suspended anyway
|
|
||||||
items = list(filter(
|
items = list(filter(
|
||||||
lambda m: m is not self.mobject,
|
lambda m: m is not self.mobject,
|
||||||
self.get_all_mobjects()
|
self.get_all_mobjects()
|
||||||
))
|
))
|
||||||
items = list(set(items))
|
items = remove_list_redundancies(items)
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
|
|
|
@ -142,6 +142,7 @@ class Mobject(object):
|
||||||
self.uniforms: UniformDict = {
|
self.uniforms: UniformDict = {
|
||||||
"is_fixed_in_frame": 0.0,
|
"is_fixed_in_frame": 0.0,
|
||||||
"shading": np.array(self.shading, dtype=float),
|
"shading": np.array(self.shading, dtype=float),
|
||||||
|
"clip_plane": np.zeros(4),
|
||||||
}
|
}
|
||||||
|
|
||||||
def init_colors(self):
|
def init_colors(self):
|
||||||
|
@ -1946,6 +1947,21 @@ class Mobject(object):
|
||||||
mob.depth_test = False
|
mob.depth_test = False
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def set_clip_plane(
|
||||||
|
self,
|
||||||
|
vect: Vect3 | None = None,
|
||||||
|
threshold: float | None = None
|
||||||
|
) -> Self:
|
||||||
|
if vect is not None:
|
||||||
|
self.uniforms["clip_plane"][:3] = vect
|
||||||
|
if threshold is not None:
|
||||||
|
self.uniforms["clip_plane"][3] = threshold
|
||||||
|
return self
|
||||||
|
|
||||||
|
def deactivate_clip_plane(self) -> Self:
|
||||||
|
self.uniforms["clip_plane"][:] = 0
|
||||||
|
return self
|
||||||
|
|
||||||
# Shader code manipulation
|
# Shader code manipulation
|
||||||
|
|
||||||
@affects_data
|
@affects_data
|
||||||
|
|
|
@ -15,7 +15,6 @@ if TYPE_CHECKING:
|
||||||
from manimlib.typing import ManimColor, Vect3
|
from manimlib.typing import ManimColor, Vect3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BulletedList(VGroup):
|
class BulletedList(VGroup):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import moderngl
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from moderngl import TRIANGLES
|
|
||||||
|
|
||||||
from manimlib.constants import DL, DR, UL, UR
|
from manimlib.constants import DL, DR, UL, UR
|
||||||
from manimlib.mobject.mobject import Mobject
|
from manimlib.mobject.mobject import Mobject
|
||||||
|
@ -25,7 +25,7 @@ class ImageMobject(Mobject):
|
||||||
('im_coords', np.float32, (2,)),
|
('im_coords', np.float32, (2,)),
|
||||||
('opacity', np.float32, (1,)),
|
('opacity', np.float32, (1,)),
|
||||||
]
|
]
|
||||||
render_primitive: int = TRIANGLES
|
render_primitive: int = moderngl.TRIANGLES
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -65,10 +65,6 @@ class Surface(Mobject):
|
||||||
)
|
)
|
||||||
self.compute_triangle_indices()
|
self.compute_triangle_indices()
|
||||||
|
|
||||||
def init_uniforms(self):
|
|
||||||
super().init_uniforms()
|
|
||||||
self.uniforms["clip_plane"] = np.zeros(4)
|
|
||||||
|
|
||||||
def uv_func(self, u: float, v: float) -> tuple[float, float, float]:
|
def uv_func(self, u: float, v: float) -> tuple[float, float, float]:
|
||||||
# To be implemented in subclasses
|
# To be implemented in subclasses
|
||||||
return (u, v, 0.0)
|
return (u, v, 0.0)
|
||||||
|
@ -216,21 +212,6 @@ class Surface(Mobject):
|
||||||
self.add_updater(updater)
|
self.add_updater(updater)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_clip_plane(
|
|
||||||
self,
|
|
||||||
vect: Vect3 | None = None,
|
|
||||||
threshold: float | None = None
|
|
||||||
) -> Self:
|
|
||||||
if vect is not None:
|
|
||||||
self.uniforms["clip_plane"][:3] = vect
|
|
||||||
if threshold is not None:
|
|
||||||
self.uniforms["clip_plane"][3] = threshold
|
|
||||||
return self
|
|
||||||
|
|
||||||
def deactivate_clip_plane(self) -> Self:
|
|
||||||
self.uniforms["clip_plane"][:] = 0
|
|
||||||
return self
|
|
||||||
|
|
||||||
def get_shader_vert_indices(self) -> np.ndarray:
|
def get_shader_vert_indices(self) -> np.ndarray:
|
||||||
return self.get_triangle_indices()
|
return self.get_triangle_indices()
|
||||||
|
|
||||||
|
|
|
@ -767,6 +767,8 @@ class VMobject(Mobject):
|
||||||
def quick_point_from_proportion(self, alpha: float) -> Vect3:
|
def quick_point_from_proportion(self, alpha: float) -> Vect3:
|
||||||
# Assumes all curves have the same length, so is inaccurate
|
# Assumes all curves have the same length, so is inaccurate
|
||||||
num_curves = self.get_num_curves()
|
num_curves = self.get_num_curves()
|
||||||
|
if num_curves == 0:
|
||||||
|
return self.get_center()
|
||||||
n, residue = integer_interpolate(0, num_curves, alpha)
|
n, residue = integer_interpolate(0, num_curves, alpha)
|
||||||
curve_func = self.get_nth_curve_function(n)
|
curve_func = self.get_nth_curve_function(n)
|
||||||
return curve_func(residue)
|
return curve_func(residue)
|
||||||
|
|
|
@ -5,7 +5,8 @@ import itertools as it
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.constants import FRAME_HEIGHT, FRAME_WIDTH
|
from manimlib.constants import FRAME_HEIGHT, FRAME_WIDTH
|
||||||
from manimlib.constants import WHITE
|
from manimlib.constants import BLUE, WHITE
|
||||||
|
from manimlib.constants import ORIGIN
|
||||||
from manimlib.animation.indication import VShowPassingFlash
|
from manimlib.animation.indication import VShowPassingFlash
|
||||||
from manimlib.mobject.geometry import Arrow
|
from manimlib.mobject.geometry import Arrow
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
|
@ -15,6 +16,7 @@ from manimlib.utils.bezier import inverse_interpolate
|
||||||
from manimlib.utils.color import get_colormap_list
|
from manimlib.utils.color import get_colormap_list
|
||||||
from manimlib.utils.color import rgb_to_color
|
from manimlib.utils.color import rgb_to_color
|
||||||
from manimlib.utils.dict_ops import merge_dicts_recursively
|
from manimlib.utils.dict_ops import merge_dicts_recursively
|
||||||
|
from manimlib.utils.iterables import cartesian_product
|
||||||
from manimlib.utils.rate_functions import linear
|
from manimlib.utils.rate_functions import linear
|
||||||
from manimlib.utils.simple_functions import sigmoid
|
from manimlib.utils.simple_functions import sigmoid
|
||||||
from manimlib.utils.space_ops import get_norm
|
from manimlib.utils.space_ops import get_norm
|
||||||
|
@ -118,7 +120,184 @@ def get_sample_points_from_coordinate_system(
|
||||||
|
|
||||||
# Mobjects
|
# Mobjects
|
||||||
|
|
||||||
class VectorField(VGroup):
|
|
||||||
|
class VectorField(VMobject):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
func,
|
||||||
|
stroke_color: ManimColor = BLUE,
|
||||||
|
stroke_opacity: float = 1.0,
|
||||||
|
center: Vect3 = ORIGIN,
|
||||||
|
sample_points: Optional[Vect3Array] = None,
|
||||||
|
x_density: float = 2.0,
|
||||||
|
y_density: float = 2.0,
|
||||||
|
z_density: float = 2.0,
|
||||||
|
width: float = 14.0,
|
||||||
|
height: float = 8.0,
|
||||||
|
depth: float = 0.0,
|
||||||
|
stroke_width: float = 2,
|
||||||
|
tip_width_ratio: float = 4,
|
||||||
|
tip_len_to_width: float = 0.01,
|
||||||
|
max_vect_len: float | None = None,
|
||||||
|
min_drawn_norm: float = 1e-2,
|
||||||
|
flat_stroke: bool = False,
|
||||||
|
norm_to_opacity_func=None,
|
||||||
|
norm_to_rgb_func=None,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
self.func = func
|
||||||
|
self.stroke_width = stroke_width
|
||||||
|
self.tip_width_ratio = tip_width_ratio
|
||||||
|
self.tip_len_to_width = tip_len_to_width
|
||||||
|
self.min_drawn_norm = min_drawn_norm
|
||||||
|
self.norm_to_opacity_func = norm_to_opacity_func
|
||||||
|
self.norm_to_rgb_func = norm_to_rgb_func
|
||||||
|
|
||||||
|
if max_vect_len is not None:
|
||||||
|
self.max_vect_len = max_vect_len
|
||||||
|
else:
|
||||||
|
densities = np.array([x_density, y_density, z_density])
|
||||||
|
dims = np.array([width, height, depth])
|
||||||
|
self.max_vect_len = 1.0 / densities[dims > 0].mean()
|
||||||
|
|
||||||
|
if sample_points is None:
|
||||||
|
self.sample_points = self.get_sample_points(
|
||||||
|
center, width, height, depth,
|
||||||
|
x_density, y_density, z_density
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.sample_points = sample_points
|
||||||
|
|
||||||
|
self.init_base_stroke_width_array(len(self.sample_points))
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
stroke_color=stroke_color,
|
||||||
|
stroke_opacity=stroke_opacity,
|
||||||
|
flat_stroke=flat_stroke,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
n_samples = len(self.sample_points)
|
||||||
|
self.set_points(np.zeros((8 * n_samples - 1, 3)))
|
||||||
|
self.set_stroke(width=stroke_width)
|
||||||
|
self.set_joint_type('no_joint')
|
||||||
|
self.update_vectors()
|
||||||
|
|
||||||
|
def get_sample_points(
|
||||||
|
self,
|
||||||
|
center: np.ndarray,
|
||||||
|
width: float,
|
||||||
|
height: float,
|
||||||
|
depth: float,
|
||||||
|
x_density: float,
|
||||||
|
y_density: float,
|
||||||
|
z_density: float
|
||||||
|
) -> np.ndarray:
|
||||||
|
to_corner = np.array([width / 2, height / 2, depth / 2])
|
||||||
|
spacings = 1.0 / np.array([x_density, y_density, z_density])
|
||||||
|
to_corner = spacings * (to_corner / spacings).astype(int)
|
||||||
|
lower_corner = center - to_corner
|
||||||
|
upper_corner = center + to_corner + spacings
|
||||||
|
return cartesian_product(*(
|
||||||
|
np.arange(low, high, space)
|
||||||
|
for low, high, space in zip(lower_corner, upper_corner, spacings)
|
||||||
|
))
|
||||||
|
|
||||||
|
def init_base_stroke_width_array(self, n_sample_points):
|
||||||
|
arr = np.ones(8 * n_sample_points - 1)
|
||||||
|
arr[4::8] = self.tip_width_ratio
|
||||||
|
arr[5::8] = self.tip_width_ratio * 0.5
|
||||||
|
arr[6::8] = 0
|
||||||
|
arr[7::8] = 0
|
||||||
|
self.base_stroke_width_array = arr
|
||||||
|
|
||||||
|
def set_sample_points(self, sample_points: Vect3Array):
|
||||||
|
self.sample_points = sample_points
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_stroke(self, color=None, width=None, opacity=None, behind=None, flat=None, recurse=True):
|
||||||
|
super().set_stroke(color, None, opacity, behind, flat, recurse)
|
||||||
|
if width is not None:
|
||||||
|
self.set_stroke_width(float(width))
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_stroke_width(self, width: float):
|
||||||
|
if self.get_num_points() > 0:
|
||||||
|
self.get_stroke_widths()[:] = width * self.base_stroke_width_array
|
||||||
|
self.stroke_width = width
|
||||||
|
return self
|
||||||
|
|
||||||
|
def update_vectors(self):
|
||||||
|
tip_width = self.tip_width_ratio * self.stroke_width
|
||||||
|
tip_len = self.tip_len_to_width * tip_width
|
||||||
|
samples = self.sample_points
|
||||||
|
|
||||||
|
# Get raw outputs and lengths
|
||||||
|
outputs = self.func(samples)
|
||||||
|
norms = np.linalg.norm(outputs, axis=1)[:, np.newaxis]
|
||||||
|
|
||||||
|
# How long should the arrows be drawn?
|
||||||
|
max_len = self.max_vect_len
|
||||||
|
if max_len < np.inf:
|
||||||
|
drawn_norms = max_len * np.tanh(norms / max_len)
|
||||||
|
else:
|
||||||
|
drawn_norms = norms
|
||||||
|
|
||||||
|
# What's the distance from the base of an arrow to
|
||||||
|
# the base of its head?
|
||||||
|
dist_to_head_base = np.clip(drawn_norms - tip_len, 0, np.inf)
|
||||||
|
|
||||||
|
# Set all points
|
||||||
|
unit_outputs = np.zeros_like(outputs)
|
||||||
|
np.true_divide(outputs, norms, out=unit_outputs, where=(norms > self.min_drawn_norm))
|
||||||
|
|
||||||
|
points = self.get_points()
|
||||||
|
points[0::8] = samples
|
||||||
|
points[2::8] = samples + dist_to_head_base * unit_outputs
|
||||||
|
points[4::8] = points[2::8]
|
||||||
|
points[6::8] = samples + drawn_norms * unit_outputs
|
||||||
|
for i in (1, 3, 5):
|
||||||
|
points[i::8] = 0.5 * (points[i - 1::8] + points[i + 1::8])
|
||||||
|
points[7::8] = points[6:-1:8]
|
||||||
|
|
||||||
|
# Adjust stroke widths
|
||||||
|
width_arr = self.stroke_width * self.base_stroke_width_array
|
||||||
|
width_scalars = np.clip(drawn_norms / tip_len, 0, 1)
|
||||||
|
width_scalars = np.repeat(width_scalars, 8)[:-1]
|
||||||
|
self.get_stroke_widths()[:] = width_scalars * width_arr
|
||||||
|
|
||||||
|
# Potentially adjust opacity and color
|
||||||
|
if self.norm_to_opacity_func is not None:
|
||||||
|
self.get_stroke_opacities()[:] = self.norm_to_opacity_func(
|
||||||
|
np.repeat(norms, 8)[:-1]
|
||||||
|
)
|
||||||
|
if self.norm_to_rgb_func is not None:
|
||||||
|
self.get_stroke_colors()
|
||||||
|
self.data['stroke_rgba'][:, :3] = self.norm_to_rgb_func(
|
||||||
|
np.repeat(norms, 8)[:-1]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.note_changed_data()
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class TimeVaryingVectorField(VectorField):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
# Takes in an array of points and a float for time
|
||||||
|
time_func,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
self.time = 0
|
||||||
|
super().__init__(func=lambda p: time_func(p, self.time), **kwargs)
|
||||||
|
self.add_updater(lambda m, dt: m.increment_time(dt))
|
||||||
|
always(self.update_vectors)
|
||||||
|
|
||||||
|
def increment_time(self, dt):
|
||||||
|
self.time += dt
|
||||||
|
|
||||||
|
|
||||||
|
class OldVectorField(VGroup):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
func: Callable[[float, float], Sequence[float]],
|
func: Callable[[float, float], Sequence[float]],
|
||||||
|
|
|
@ -507,6 +507,8 @@ class InteractiveScene(Scene):
|
||||||
self.save_selection_to_file()
|
self.save_selection_to_file()
|
||||||
elif char == "d" and modifiers == SHIFT_MODIFIER:
|
elif char == "d" and modifiers == SHIFT_MODIFIER:
|
||||||
self.copy_frame_positioning()
|
self.copy_frame_positioning()
|
||||||
|
elif char == "c" and modifiers == SHIFT_MODIFIER:
|
||||||
|
self.copy_cursor_position()
|
||||||
elif symbol in ARROW_SYMBOLS:
|
elif symbol in ARROW_SYMBOLS:
|
||||||
self.nudge_selection(
|
self.nudge_selection(
|
||||||
vect=[LEFT, UP, RIGHT, DOWN][ARROW_SYMBOLS.index(symbol)],
|
vect=[LEFT, UP, RIGHT, DOWN][ARROW_SYMBOLS.index(symbol)],
|
||||||
|
@ -631,3 +633,6 @@ class InteractiveScene(Scene):
|
||||||
call += ", {:.2f}".format(height)
|
call += ", {:.2f}".format(height)
|
||||||
call += ")"
|
call += ")"
|
||||||
pyperclip.copy(call)
|
pyperclip.copy(call)
|
||||||
|
|
||||||
|
def copy_cursor_position(self):
|
||||||
|
pyperclip.copy(str(tuple(self.mouse_point.get_center().round(2))))
|
||||||
|
|
|
@ -255,17 +255,6 @@ class Scene(object):
|
||||||
pt_inputhooks.register("manim", inputhook)
|
pt_inputhooks.register("manim", inputhook)
|
||||||
shell.enable_gui("manim")
|
shell.enable_gui("manim")
|
||||||
|
|
||||||
# This is hacky, but there's an issue with ipython which is that
|
|
||||||
# when you define lambda's or list comprehensions during a shell session,
|
|
||||||
# they are not aware of local variables in the surrounding scope. Because
|
|
||||||
# That comes up a fair bit during scene construction, to get around this,
|
|
||||||
# we (admittedly sketchily) update the global namespace to match the local
|
|
||||||
# namespace, since this is just a shell session anyway.
|
|
||||||
shell.events.register(
|
|
||||||
"pre_run_cell",
|
|
||||||
lambda *args, **kwargs: shell.user_global_ns.update(shell.user_ns)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Operation to run after each ipython command
|
# Operation to run after each ipython command
|
||||||
def post_cell_func(*args, **kwargs):
|
def post_cell_func(*args, **kwargs):
|
||||||
if not self.is_window_closing():
|
if not self.is_window_closing():
|
||||||
|
|
|
@ -9,6 +9,7 @@ import sys
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from pydub import AudioSegment
|
from pydub import AudioSegment
|
||||||
from tqdm.auto import tqdm as ProgressDisplay
|
from tqdm.auto import tqdm as ProgressDisplay
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from manimlib.constants import FFMPEG_BIN
|
from manimlib.constants import FFMPEG_BIN
|
||||||
from manimlib.logger import log
|
from manimlib.logger import log
|
||||||
|
@ -299,15 +300,21 @@ class SceneFileWriter(object):
|
||||||
self.video_codec = "libx264rgb"
|
self.video_codec = "libx264rgb"
|
||||||
self.pixel_format = "rgb32"
|
self.pixel_format = "rgb32"
|
||||||
|
|
||||||
|
def get_insert_file_path(self, index: int) -> Path:
|
||||||
|
movie_path = Path(self.get_movie_file_path())
|
||||||
|
scene_name = movie_path.stem
|
||||||
|
insert_dir = Path(movie_path.parent, "inserts")
|
||||||
|
guarantee_existence(str(insert_dir))
|
||||||
|
return Path(insert_dir, f"{scene_name}_{index}{movie_path.suffix}")
|
||||||
|
|
||||||
def begin_insert(self):
|
def begin_insert(self):
|
||||||
# Begin writing process
|
# Begin writing process
|
||||||
self.write_to_movie = True
|
self.write_to_movie = True
|
||||||
self.init_output_directories()
|
self.init_output_directories()
|
||||||
movie_path = self.get_movie_file_path()
|
index = 0
|
||||||
count = 0
|
while (insert_path := self.get_insert_file_path(index)).exists():
|
||||||
while os.path.exists(name := movie_path.replace(".", f"_insert_{count}.")):
|
index += 1
|
||||||
count += 1
|
self.inserted_file_path = str(insert_path)
|
||||||
self.inserted_file_path = name
|
|
||||||
self.open_movie_pipe(self.inserted_file_path)
|
self.open_movie_pipe(self.inserted_file_path)
|
||||||
|
|
||||||
def end_insert(self):
|
def end_insert(self):
|
||||||
|
|
|
@ -2,6 +2,7 @@ uniform float is_fixed_in_frame;
|
||||||
uniform mat4 view;
|
uniform mat4 view;
|
||||||
uniform float focal_distance;
|
uniform float focal_distance;
|
||||||
uniform vec3 frame_rescale_factors;
|
uniform vec3 frame_rescale_factors;
|
||||||
|
uniform vec4 clip_plane;
|
||||||
|
|
||||||
void emit_gl_Position(vec3 point){
|
void emit_gl_Position(vec3 point){
|
||||||
vec4 result = vec4(point, 1.0);
|
vec4 result = vec4(point, 1.0);
|
||||||
|
@ -13,4 +14,8 @@ void emit_gl_Position(vec3 point){
|
||||||
// Flip and scale to prevent premature clipping
|
// Flip and scale to prevent premature clipping
|
||||||
result.z *= -0.1;
|
result.z *= -0.1;
|
||||||
gl_Position = result;
|
gl_Position = result;
|
||||||
|
|
||||||
|
if(clip_plane.xyz != vec3(0.0, 0.0, 0.0)){
|
||||||
|
gl_ClipDistance[0] = dot(vec4(point, 1.0), clip_plane);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#version 330
|
#version 330
|
||||||
|
|
||||||
uniform vec4 clip_plane;
|
|
||||||
|
|
||||||
in vec3 point;
|
in vec3 point;
|
||||||
in vec3 du_point;
|
in vec3 du_point;
|
||||||
in vec3 dv_point;
|
in vec3 dv_point;
|
||||||
|
@ -17,8 +15,4 @@ void main(){
|
||||||
emit_gl_Position(point);
|
emit_gl_Position(point);
|
||||||
vec3 normal = cross(normalize(du_point - point), normalize(dv_point - point));
|
vec3 normal = cross(normalize(du_point - point), normalize(dv_point - point));
|
||||||
v_color = finalize_color(rgba, point, normalize(normal));
|
v_color = finalize_color(rgba, point, normalize(normal));
|
||||||
|
|
||||||
if(clip_plane.xyz != vec3(0.0, 0.0, 0.0)){
|
|
||||||
gl_ClipDistance[0] = dot(vec4(point, 1.0), clip_plane);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -27,6 +27,6 @@ def index_labels(
|
||||||
label = Integer(n)
|
label = Integer(n)
|
||||||
label.set_height(label_height)
|
label.set_height(label_height)
|
||||||
label.move_to(submob)
|
label.move_to(submob)
|
||||||
label.set_stroke(BLACK, 5, background=True)
|
label.set_backstroke(BLACK, 5)
|
||||||
labels.add(label)
|
labels.add(label)
|
||||||
return labels
|
return labels
|
||||||
|
|
|
@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
def remove_list_redundancies(lst: Sequence[T]) -> list[T]:
|
def remove_list_redundancies(lst: Sequence[T]) -> list[T]:
|
||||||
"""
|
"""
|
||||||
Used instead of list(set(l)) to maintain order
|
Remove duplicate elements while preserving order.
|
||||||
Keeps the last occurrence of each element
|
Keeps the last occurrence of each element
|
||||||
"""
|
"""
|
||||||
return list(reversed(dict.fromkeys(reversed(lst))))
|
return list(reversed(dict.fromkeys(reversed(lst))))
|
||||||
|
|
|
@ -67,7 +67,7 @@ def set_program_uniform(
|
||||||
uniform_mirror = PROGRAM_UNIFORM_MIRRORS[pid]
|
uniform_mirror = PROGRAM_UNIFORM_MIRRORS[pid]
|
||||||
|
|
||||||
if type(value) is np.ndarray and value.ndim > 0:
|
if type(value) is np.ndarray and value.ndim > 0:
|
||||||
value = tuple(value)
|
value = tuple(value.flatten())
|
||||||
if uniform_mirror.get(name, None) == value:
|
if uniform_mirror.get(name, None) == value:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue