mirror of
https://github.com/3b1b/manim.git
synced 2025-09-01 00:48:45 +00:00
Kill CONFIG in vectorized_mobject.py
This commit is contained in:
parent
97a5861ccf
commit
a715a5bc3f
1 changed files with 143 additions and 121 deletions
|
@ -42,57 +42,67 @@ from manimlib.shader_wrapper import ShaderWrapper
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Callable, Iterable, Sequence
|
from typing import Callable, Iterable, Sequence, Tuple
|
||||||
|
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
from manimlib.constants import ManimColor
|
|
||||||
|
from manimlib.constants import ManimColor, np_vector
|
||||||
|
|
||||||
|
|
||||||
class VMobject(Mobject):
|
class VMobject(Mobject):
|
||||||
CONFIG = {
|
n_points_per_curve: int = 3
|
||||||
"fill_color": None,
|
stroke_shader_folder: str = "quadratic_bezier_stroke"
|
||||||
"fill_opacity": 0.0,
|
fill_shader_folder: str = "quadratic_bezier_fill"
|
||||||
"stroke_color": None,
|
fill_dtype: Sequence[Tuple[str, type, Tuple[int]]] = [
|
||||||
"stroke_opacity": 1.0,
|
('point', np.float32, (3,)),
|
||||||
"stroke_width": DEFAULT_STROKE_WIDTH,
|
('unit_normal', np.float32, (3,)),
|
||||||
"draw_stroke_behind_fill": False,
|
('color', np.float32, (4,)),
|
||||||
# Indicates that it will not be displayed, but
|
('vert_index', np.float32, (1,)),
|
||||||
# that it should count in parent mobject's path
|
]
|
||||||
"pre_function_handle_to_anchor_scale_factor": 0.01,
|
stroke_dtype: Sequence[Tuple[str, type, Tuple[int]]] = [
|
||||||
"make_smooth_after_applying_functions": False,
|
("point", np.float32, (3,)),
|
||||||
"background_image_file": None,
|
("prev_point", np.float32, (3,)),
|
||||||
# This is within a pixel
|
("next_point", np.float32, (3,)),
|
||||||
# TODO, do we care about accounting for
|
('unit_normal', np.float32, (3,)),
|
||||||
# varying zoom levels?
|
("stroke_width", np.float32, (1,)),
|
||||||
"tolerance_for_point_equality": 1e-8,
|
("color", np.float32, (4,)),
|
||||||
"n_points_per_curve": 3,
|
]
|
||||||
"long_lines": False,
|
render_primitive: int = moderngl.TRIANGLES
|
||||||
# For shaders
|
|
||||||
"stroke_shader_folder": "quadratic_bezier_stroke",
|
pre_function_handle_to_anchor_scale_factor: float = 0.01
|
||||||
"fill_shader_folder": "quadratic_bezier_fill",
|
make_smooth_after_applying_functions: bool = False
|
||||||
# Could also be "bevel", "miter", "round"
|
# TODO, do we care about accounting for varying zoom levels?
|
||||||
"joint_type": "auto",
|
tolerance_for_point_equality: float = 1e-8
|
||||||
"flat_stroke": False,
|
|
||||||
"render_primitive": moderngl.TRIANGLES,
|
def __init__(
|
||||||
"fill_dtype": [
|
self,
|
||||||
('point', np.float32, (3,)),
|
color: ManimColor = None, # If set, this will override stroke_color and fill_color
|
||||||
('unit_normal', np.float32, (3,)),
|
fill_color: ManimColor = WHITE,
|
||||||
('color', np.float32, (4,)),
|
fill_opacity: float = 0.0,
|
||||||
('vert_index', np.float32, (1,)),
|
stroke_color: ManimColor = GREY_C,
|
||||||
],
|
stroke_opacity: float = 1.0,
|
||||||
"stroke_dtype": [
|
stroke_width: float = DEFAULT_STROKE_WIDTH,
|
||||||
("point", np.float32, (3,)),
|
draw_stroke_behind_fill: bool = False,
|
||||||
("prev_point", np.float32, (3,)),
|
background_image_file: str | None = None,
|
||||||
("next_point", np.float32, (3,)),
|
long_lines: bool = False,
|
||||||
('unit_normal', np.float32, (3,)),
|
# Could also be "bevel", "miter", "round"
|
||||||
("stroke_width", np.float32, (1,)),
|
joint_type: str = "auto",
|
||||||
("color", np.float32, (4,)),
|
flat_stroke: bool = False,
|
||||||
]
|
**kwargs
|
||||||
}
|
):
|
||||||
|
self.fill_color = color or fill_color
|
||||||
|
self.fill_opacity = fill_opacity
|
||||||
|
self.stroke_color = color or stroke_color
|
||||||
|
self.stroke_opacity = stroke_opacity
|
||||||
|
self.stroke_width = stroke_width
|
||||||
|
self.draw_stroke_behind_fill = draw_stroke_behind_fill
|
||||||
|
self.background_image_file = background_image_file
|
||||||
|
self.long_lines = long_lines
|
||||||
|
self.joint_type = joint_type
|
||||||
|
self.flat_stroke = flat_stroke
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.needs_new_triangulation = True
|
self.needs_new_triangulation = True
|
||||||
self.triangulation = np.zeros(0, dtype='i4')
|
self.triangulation = np.zeros(0, dtype='i4')
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def get_group_class(self):
|
def get_group_class(self):
|
||||||
|
@ -108,32 +118,40 @@ class VMobject(Mobject):
|
||||||
"unit_normal": np.zeros((1, 3))
|
"unit_normal": np.zeros((1, 3))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# These are here just to make type checkers happy
|
||||||
def get_family(self, recurse: bool = True) -> list[VMobject]:
|
def get_family(self, recurse: bool = True) -> list[VMobject]:
|
||||||
return super().get_family(recurse)
|
return super().get_family(recurse)
|
||||||
|
|
||||||
|
def family_members_with_points(self) -> list[VMobject]:
|
||||||
|
return super().family_members_with_points()
|
||||||
|
|
||||||
def replicate(self, n: int) -> VGroup:
|
def replicate(self, n: int) -> VGroup:
|
||||||
return super().replicate(n)
|
return super().replicate(n)
|
||||||
|
|
||||||
|
def get_grid(self, *args, **kwargs) -> VGroup:
|
||||||
|
return super().get_grid(*args, **kwargs)
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
def init_colors(self):
|
def init_colors(self):
|
||||||
self.set_fill(
|
self.set_fill(
|
||||||
color=self.fill_color or self.color,
|
color=self.fill_color,
|
||||||
opacity=self.fill_opacity,
|
opacity=self.fill_opacity,
|
||||||
)
|
)
|
||||||
self.set_stroke(
|
self.set_stroke(
|
||||||
color=self.stroke_color or self.color,
|
color=self.stroke_color,
|
||||||
width=self.stroke_width,
|
width=self.stroke_width,
|
||||||
opacity=self.stroke_opacity,
|
opacity=self.stroke_opacity,
|
||||||
background=self.draw_stroke_behind_fill,
|
background=self.draw_stroke_behind_fill,
|
||||||
)
|
)
|
||||||
self.set_gloss(self.gloss)
|
self.set_gloss(self.gloss)
|
||||||
self.set_flat_stroke(self.flat_stroke)
|
self.set_flat_stroke(self.flat_stroke)
|
||||||
|
self.color = self.get_color()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_rgba_array(
|
def set_rgba_array(
|
||||||
self,
|
self,
|
||||||
rgba_array: npt.ArrayLike,
|
rgba_array: np_vector,
|
||||||
name: str = None,
|
name: str | None = None,
|
||||||
recurse: bool = False
|
recurse: bool = False
|
||||||
):
|
):
|
||||||
if name is None:
|
if name is None:
|
||||||
|
@ -147,7 +165,7 @@ class VMobject(Mobject):
|
||||||
|
|
||||||
def set_fill(
|
def set_fill(
|
||||||
self,
|
self,
|
||||||
color: ManimColor | Iterable[ManimColor] | None = None,
|
color: ManimColor | Iterable[ManimColor] = None,
|
||||||
opacity: float | Iterable[float] | None = None,
|
opacity: float | Iterable[float] | None = None,
|
||||||
recurse: bool = True
|
recurse: bool = True
|
||||||
):
|
):
|
||||||
|
@ -156,7 +174,7 @@ class VMobject(Mobject):
|
||||||
|
|
||||||
def set_stroke(
|
def set_stroke(
|
||||||
self,
|
self,
|
||||||
color: ManimColor | Iterable[ManimColor] | None = None,
|
color: ManimColor | Iterable[ManimColor] = None,
|
||||||
width: float | Iterable[float] | None = None,
|
width: float | Iterable[float] | None = None,
|
||||||
opacity: float | Iterable[float] | None = None,
|
opacity: float | Iterable[float] | None = None,
|
||||||
background: bool | None = None,
|
background: bool | None = None,
|
||||||
|
@ -196,10 +214,10 @@ class VMobject(Mobject):
|
||||||
self,
|
self,
|
||||||
fill_color: ManimColor | Iterable[ManimColor] | None = None,
|
fill_color: ManimColor | Iterable[ManimColor] | None = None,
|
||||||
fill_opacity: float | Iterable[float] | None = None,
|
fill_opacity: float | Iterable[float] | None = None,
|
||||||
fill_rgba: npt.ArrayLike | None = None,
|
fill_rgba: np_vector | None = None,
|
||||||
stroke_color: ManimColor | Iterable[ManimColor] | None = None,
|
stroke_color: ManimColor | Iterable[ManimColor] | None = None,
|
||||||
stroke_opacity: float | Iterable[float] | None = None,
|
stroke_opacity: float | Iterable[float] | None = None,
|
||||||
stroke_rgba: npt.ArrayLike | None = None,
|
stroke_rgba: np_vector | None = None,
|
||||||
stroke_width: float | Iterable[float] | None = None,
|
stroke_width: float | Iterable[float] | None = None,
|
||||||
stroke_background: bool = True,
|
stroke_background: bool = True,
|
||||||
reflectiveness: float | None = None,
|
reflectiveness: float | None = None,
|
||||||
|
@ -218,7 +236,7 @@ class VMobject(Mobject):
|
||||||
)
|
)
|
||||||
|
|
||||||
if stroke_rgba is not None:
|
if stroke_rgba is not None:
|
||||||
mob.data['stroke_rgba'] = resize_with_interpolation(stroke_rgba, len(fill_rgba))
|
mob.data['stroke_rgba'] = resize_with_interpolation(stroke_rgba, len(stroke_rgba))
|
||||||
mob.set_stroke(
|
mob.set_stroke(
|
||||||
width=stroke_width,
|
width=stroke_width,
|
||||||
background=stroke_background,
|
background=stroke_background,
|
||||||
|
@ -305,7 +323,7 @@ class VMobject(Mobject):
|
||||||
for rgba in self.data['fill_rgba']
|
for rgba in self.data['fill_rgba']
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_fill_opacities(self) -> np.ndarray:
|
def get_fill_opacities(self) -> np_vector:
|
||||||
return self.data['fill_rgba'][:, 3]
|
return self.data['fill_rgba'][:, 3]
|
||||||
|
|
||||||
def get_stroke_colors(self) -> list[str]:
|
def get_stroke_colors(self) -> list[str]:
|
||||||
|
@ -314,10 +332,10 @@ class VMobject(Mobject):
|
||||||
for rgba in self.data['stroke_rgba']
|
for rgba in self.data['stroke_rgba']
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_stroke_opacities(self) -> np.ndarray:
|
def get_stroke_opacities(self) -> np_vector:
|
||||||
return self.data['stroke_rgba'][:, 3]
|
return self.data['stroke_rgba'][:, 3]
|
||||||
|
|
||||||
def get_stroke_widths(self) -> np.ndarray:
|
def get_stroke_widths(self) -> np_vector:
|
||||||
return self.data['stroke_width'][:, 0]
|
return self.data['stroke_width'][:, 0]
|
||||||
|
|
||||||
# TODO, it's weird for these to return the first of various lists
|
# TODO, it's weird for these to return the first of various lists
|
||||||
|
@ -339,7 +357,7 @@ class VMobject(Mobject):
|
||||||
def get_stroke_color(self) -> str:
|
def get_stroke_color(self) -> str:
|
||||||
return self.get_stroke_colors()[0]
|
return self.get_stroke_colors()[0]
|
||||||
|
|
||||||
def get_stroke_width(self) -> float | np.ndarray:
|
def get_stroke_width(self) -> float | np_vector:
|
||||||
return self.get_stroke_widths()[0]
|
return self.get_stroke_widths()[0]
|
||||||
|
|
||||||
def get_stroke_opacity(self) -> float:
|
def get_stroke_opacity(self) -> float:
|
||||||
|
@ -380,9 +398,9 @@ class VMobject(Mobject):
|
||||||
# Points
|
# Points
|
||||||
def set_anchors_and_handles(
|
def set_anchors_and_handles(
|
||||||
self,
|
self,
|
||||||
anchors1: np.ndarray,
|
anchors1: np_vector,
|
||||||
handles: np.ndarray,
|
handles: np_vector,
|
||||||
anchors2: np.ndarray
|
anchors2: np_vector
|
||||||
):
|
):
|
||||||
assert(len(anchors1) == len(handles) == len(anchors2))
|
assert(len(anchors1) == len(handles) == len(anchors2))
|
||||||
nppc = self.n_points_per_curve
|
nppc = self.n_points_per_curve
|
||||||
|
@ -393,26 +411,26 @@ class VMobject(Mobject):
|
||||||
self.set_points(new_points)
|
self.set_points(new_points)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def start_new_path(self, point: np.ndarray):
|
def start_new_path(self, point: np_vector):
|
||||||
assert(self.get_num_points() % self.n_points_per_curve == 0)
|
assert(self.get_num_points() % self.n_points_per_curve == 0)
|
||||||
self.append_points([point])
|
self.append_points([point])
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_cubic_bezier_curve(
|
def add_cubic_bezier_curve(
|
||||||
self,
|
self,
|
||||||
anchor1: npt.ArrayLike,
|
anchor1: np_vector,
|
||||||
handle1: npt.ArrayLike,
|
handle1: np_vector,
|
||||||
handle2: npt.ArrayLike,
|
handle2: np_vector,
|
||||||
anchor2: npt.ArrayLike
|
anchor2: np_vector
|
||||||
):
|
):
|
||||||
new_points = get_quadratic_approximation_of_cubic(anchor1, handle1, handle2, anchor2)
|
new_points = get_quadratic_approximation_of_cubic(anchor1, handle1, handle2, anchor2)
|
||||||
self.append_points(new_points)
|
self.append_points(new_points)
|
||||||
|
|
||||||
def add_cubic_bezier_curve_to(
|
def add_cubic_bezier_curve_to(
|
||||||
self,
|
self,
|
||||||
handle1: npt.ArrayLike,
|
handle1: np_vector,
|
||||||
handle2: npt.ArrayLike,
|
handle2: np_vector,
|
||||||
anchor: npt.ArrayLike
|
anchor: np_vector
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Add cubic bezier curve to the path.
|
Add cubic bezier curve to the path.
|
||||||
|
@ -426,14 +444,14 @@ class VMobject(Mobject):
|
||||||
else:
|
else:
|
||||||
self.append_points(quadratic_approx)
|
self.append_points(quadratic_approx)
|
||||||
|
|
||||||
def add_quadratic_bezier_curve_to(self, handle: np.ndarray, anchor: np.ndarray):
|
def add_quadratic_bezier_curve_to(self, handle: np_vector, anchor: np_vector):
|
||||||
self.throw_error_if_no_points()
|
self.throw_error_if_no_points()
|
||||||
if self.has_new_path_started():
|
if self.has_new_path_started():
|
||||||
self.append_points([handle, anchor])
|
self.append_points([handle, anchor])
|
||||||
else:
|
else:
|
||||||
self.append_points([self.get_last_point(), handle, anchor])
|
self.append_points([self.get_last_point(), handle, anchor])
|
||||||
|
|
||||||
def add_line_to(self, point: np.ndarray):
|
def add_line_to(self, point: np_vector):
|
||||||
end = self.get_points()[-1]
|
end = self.get_points()[-1]
|
||||||
alphas = np.linspace(0, 1, self.n_points_per_curve)
|
alphas = np.linspace(0, 1, self.n_points_per_curve)
|
||||||
if self.long_lines:
|
if self.long_lines:
|
||||||
|
@ -455,7 +473,7 @@ class VMobject(Mobject):
|
||||||
self.append_points(points)
|
self.append_points(points)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_smooth_curve_to(self, point: np.ndarray):
|
def add_smooth_curve_to(self, point: np_vector):
|
||||||
if self.has_new_path_started():
|
if self.has_new_path_started():
|
||||||
self.add_line_to(point)
|
self.add_line_to(point)
|
||||||
else:
|
else:
|
||||||
|
@ -464,7 +482,7 @@ class VMobject(Mobject):
|
||||||
self.add_quadratic_bezier_curve_to(new_handle, point)
|
self.add_quadratic_bezier_curve_to(new_handle, point)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_smooth_cubic_curve_to(self, handle: np.ndarray, point: np.ndarray):
|
def add_smooth_cubic_curve_to(self, handle: np_vector, point: np_vector):
|
||||||
self.throw_error_if_no_points()
|
self.throw_error_if_no_points()
|
||||||
if self.get_num_points() == 1:
|
if self.get_num_points() == 1:
|
||||||
new_handle = self.get_points()[-1]
|
new_handle = self.get_points()[-1]
|
||||||
|
@ -475,10 +493,10 @@ class VMobject(Mobject):
|
||||||
def has_new_path_started(self) -> bool:
|
def has_new_path_started(self) -> bool:
|
||||||
return self.get_num_points() % self.n_points_per_curve == 1
|
return self.get_num_points() % self.n_points_per_curve == 1
|
||||||
|
|
||||||
def get_last_point(self) -> np.ndarray:
|
def get_last_point(self) -> np_vector:
|
||||||
return self.get_points()[-1]
|
return self.get_points()[-1]
|
||||||
|
|
||||||
def get_reflection_of_last_handle(self) -> np.ndarray:
|
def get_reflection_of_last_handle(self) -> np_vector:
|
||||||
points = self.get_points()
|
points = self.get_points()
|
||||||
return 2 * points[-1] - points[-2]
|
return 2 * points[-1] - points[-2]
|
||||||
|
|
||||||
|
@ -513,12 +531,12 @@ class VMobject(Mobject):
|
||||||
vmob.set_points(np.vstack(new_points))
|
vmob.set_points(np.vstack(new_points))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_points_as_corners(self, points: Iterable[np.ndarray]):
|
def add_points_as_corners(self, points: Iterable[np_vector]):
|
||||||
for point in points:
|
for point in points:
|
||||||
self.add_line_to(point)
|
self.add_line_to(point)
|
||||||
return points
|
return points
|
||||||
|
|
||||||
def set_points_as_corners(self, points: Iterable[np.ndarray]):
|
def set_points_as_corners(self, points: Iterable[np_vector]):
|
||||||
nppc = self.n_points_per_curve
|
nppc = self.n_points_per_curve
|
||||||
points = np.array(points)
|
points = np.array(points)
|
||||||
self.set_anchors_and_handles(*[
|
self.set_anchors_and_handles(*[
|
||||||
|
@ -529,7 +547,7 @@ class VMobject(Mobject):
|
||||||
|
|
||||||
def set_points_smoothly(
|
def set_points_smoothly(
|
||||||
self,
|
self,
|
||||||
points: Iterable[np.ndarray],
|
points: Iterable[np_vector],
|
||||||
true_smooth: bool = False
|
true_smooth: bool = False
|
||||||
):
|
):
|
||||||
self.set_points_as_corners(points)
|
self.set_points_as_corners(points)
|
||||||
|
@ -584,7 +602,7 @@ class VMobject(Mobject):
|
||||||
self.change_anchor_mode("jagged")
|
self.change_anchor_mode("jagged")
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_subpath(self, points: Iterable[np.ndarray]):
|
def add_subpath(self, points: Iterable[np_vector]):
|
||||||
assert(len(points) % self.n_points_per_curve == 0)
|
assert(len(points) % self.n_points_per_curve == 0)
|
||||||
self.append_points(points)
|
self.append_points(points)
|
||||||
return self
|
return self
|
||||||
|
@ -600,11 +618,11 @@ class VMobject(Mobject):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
#
|
#
|
||||||
def consider_points_equals(self, p0: np.ndarray, p1: np.ndarray) -> bool:
|
def consider_points_equals(self, p0: np_vector, p1: np_vector) -> bool:
|
||||||
return get_norm(p1 - p0) < self.tolerance_for_point_equality
|
return get_norm(p1 - p0) < self.tolerance_for_point_equality
|
||||||
|
|
||||||
# Information about the curve
|
# Information about the curve
|
||||||
def get_bezier_tuples_from_points(self, points: Sequence[np.ndarray]):
|
def get_bezier_tuples_from_points(self, points: Sequence[np_vector]):
|
||||||
nppc = self.n_points_per_curve
|
nppc = self.n_points_per_curve
|
||||||
remainder = len(points) % nppc
|
remainder = len(points) % nppc
|
||||||
points = points[:len(points) - remainder]
|
points = points[:len(points) - remainder]
|
||||||
|
@ -618,8 +636,8 @@ class VMobject(Mobject):
|
||||||
|
|
||||||
def get_subpaths_from_points(
|
def get_subpaths_from_points(
|
||||||
self,
|
self,
|
||||||
points: Sequence[np.ndarray]
|
points: Sequence[np_vector]
|
||||||
) -> list[Sequence[np.ndarray]]:
|
) -> list[Sequence[np_vector]]:
|
||||||
nppc = self.n_points_per_curve
|
nppc = self.n_points_per_curve
|
||||||
diffs = points[nppc - 1:-1:nppc] - points[nppc::nppc]
|
diffs = points[nppc - 1:-1:nppc] - points[nppc::nppc]
|
||||||
splits = (diffs * diffs).sum(1) > self.tolerance_for_point_equality
|
splits = (diffs * diffs).sum(1) > self.tolerance_for_point_equality
|
||||||
|
@ -636,28 +654,28 @@ class VMobject(Mobject):
|
||||||
if (i2 - i1) >= nppc
|
if (i2 - i1) >= nppc
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_subpaths(self) -> list[Sequence[np.ndarray]]:
|
def get_subpaths(self) -> list[Sequence[np_vector]]:
|
||||||
return self.get_subpaths_from_points(self.get_points())
|
return self.get_subpaths_from_points(self.get_points())
|
||||||
|
|
||||||
def get_nth_curve_points(self, n: int) -> np.ndarray:
|
def get_nth_curve_points(self, n: int) -> np_vector:
|
||||||
assert(n < self.get_num_curves())
|
assert(n < self.get_num_curves())
|
||||||
nppc = self.n_points_per_curve
|
nppc = self.n_points_per_curve
|
||||||
return self.get_points()[nppc * n:nppc * (n + 1)]
|
return self.get_points()[nppc * n:nppc * (n + 1)]
|
||||||
|
|
||||||
def get_nth_curve_function(self, n: int) -> Callable[[float], np.ndarray]:
|
def get_nth_curve_function(self, n: int) -> Callable[[float], np_vector]:
|
||||||
return bezier(self.get_nth_curve_points(n))
|
return bezier(self.get_nth_curve_points(n))
|
||||||
|
|
||||||
def get_num_curves(self) -> int:
|
def get_num_curves(self) -> int:
|
||||||
return self.get_num_points() // self.n_points_per_curve
|
return self.get_num_points() // self.n_points_per_curve
|
||||||
|
|
||||||
def quick_point_from_proportion(self, alpha: float) -> np.ndarray:
|
def quick_point_from_proportion(self, alpha: float) -> np_vector:
|
||||||
# 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()
|
||||||
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)
|
||||||
|
|
||||||
def point_from_proportion(self, alpha: float) -> np.ndarray:
|
def point_from_proportion(self, alpha: float) -> np_vector:
|
||||||
if alpha <= 0:
|
if alpha <= 0:
|
||||||
return self.get_start()
|
return self.get_start()
|
||||||
elif alpha >= 1:
|
elif alpha >= 1:
|
||||||
|
@ -679,7 +697,7 @@ class VMobject(Mobject):
|
||||||
residue = inverse_interpolate(partials[i - 1] / full, partials[i] / full, alpha)
|
residue = inverse_interpolate(partials[i - 1] / full, partials[i] / full, alpha)
|
||||||
return self.get_nth_curve_function(i - 1)(residue)
|
return self.get_nth_curve_function(i - 1)(residue)
|
||||||
|
|
||||||
def get_anchors_and_handles(self) -> list[np.ndarray]:
|
def get_anchors_and_handles(self) -> list[np_vector]:
|
||||||
"""
|
"""
|
||||||
returns anchors1, handles, anchors2,
|
returns anchors1, handles, anchors2,
|
||||||
where (anchors1[i], handles[i], anchors2[i])
|
where (anchors1[i], handles[i], anchors2[i])
|
||||||
|
@ -693,14 +711,14 @@ class VMobject(Mobject):
|
||||||
for i in range(nppc)
|
for i in range(nppc)
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_start_anchors(self) -> np.ndarray:
|
def get_start_anchors(self) -> np_vector:
|
||||||
return self.get_points()[0::self.n_points_per_curve]
|
return self.get_points()[0::self.n_points_per_curve]
|
||||||
|
|
||||||
def get_end_anchors(self) -> np.ndarray:
|
def get_end_anchors(self) -> np_vector:
|
||||||
nppc = self.n_points_per_curve
|
nppc = self.n_points_per_curve
|
||||||
return self.get_points()[nppc - 1::nppc]
|
return self.get_points()[nppc - 1::nppc]
|
||||||
|
|
||||||
def get_anchors(self) -> np.ndarray:
|
def get_anchors(self) -> np_vector:
|
||||||
points = self.get_points()
|
points = self.get_points()
|
||||||
if len(points) == 1:
|
if len(points) == 1:
|
||||||
return points
|
return points
|
||||||
|
@ -709,7 +727,7 @@ class VMobject(Mobject):
|
||||||
self.get_end_anchors(),
|
self.get_end_anchors(),
|
||||||
))))
|
))))
|
||||||
|
|
||||||
def get_points_without_null_curves(self, atol: float = 1e-9) -> np.ndarray:
|
def get_points_without_null_curves(self, atol: float = 1e-9) -> np_vector:
|
||||||
nppc = self.n_points_per_curve
|
nppc = self.n_points_per_curve
|
||||||
points = self.get_points()
|
points = self.get_points()
|
||||||
distinct_curves = reduce(op.or_, [
|
distinct_curves = reduce(op.or_, [
|
||||||
|
@ -729,7 +747,7 @@ class VMobject(Mobject):
|
||||||
norms = np.array([get_norm(d) for d in diffs])
|
norms = np.array([get_norm(d) for d in diffs])
|
||||||
return norms.sum()
|
return norms.sum()
|
||||||
|
|
||||||
def get_area_vector(self) -> np.ndarray:
|
def get_area_vector(self) -> np_vector:
|
||||||
# Returns a vector whose length is the area bound by
|
# Returns a vector whose length is the area bound by
|
||||||
# the polygon formed by the anchor points, pointing
|
# the polygon formed by the anchor points, pointing
|
||||||
# in a direction perpendicular to the polygon according
|
# in a direction perpendicular to the polygon according
|
||||||
|
@ -749,7 +767,7 @@ class VMobject(Mobject):
|
||||||
sum((p0[:, 0] + p1[:, 0]) * (p1[:, 1] - p0[:, 1])), # Add up (x1 + x2)*(y2 - y1)
|
sum((p0[:, 0] + p1[:, 0]) * (p1[:, 1] - p0[:, 1])), # Add up (x1 + x2)*(y2 - y1)
|
||||||
])
|
])
|
||||||
|
|
||||||
def get_unit_normal(self, recompute: bool = False) -> np.ndarray:
|
def get_unit_normal(self, recompute: bool = False) -> np_vector:
|
||||||
if not recompute:
|
if not recompute:
|
||||||
return self.data["unit_normal"][0]
|
return self.data["unit_normal"][0]
|
||||||
|
|
||||||
|
@ -834,7 +852,7 @@ class VMobject(Mobject):
|
||||||
mob.set_points(new_points)
|
mob.set_points(new_points)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def insert_n_curves_to_point_list(self, n: int, points: np.ndarray):
|
def insert_n_curves_to_point_list(self, n: int, points: np_vector):
|
||||||
nppc = self.n_points_per_curve
|
nppc = self.n_points_per_curve
|
||||||
if len(points) == 1:
|
if len(points) == 1:
|
||||||
return np.repeat(points, nppc * n, 0)
|
return np.repeat(points, nppc * n, 0)
|
||||||
|
@ -936,7 +954,7 @@ class VMobject(Mobject):
|
||||||
mob.needs_new_triangulation = True
|
mob.needs_new_triangulation = True
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_triangulation(self, normal_vector: np.ndarray | None = None):
|
def get_triangulation(self, normal_vector: np_vector | None = None):
|
||||||
# Figure out how to triangulate the interior to know
|
# Figure out how to triangulate the interior to know
|
||||||
# how to send the points as to the vertex shader.
|
# how to send the points as to the vertex shader.
|
||||||
# First triangles come directly from the points
|
# First triangles come directly from the points
|
||||||
|
@ -1005,7 +1023,7 @@ class VMobject(Mobject):
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
@triggers_refreshed_triangulation
|
@triggers_refreshed_triangulation
|
||||||
def set_points(self, points: npt.ArrayLike):
|
def set_points(self, points: np_vector):
|
||||||
super().set_points(points)
|
super().set_points(points)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -1018,7 +1036,7 @@ class VMobject(Mobject):
|
||||||
@triggers_refreshed_triangulation
|
@triggers_refreshed_triangulation
|
||||||
def apply_function(
|
def apply_function(
|
||||||
self,
|
self,
|
||||||
function: Callable[[np.ndarray], np.ndarray],
|
function: Callable[[np_vector], np_vector],
|
||||||
make_smooth: bool = False,
|
make_smooth: bool = False,
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
|
@ -1027,7 +1045,7 @@ class VMobject(Mobject):
|
||||||
self.make_approximately_smooth()
|
self.make_approximately_smooth()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def flip(self, axis: np.ndarray = UP, **kwargs):
|
def flip(self, axis: np_vector = UP, **kwargs):
|
||||||
super().flip(axis, **kwargs)
|
super().flip(axis, **kwargs)
|
||||||
self.refresh_unit_normal()
|
self.refresh_unit_normal()
|
||||||
self.refresh_triangulation()
|
self.refresh_triangulation()
|
||||||
|
@ -1154,17 +1172,22 @@ class VGroup(VMobject):
|
||||||
|
|
||||||
|
|
||||||
class VectorizedPoint(Point, VMobject):
|
class VectorizedPoint(Point, VMobject):
|
||||||
CONFIG = {
|
def __init__(
|
||||||
"color": BLACK,
|
self,
|
||||||
"fill_opacity": 0,
|
location: np.ndarray = ORIGIN,
|
||||||
"stroke_width": 0,
|
color: ManimColor = BLACK,
|
||||||
"artificial_width": 0.01,
|
fill_opacity: float = 0.0,
|
||||||
"artificial_height": 0.01,
|
stroke_width: float = 0.0,
|
||||||
}
|
**kwargs
|
||||||
|
):
|
||||||
def __init__(self, location: np.ndarray = ORIGIN, **kwargs):
|
Point.__init__(self, location, **kwargs)
|
||||||
Point.__init__(self, **kwargs)
|
VMobject.__init__(
|
||||||
VMobject.__init__(self, **kwargs)
|
self,
|
||||||
|
color=color,
|
||||||
|
fill_opacity=fill_opacity,
|
||||||
|
stroke_width=stroke_width,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
self.set_points(np.array([location]))
|
self.set_points(np.array([location]))
|
||||||
|
|
||||||
|
|
||||||
|
@ -1179,23 +1202,22 @@ class CurvesAsSubmobjects(VGroup):
|
||||||
|
|
||||||
|
|
||||||
class DashedVMobject(VMobject):
|
class DashedVMobject(VMobject):
|
||||||
CONFIG = {
|
def __init__(
|
||||||
"num_dashes": 15,
|
self,
|
||||||
"positive_space_ratio": 0.5,
|
vmobject: VMobject,
|
||||||
"color": WHITE
|
num_dashes: int = 15,
|
||||||
}
|
positive_space_ratio: float = 0.5,
|
||||||
|
**kwargs
|
||||||
def __init__(self, vmobject: VMobject, **kwargs):
|
):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
num_dashes = self.num_dashes
|
|
||||||
ps_ratio = self.positive_space_ratio
|
|
||||||
if num_dashes > 0:
|
if num_dashes > 0:
|
||||||
# End points of the unit interval for division
|
# End points of the unit interval for division
|
||||||
alphas = np.linspace(0, 1, num_dashes + 1)
|
alphas = np.linspace(0, 1, num_dashes + 1)
|
||||||
|
|
||||||
# This determines the length of each "dash"
|
# This determines the length of each "dash"
|
||||||
full_d_alpha = (1.0 / num_dashes)
|
full_d_alpha = (1.0 / num_dashes)
|
||||||
partial_d_alpha = full_d_alpha * ps_ratio
|
partial_d_alpha = full_d_alpha * positive_space_ratio
|
||||||
|
|
||||||
# Rescale so that the last point of vmobject will
|
# Rescale so that the last point of vmobject will
|
||||||
# be the end of the last dash
|
# be the end of the last dash
|
||||||
|
@ -1215,7 +1237,7 @@ class VHighlight(VGroup):
|
||||||
self,
|
self,
|
||||||
vmobject: VMobject,
|
vmobject: VMobject,
|
||||||
n_layers: int = 5,
|
n_layers: int = 5,
|
||||||
color_bounds: tuple[ManimColor] = (GREY_C, GREY_E),
|
color_bounds: Tuple[ManimColor] = (GREY_C, GREY_E),
|
||||||
max_stroke_addition: float = 5.0,
|
max_stroke_addition: float = 5.0,
|
||||||
):
|
):
|
||||||
outline = vmobject.replicate(n_layers)
|
outline = vmobject.replicate(n_layers)
|
||||||
|
|
Loading…
Add table
Reference in a new issue