Kill CONFIG in vectorized_mobject.py

This commit is contained in:
Grant Sanderson 2022-12-15 09:19:05 -08:00
parent 97a5861ccf
commit a715a5bc3f

View file

@ -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)