Kill CONFIG in geometry.py

This commit is contained in:
Grant Sanderson 2022-12-15 18:19:09 -08:00
parent c244f8738f
commit 2a645b27f8

View file

@ -31,7 +31,7 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Iterable
from manimlib.constants import ManimColor
from manimlib.constants import ManimColor, np_vector
DEFAULT_DOT_RADIUS = 0.08
@ -60,14 +60,11 @@ class TipableVMobject(VMobject):
- Straightforward accessors, returning information pertaining
to the TipableVMobject instance's tip(s), its length etc
"""
CONFIG = {
"tip_config": {
"fill_opacity": 1,
"stroke_width": 0,
"tip_style": 0, # triangle=0, inner_smooth=1, dot=2
},
"normal_vector": OUT,
}
tip_config: dict = dict(
fill_opacity=1.0,
stroke_width=0.0,
tip_style=0.0, # triangle=0, inner_smooth=1, dot=2
)
# Adding, Creating, Modifying tips
def add_tip(self, at_start: bool = False, **kwargs):
@ -181,19 +178,19 @@ class TipableVMobject(VMobject):
def get_default_tip_length(self) -> float:
return self.tip_length
def get_first_handle(self) -> np.ndarray:
def get_first_handle(self) -> np_vector:
return self.get_points()[1]
def get_last_handle(self) -> np.ndarray:
def get_last_handle(self) -> np_vector:
return self.get_points()[-2]
def get_end(self) -> np.ndarray:
def get_end(self) -> np_vector:
if self.has_tip():
return self.tip.get_start()
else:
return VMobject.get_end(self)
def get_start(self) -> np.ndarray:
def get_start(self) -> np_vector:
if self.has_start_tip():
return self.start_tip.get_start()
else:
@ -205,38 +202,31 @@ class TipableVMobject(VMobject):
class Arc(TipableVMobject):
CONFIG = {
"radius": 1.0,
"n_components": 8,
"anchors_span_full_range": True,
"arc_center": ORIGIN,
}
def __init__(
self,
start_angle: float = 0,
angle: float = TAU / 4,
radius: float = 1.0,
n_components: int = 8,
arc_center: np_vector = ORIGIN,
**kwargs
):
self.start_angle = start_angle
self.angle = angle
VMobject.__init__(self, **kwargs)
super().__init__(**kwargs)
def init_points(self) -> None:
self.set_points(Arc.create_quadratic_bezier_points(
angle=self.angle,
start_angle=self.start_angle,
n_components=self.n_components
angle=angle,
start_angle=start_angle,
n_components=n_components
))
self.scale(self.radius, about_point=ORIGIN)
self.shift(self.arc_center)
self.scale(radius, about_point=ORIGIN)
self.shift(arc_center)
@staticmethod
def create_quadratic_bezier_points(
angle: float,
start_angle: float = 0,
n_components: int = 8
) -> np.ndarray:
) -> np_vector:
samples = np.array([
[np.cos(a), np.sin(a), 0]
for a in np.linspace(
@ -254,7 +244,7 @@ class Arc(TipableVMobject):
points[2::3] = samples[2::2]
return points
def get_arc_center(self) -> np.ndarray:
def get_arc_center(self) -> np_vector:
"""
Looks at the normals to the first two
anchors, and finds their intersection points
@ -277,7 +267,7 @@ class Arc(TipableVMobject):
angle = angle_of_vector(self.get_end() - self.get_arc_center())
return angle % TAU
def move_arc_center_to(self, point: np.ndarray):
def move_arc_center_to(self, point: np_vector):
self.shift(point - self.get_arc_center())
return self
@ -285,8 +275,8 @@ class Arc(TipableVMobject):
class ArcBetweenPoints(Arc):
def __init__(
self,
start: np.ndarray,
end: np.ndarray,
start: np_vector,
end: np_vector,
angle: float = TAU / 4,
**kwargs
):
@ -299,34 +289,39 @@ class ArcBetweenPoints(Arc):
class CurvedArrow(ArcBetweenPoints):
def __init__(
self,
start_point: np.ndarray,
end_point: np.ndarray,
start_point: np_vector,
end_point: np_vector,
**kwargs
):
ArcBetweenPoints.__init__(self, start_point, end_point, **kwargs)
super().__init__(start_point, end_point, **kwargs)
self.add_tip()
class CurvedDoubleArrow(CurvedArrow):
def __init__(
self,
start_point: np.ndarray,
end_point: np.ndarray,
start_point: np_vector,
end_point: np_vector,
**kwargs
):
CurvedArrow.__init__(self, start_point, end_point, **kwargs)
super().__init__(start_point, end_point, **kwargs)
self.add_tip(at_start=True)
class Circle(Arc):
CONFIG = {
"color": RED,
"close_new_points": True,
"anchors_span_full_range": False
}
def __init__(self, start_angle: float = 0, **kwargs):
Arc.__init__(self, start_angle, TAU, **kwargs)
def __init__(
self,
start_angle: float = 0,
stroke_color: ManimColor = RED,
close_new_points: bool = True,
**kwargs
):
super().__init__(
start_angle, TAU,
stroke_color=stroke_color,
close_new_points=close_new_points,
**kwargs
)
def surround(
self,
@ -335,15 +330,12 @@ class Circle(Arc):
stretch: bool = False,
buff: float = MED_SMALL_BUFF
):
# Ignores dim_to_match and stretch; result will always be a circle
# TODO: Perhaps create an ellipse class to handle singele-dimension stretching
self.replace(mobject, dim_to_match, stretch)
self.stretch((self.get_width() + 2 * buff) / self.get_width(), 0)
self.stretch((self.get_height() + 2 * buff) / self.get_height(), 1)
return self
def point_at_angle(self, angle: float) -> np.ndarray:
def point_at_angle(self, angle: float) -> np_vector:
start_angle = self.get_start_angle()
return self.point_from_proportion(
((angle - start_angle) % TAU) / TAU
@ -354,55 +346,75 @@ class Circle(Arc):
class Dot(Circle):
CONFIG = {
"radius": DEFAULT_DOT_RADIUS,
"stroke_width": 0,
"fill_opacity": 1.0,
"color": WHITE
}
def __init__(self, point: np.ndarray = ORIGIN, **kwargs):
super().__init__(arc_center=point, **kwargs)
def __init__(
self,
point: np_vector = ORIGIN,
radius: float = DEFAULT_DOT_RADIUS,
stroke_width: float = 0.0,
fill_opacity: float = 1.0,
color: ManimColor = WHITE,
**kwargs
):
super().__init__(
arc_center=point,
radius=radius,
stroke_width=stroke_width,
fill_opacity=fill_opacity,
color=color,
**kwargs
)
class SmallDot(Dot):
CONFIG = {
"radius": DEFAULT_SMALL_DOT_RADIUS,
}
def __init__(
self,
point: np_vector = ORIGIN,
radius: float = DEFAULT_SMALL_DOT_RADIUS,
**kwargs
):
super().__init__(point, radius=radius, **kwargs)
class Ellipse(Circle):
CONFIG = {
"width": 2,
"height": 1
}
def __init__(self, **kwargs):
def __init__(
self,
width: float = 2.0,
height: float = 1.0,
**kwargs
):
super().__init__(**kwargs)
self.set_width(self.width, stretch=True)
self.set_height(self.height, stretch=True)
self.set_width(width, stretch=True)
self.set_height(height, stretch=True)
class AnnularSector(Arc):
CONFIG = {
"inner_radius": 1,
"outer_radius": 2,
"angle": TAU / 4,
"start_angle": 0,
"fill_opacity": 1,
"stroke_width": 0,
"color": WHITE,
}
class AnnularSector(VMobject):
def __init__(
self,
angle: float = TAU / 4,
start_angle: float = 0.0,
inner_radius: float = 1.0,
outer_radius: float = 2.0,
arc_center: np_vector = ORIGIN,
color: ManimColor = GREY_A,
fill_opacity: float = 1.0,
stroke_width: float = 0.0,
**kwargs,
):
super().__init__(
color=color,
fill_opacity=fill_opacity,
stroke_width=stroke_width,
)
def init_points(self):
# Initialize points
inner_arc, outer_arc = [
Arc(
start_angle=self.start_angle,
angle=self.angle,
start_angle=start_angle,
angle=angle,
radius=radius,
arc_center=self.arc_center,
arc_center=arc_center,
)
for radius in (self.inner_radius, self.outer_radius)
for radius in (inner_radius, outer_radius)
]
outer_arc.reverse_points()
self.append_points(inner_arc.get_points())
@ -412,56 +424,64 @@ class AnnularSector(Arc):
class Sector(AnnularSector):
CONFIG = {
"outer_radius": 1,
"inner_radius": 0
}
def __init__(
self,
angle: float = TAU / 4,
radius: float = 1.0,
**kwargs
):
super().__init__(
angle,
inner_radius=0,
outer_radius=radius,
**kwargs
)
class Annulus(Circle):
CONFIG = {
"inner_radius": 1,
"outer_radius": 2,
"fill_opacity": 1,
"stroke_width": 0,
"color": WHITE,
"mark_paths_closed": False,
}
class Annulus(VMobject):
def __init__(
self,
inner_radius: float = 1.0,
outer_radius: float = 2.0,
fill_opacity: float = 1.0,
stroke_width: float = 0.0,
color: ManimColor = GREY_A,
center: np_vector = ORIGIN,
**kwargs,
):
super().__init__(
color=color,
fill_opacity=fill_opacity,
stroke_width=stroke_width,
)
def init_points(self):
self.radius = self.outer_radius
outer_circle = Circle(radius=self.outer_radius)
inner_circle = Circle(radius=self.inner_radius)
self.radius = outer_radius
outer_circle = Circle(radius=outer_radius)
inner_circle = Circle(radius=inner_radius)
inner_circle.reverse_points()
self.append_points(outer_circle.get_points())
self.append_points(inner_circle.get_points())
self.shift(self.arc_center)
self.shift(center)
class Line(TipableVMobject):
CONFIG = {
"buff": 0,
# Angle of arc specified here
"path_arc": 0,
}
def __init__(
self,
start: np.ndarray = LEFT,
end: np.ndarray = RIGHT,
start: np_vector | Mobject = LEFT,
end: np_vector | Mobject = RIGHT,
buff: float = 0.0,
path_arc: float = 0.0,
**kwargs
):
digest_config(self, kwargs)
self.set_start_and_end_attrs(start, end)
super().__init__(**kwargs)
def init_points(self) -> None:
self.set_points_by_ends(self.start, self.end, self.buff, self.path_arc)
self.path_arc = path_arc
self.set_start_and_end_attrs(start, end)
self.set_points_by_ends(self.start, self.end, buff, path_arc)
def set_points_by_ends(
self,
start: np.ndarray,
end: np.ndarray,
start: np_vector,
end: np_vector,
buff: float = 0,
path_arc: float = 0
):
@ -497,7 +517,7 @@ class Line(TipableVMobject):
self.path_arc = new_value
self.init_points()
def set_start_and_end_attrs(self, start: np.ndarray, end: np.ndarray):
def set_start_and_end_attrs(self, start: np_vector | Mobject, end: np_vector | Mobject):
# If either start or end are Mobjects, this
# gives their centers
rough_start = self.pointify(start)
@ -511,9 +531,9 @@ class Line(TipableVMobject):
def pointify(
self,
mob_or_point: Mobject | np.ndarray,
direction: np.ndarray | None = None
) -> np.ndarray:
mob_or_point: Mobject | np_vector,
direction: np_vector | None = None
) -> np_vector:
"""
Take an argument passed into Line (or subclass) and turn
it into a 3d point.
@ -530,7 +550,7 @@ class Line(TipableVMobject):
result[:len(point)] = point
return result
def put_start_and_end_on(self, start: np.ndarray, end: np.ndarray):
def put_start_and_end_on(self, start: np_vector, end: np_vector):
curr_start, curr_end = self.get_start_and_end()
if np.isclose(curr_start, curr_end).all():
# Handle null lines more gracefully
@ -538,16 +558,16 @@ class Line(TipableVMobject):
return self
return super().put_start_and_end_on(start, end)
def get_vector(self) -> np.ndarray:
def get_vector(self) -> np_vector:
return self.get_end() - self.get_start()
def get_unit_vector(self) -> np.ndarray:
def get_unit_vector(self) -> np_vector:
return normalize(self.get_vector())
def get_angle(self) -> float:
return angle_of_vector(self.get_vector())
def get_projection(self, point: np.ndarray) -> np.ndarray:
def get_projection(self, point: np_vector) -> np_vector:
"""
Return projection of a point onto the line
"""
@ -558,7 +578,7 @@ class Line(TipableVMobject):
def get_slope(self) -> float:
return np.tan(self.get_angle())
def set_angle(self, angle: float, about_point: np.ndarray | None = None):
def set_angle(self, angle: float, about_point: np_vector | None = None):
if about_point is None:
about_point = self.get_start()
self.rotate(
@ -571,118 +591,124 @@ class Line(TipableVMobject):
self.scale(length / self.get_length(), **kwargs)
return self
def get_arc_length(self) -> float:
arc_len = get_norm(self.get_vector())
if self.path_arc > 0:
arc_len *= self.path_arc / (2 * math.sin(self.path_arc / 2))
return arc_len
class DashedLine(Line):
CONFIG = {
"dash_length": DEFAULT_DASH_LENGTH,
"dash_spacing": None,
"positive_space_ratio": 0.5,
}
def __init__(
self,
start: np_vector = LEFT,
end: np_vector = RIGHT,
dash_length: float = DEFAULT_DASH_LENGTH,
positive_space_ratio: float = 0.5,
**kwargs
):
super().__init__(start, end, **kwargs)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
ps_ratio = self.positive_space_ratio
num_dashes = self.calculate_num_dashes(ps_ratio)
num_dashes = self.calculate_num_dashes(dash_length, positive_space_ratio)
dashes = DashedVMobject(
self,
num_dashes=num_dashes,
positive_space_ratio=ps_ratio
positive_space_ratio=positive_space_ratio
)
self.clear_points()
self.add(*dashes)
def calculate_num_dashes(self, positive_space_ratio: float) -> int:
def calculate_num_dashes(self, dash_length: float, positive_space_ratio: float) -> int:
try:
full_length = self.dash_length / positive_space_ratio
full_length = dash_length / positive_space_ratio
return int(np.ceil(self.get_length() / full_length))
except ZeroDivisionError:
return 1
def calculate_positive_space_ratio(self) -> float:
return fdiv(
self.dash_length,
self.dash_length + self.dash_spacing,
)
def get_start(self) -> np.ndarray:
def get_start(self) -> np_vector:
if len(self.submobjects) > 0:
return self.submobjects[0].get_start()
else:
return Line.get_start(self)
def get_end(self) -> np.ndarray:
def get_end(self) -> np_vector:
if len(self.submobjects) > 0:
return self.submobjects[-1].get_end()
else:
return Line.get_end(self)
def get_first_handle(self) -> np.ndarray:
def get_first_handle(self) -> np_vector:
return self.submobjects[0].get_points()[1]
def get_last_handle(self) -> np.ndarray:
def get_last_handle(self) -> np_vector:
return self.submobjects[-1].get_points()[-2]
class TangentLine(Line):
CONFIG = {
"length": 1,
"d_alpha": 1e-6
}
def __init__(self, vmob: VMobject, alpha: float, **kwargs):
digest_config(self, kwargs)
da = self.d_alpha
a1 = clip(alpha - da, 0, 1)
a2 = clip(alpha + da, 0, 1)
def __init__(
self,
vmob: VMobject,
alpha: float,
length: float = 2,
d_alpha: float = 1e-6,
**kwargs
):
a1 = clip(alpha - d_alpha, 0, 1)
a2 = clip(alpha + d_alpha, 0, 1)
super().__init__(vmob.pfp(a1), vmob.pfp(a2), **kwargs)
self.scale(self.length / self.get_length())
self.scale(length / self.get_length())
class Elbow(VMobject):
CONFIG = {
"width": 0.2,
"angle": 0,
}
def __init__(self, **kwargs):
def __init__(
self,
width: float = 0.2,
angle: float = 0,
**kwargs
):
super().__init__(**kwargs)
self.set_points_as_corners([UP, UP + RIGHT, RIGHT])
self.set_width(self.width, about_point=ORIGIN)
self.rotate(self.angle, about_point=ORIGIN)
self.set_points_as_corners([UP, UR, RIGHT])
self.set_width(width, about_point=ORIGIN)
self.rotate(angle, about_point=ORIGIN)
class Arrow(Line):
CONFIG = {
"color": GREY_A,
"stroke_width": 5,
"tip_width_ratio": 4,
"width_to_tip_len": 0.0075,
"max_tip_length_to_length_ratio": 0.3,
"max_width_to_length_ratio": 10,
"buff": 0.25,
}
def __init__(
self,
start: np_vector | Mobject,
end: np_vector | Mobject,
color: ManimColor = GREY_A,
stroke_width: float = 5,
buff: float = 0.25,
tip_width_ratio: float = 5,
width_to_tip_len: float = 0.0075,
max_tip_length_to_length_ratio: float = 0.3,
max_width_to_length_ratio: float = 8.0,
**kwargs,
):
self.tip_width_ratio = tip_width_ratio
self.width_to_tip_len = width_to_tip_len
self.max_tip_length_to_length_ratio = max_tip_length_to_length_ratio
self.max_width_to_length_ratio = max_width_to_length_ratio
self.max_stroke_width = stroke_width
super().__init__(
start, end,
color=color,
stroke_width=stroke_width,
buff=buff,
**kwargs
)
def set_points_by_ends(
self,
start: np.ndarray,
end: np.ndarray,
start: np_vector,
end: np_vector,
buff: float = 0,
path_arc: float = 0
):
super().set_points_by_ends(start, end, buff, path_arc)
self.insert_tip_anchor()
return self
def init_colors(self) -> None:
super().init_colors()
self.create_tip_with_stroke_width()
def get_arc_length(self) -> float:
# Push up into Line?
arc_len = get_norm(self.get_vector())
if self.path_arc > 0:
arc_len *= self.path_arc / (2 * math.sin(self.path_arc / 2))
return arc_len
return self
def insert_tip_anchor(self):
prev_end = self.get_end()
@ -697,6 +723,8 @@ class Arrow(Line):
return self
def create_tip_with_stroke_width(self):
if not self.has_points():
return self
width = min(
self.max_stroke_width,
self.max_width_to_length_ratio * self.get_length(),
@ -713,11 +741,9 @@ class Arrow(Line):
def reset_tip(self):
self.set_points_by_ends(
self.get_start(),
self.get_end(),
path_arc=self.path_arc,
self.get_start(), self.get_end(),
path_arc=self.path_arc
)
self.create_tip_with_stroke_width()
return self
def set_stroke(
@ -729,7 +755,7 @@ class Arrow(Line):
super().set_stroke(color=color, width=width, *args, **kwargs)
if isinstance(width, numbers.Number):
self.max_stroke_width = width
self.reset_tip()
self.create_tip_with_stroke_width()
return self
def _handle_scale_side_effects(self, scale_factor: float):
@ -737,22 +763,39 @@ class Arrow(Line):
class FillArrow(Line):
CONFIG = {
"fill_color": GREY_A,
"fill_opacity": 1,
"stroke_width": 0,
"buff": MED_SMALL_BUFF,
"thickness": 0.05,
"tip_width_ratio": 5,
"tip_angle": PI / 3,
"max_tip_length_to_length_ratio": 0.5,
"max_width_to_length_ratio": 0.1,
}
def __init__(
self,
start: np_vector | Mobject = LEFT,
end: np_vector | Mobject = LEFT,
fill_color: ManimColor = GREY_A,
fill_opacity: float = 1.0,
stroke_width: float = 0.0,
buff: float = MED_SMALL_BUFF,
thickness: float = 0.05,
tip_width_ratio: float = 5,
tip_angle: float = PI / 3,
max_tip_length_to_length_ratio: float = 0.5,
max_width_to_length_ratio: float = 0.1,
**kwargs,
):
self.thickness = thickness
self.tip_width_ratio = tip_width_ratio
self.tip_angle = tip_angle
self.max_tip_length_to_length_ratio = max_tip_length_to_length_ratio
self.max_width_to_length_ratio = max_width_to_length_ratio
super().__init__(
start, end,
fill_color=fill_color,
fill_opacity=fill_opacity,
stroke_width=stroke_width,
buff=buff,
**kwargs
)
def set_points_by_ends(
self,
start: np.ndarray,
end: np.ndarray,
start: np_vector,
end: np_vector,
buff: float = 0,
path_arc: float = 0
) -> None:
@ -806,7 +849,7 @@ class FillArrow(Line):
self.append_points(points2)
self.add_line_to(points1[0])
if length > 0:
if length > 0 and self.get_length() > 0:
# Final correction
super().scale(length / self.get_length())
@ -824,15 +867,15 @@ class FillArrow(Line):
)
return self
def get_start(self) -> np.ndarray:
def get_start(self) -> np_vector:
nppc = self.n_points_per_curve
points = self.get_points()
return (points[0] + points[-nppc]) / 2
def get_end(self) -> np.ndarray:
def get_end(self) -> np_vector:
return self.get_points()[self.tip_index]
def put_start_and_end_on(self, start: np.ndarray, end: np.ndarray):
def put_start_and_end_on(self, start: np_vector, end: np_vector):
self.set_points_by_ends(start, end, buff=0, path_arc=self.path_arc)
return self
@ -853,45 +896,36 @@ class FillArrow(Line):
class Vector(Arrow):
CONFIG = {
"buff": 0,
}
def __init__(self, direction: np.ndarray = RIGHT, **kwargs):
def __init__(
self,
direction: np_vector = RIGHT,
buff: float = 0.0,
**kwargs
):
if len(direction) == 2:
direction = np.hstack([direction, 0])
super().__init__(ORIGIN, direction, **kwargs)
class DoubleArrow(Arrow):
def __init__(self, *args, **kwargs):
Arrow.__init__(self, *args, **kwargs)
self.add_tip(at_start=True)
super().__init__(ORIGIN, direction, buff=buff, **kwargs)
class CubicBezier(VMobject):
def __init__(
self,
a0: np.ndarray,
h0: np.ndarray,
h1: np.ndarray,
a1: np.ndarray,
a0: np_vector,
h0: np_vector,
h1: np_vector,
a1: np_vector,
**kwargs
):
VMobject.__init__(self, **kwargs)
super().__init__(**kwargs)
self.add_cubic_bezier_curve(a0, h0, h1, a1)
class Polygon(VMobject):
def __init__(self, *vertices: np.ndarray, **kwargs):
self.vertices = vertices
def __init__(self, *vertices: np_vector, **kwargs):
super().__init__(**kwargs)
self.set_points_as_corners([*vertices, vertices[0]])
def init_points(self) -> None:
verts = self.vertices
self.set_points_as_corners([*verts, verts[0]])
def get_vertices(self) -> list[np.ndarray]:
def get_vertices(self) -> list[np_vector]:
return self.get_start_anchors()
def round_corners(self, radius: float | None = None):
@ -940,22 +974,23 @@ class Polygon(VMobject):
return self
class Polyline(Polygon):
def init_points(self) -> None:
self.set_points_as_corners(self.vertices)
class Polyline(VMobject):
def __init__(self, *vertices: np_vector, **kwargs):
super().__init__(**kwargs)
self.set_points_as_corners(vertices)
class RegularPolygon(Polygon):
CONFIG = {
"start_angle": None,
}
def __init__(self, n: int = 6, **kwargs):
digest_config(self, kwargs, locals())
if self.start_angle is None:
# 0 for odd, 90 for even
self.start_angle = (n % 2) * 90 * DEGREES
start_vect = rotate_vector(RIGHT, self.start_angle)
def __init__(
self,
n: int = 6,
radius: float = 1.0,
start_angle: float | None = None,
**kwargs
):
# Defaults to 0 for odd, 90 for even
start_angle = start_angle or (n % 2) * 90 * DEGREES
start_vect = rotate_vector(radius * RIGHT, start_angle)
vertices = compass_directions(n, start_vect)
super().__init__(*vertices, **kwargs)
@ -966,36 +1001,42 @@ class Triangle(RegularPolygon):
class ArrowTip(Triangle):
CONFIG = {
"fill_opacity": 1,
"fill_color": WHITE,
"stroke_width": 0,
"width": DEFAULT_ARROW_TIP_WIDTH,
"length": DEFAULT_ARROW_TIP_LENGTH,
"angle": 0,
"tip_style": 0, # triangle=0, inner_smooth=1, dot=2
}
def __init__(self, **kwargs):
Triangle.__init__(self, start_angle=0, **kwargs)
self.set_height(self.width)
self.set_width(self.length, stretch=True)
if self.tip_style == 1:
self.set_height(self.length * 0.9, stretch=True)
self.data["points"][4] += np.array([0.6 * self.length, 0, 0])
elif self.tip_style == 2:
h = self.length / 2
def __init__(
self,
angle: float = 0,
width: float = DEFAULT_ARROW_TIP_WIDTH,
length: float = DEFAULT_ARROW_TIP_LENGTH,
fill_opacity: float = 1.0,
fill_color: ManimColor = WHITE,
stroke_width: float = 0.0,
tip_style: int = 0, # triangle=0, inner_smooth=1, dot=2
**kwargs
):
super().__init__(
start_angle=0,
fill_opacity=fill_opacity,
fill_color=fill_color,
stroke_width=stroke_width,
**kwargs
)
self.set_height(width)
self.set_width(length, stretch=True)
if tip_style == 1:
self.set_height(length * 0.9, stretch=True)
self.data["points"][4] += np.array([0.6 * length, 0, 0])
elif tip_style == 2:
h = length / 2
self.clear_points()
self.data["points"] = Dot().set_width(h).get_points()
self.rotate(self.angle)
self.rotate(angle)
def get_base(self) -> np.ndarray:
def get_base(self) -> np_vector:
return self.point_from_proportion(0.5)
def get_tip_point(self) -> np.ndarray:
def get_tip_point(self) -> np_vector:
return self.get_points()[0]
def get_vector(self) -> np.ndarray:
def get_vector(self) -> np_vector:
return self.get_tip_point() - self.get_base()
def get_angle(self) -> float:
@ -1006,42 +1047,30 @@ class ArrowTip(Triangle):
class Rectangle(Polygon):
CONFIG = {
"color": WHITE,
"width": 4.0,
"height": 2.0,
"mark_paths_closed": True,
"close_new_points": True,
}
def __init__(
self,
width: float | None = None,
height: float | None = None,
width: float = 4.0,
height: float = 2.0,
color: ManimColor = WHITE,
**kwargs
):
Polygon.__init__(self, UR, UL, DL, DR, **kwargs)
if width is None:
width = self.width
if height is None:
height = self.height
super().__init__(UR, UL, DL, DR, color=color, **kwargs)
self.set_width(width, stretch=True)
self.set_height(height, stretch=True)
class Square(Rectangle):
def __init__(self, side_length: float = 2.0, **kwargs):
self.side_length = side_length
super().__init__(side_length, side_length, **kwargs)
class RoundedRectangle(Rectangle):
CONFIG = {
"corner_radius": 0.5,
}
def __init__(self, **kwargs):
Rectangle.__init__(self, **kwargs)
self.round_corners(self.corner_radius)
def __init__(
self,
width: float = 4.0,
height: float = 2.0,
corner_radius: float = 0.5,
**kwargs
):
super().__init__(width, height, **kwargs)
self.round_corners(corner_radius)