1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132from __future__ import annotations
from colour import Color
from manimlib.config import manim_config
from manimlib.constants import BLACK, RED, YELLOW, DEFAULT_MOBJECT_COLOR
from manimlib.constants import DL, DOWN, DR, LEFT, RIGHT, UL, UR
from manimlib.constants import SMALL_BUFF
from manimlib.mobject.geometry import Line
from manimlib.mobject.geometry import Rectangle
from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.mobject.types.vectorized_mobject import VMobject
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Sequence
from manimlib.mobject.mobject import Mobject
from manimlib.typing import ManimColor, Self
class SurroundingRectangle(Rectangle):
def __init__(
self,
mobject: Mobject,
buff: float = SMALL_BUFF,
color: ManimColor = YELLOW,
**kwargs
):
super().__init__(color=color, **kwargs)
self.buff = buff
self.surround(mobject)
if mobject.is_fixed_in_frame():
self.fix_in_frame()
def surround(self, mobject, buff=None) -> Self:
self.mobject = mobject
self.buff = buff if buff is not None else self.buff
super().surround(mobject, self.buff)
return self
def set_buff(self, buff) -> Self:
self.buff = buff
self.surround(self.mobject)
return self
class BackgroundRectangle(SurroundingRectangle):
def __init__(
self,
mobject: Mobject,
color: ManimColor = None,
stroke_width: float = 0,
stroke_opacity: float = 0,
fill_opacity: float = 0.75,
buff: float = 0,
**kwargs
):
if color is None:
color = manim_config.camera.background_color
super().__init__(
mobject,
color=color,
stroke_width=stroke_width,
stroke_opacity=stroke_opacity,
fill_opacity=fill_opacity,
buff=buff,
**kwargs
)
self.original_fill_opacity = fill_opacity
def pointwise_become_partial(self, mobject: Mobject, a: float, b: float) -> Self:
self.set_fill(opacity=b * self.original_fill_opacity)
return self
def set_style(
self,
stroke_color: ManimColor | None = None,
stroke_width: float | None = None,
fill_color: ManimColor | None = None,
fill_opacity: float | None = None,
family: bool = True,
**kwargs
) -> Self:
# Unchangeable style, except for fill_opacity
VMobject.set_style(
self,
stroke_color=BLACK,
stroke_width=0,
fill_color=BLACK,
fill_opacity=fill_opacity
)
return self
def get_fill_color(self) -> Color:
return Color(self.color)
class Cross(VGroup):
def __init__(
self,
mobject: Mobject,
stroke_color: ManimColor = RED,
stroke_width: float | Sequence[float] = [0, 6, 0],
**kwargs
):
super().__init__(
Line(UL, DR),
Line(UR, DL),
)
self.insert_n_curves(20)
self.replace(mobject, stretch=True)
self.set_stroke(stroke_color, width=stroke_width)
class Underline(Line):
def __init__(
self,
mobject: Mobject,
buff: float = SMALL_BUFF,
stroke_color=DEFAULT_MOBJECT_COLOR,
stroke_width: float | Sequence[float] = [0, 3, 3, 0],
stretch_factor=1.2,
**kwargs
):
super().__init__(LEFT, RIGHT, **kwargs)
if not isinstance(stroke_width, (float, int)):
self.insert_n_curves(len(stroke_width) - 2)
self.set_stroke(stroke_color, stroke_width)
self.set_width(mobject.get_width() * stretch_factor)
self.next_to(mobject, DOWN, buff=buff)