Skip to content

Commit 551c42a

Browse files
authored
Generalize ActivableMobject to non-vector objs (#29)
* Generalize Activable to non-vector objs * Add ImageAutoActivable * Rename file to vgroupactivable to groupactivable * Fix incorrect opacity variable * Reorganize class definitions * Fix typing * Fix pre commits
1 parent 8bfb2dd commit 551c42a

File tree

5 files changed

+158
-44
lines changed

5 files changed

+158
-44
lines changed

src/powermanim/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
from .components import (
2-
Activable,
3-
AutoActivable,
42
ChartBars,
53
DirectionalArrow,
4+
GroupActivable,
5+
ImageAutoActivable,
66
Invariance,
77
NumberSlider,
88
PowerManim,
9-
VGroupActivable,
9+
VActivable,
10+
VAutoActivable,
1011
)
1112
from .layouts import ArrangedBullets, Bullet, MathBullet, SwitchPalette
1213
from .templates import BulletList, Reference, SectionTitle
1314

1415
__all__ = [
15-
"AutoActivable",
16+
"ImageAutoActivable",
17+
"VAutoActivable",
1618
"ChartBars",
1719
"DirectionalArrow",
18-
"Activable",
20+
"VActivable",
1921
"Invariance",
2022
"NumberSlider",
2123
"PowerManim",
22-
"VGroupActivable",
24+
"GroupActivable",
2325
"ArrangedBullets",
2426
"SwitchPalette",
2527
"Bullet",

src/powermanim/components/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
from .chartbars import ChartBars
22
from .directionalarrow import DirectionalArrow
3+
from .groupactivable import GroupActivable, ImageAutoActivable, VActivable, VAutoActivable
34
from .invariance import Invariance
45
from .logo import PowerManim
56
from .numberslider import NumberSlider
6-
from .vgroupactivable import Activable, AutoActivable, VGroupActivable
77

88
__all__ = [
9-
"AutoActivable",
9+
"ImageAutoActivable",
10+
"VAutoActivable",
1011
"ChartBars",
1112
"DirectionalArrow",
12-
"Activable",
13+
"VActivable",
1314
"Invariance",
1415
"NumberSlider",
1516
"PowerManim",
16-
"VGroupActivable",
17+
"GroupActivable",
1718
]

src/powermanim/components/vgroupactivable.py renamed to src/powermanim/components/groupactivable.py

Lines changed: 137 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1+
import abc
12
import typing as T
23
from collections import defaultdict
34

45
from manim import *
56

67

7-
class Activable(VGroup):
8+
class ActivableMobject(Group):
89
def __init__(
910
self,
10-
inactive_vmobject: VMobject,
11-
active_vmobject: VMobject,
11+
inactive_vmobject: Mobject,
12+
active_vmobject: Mobject,
1213
start_active: bool = False,
1314
activation_anim_run_time: float = 1.0,
1415
deactivation_anim_run_time: float = 1.0,
1516
group: T.Optional[int] = None,
1617
) -> None:
17-
self.active_stroke_opacities = [x.stroke_opacity for x in active_vmobject.get_family()]
18-
self.active_fill_opacities = [x.fill_opacity for x in active_vmobject.get_family()]
19-
self.inactive_stroke_opacities = [x.stroke_opacity for x in inactive_vmobject.get_family()]
20-
self.inactive_fill_opacities = [x.fill_opacity for x in inactive_vmobject.get_family()]
21-
18+
self.is_active = start_active
19+
self.activation_anim_run_time = activation_anim_run_time
20+
self.deactivation_anim_run_time = deactivation_anim_run_time
21+
self.group = group
2222
super().__init__(
2323
active_vmobject.copy() if start_active else inactive_vmobject.copy(),
2424
active_vmobject.set_opacity(0),
@@ -34,14 +34,55 @@ def __init__(
3434
self.group = group
3535

3636
@property
37-
def obj(self) -> VMobject:
37+
def obj(self) -> Mobject:
3838
return self[0]
3939

40-
def get_activation_anim(self) -> Animation:
40+
@abc.abstractmethod
41+
def _get_activation_anim(self):
42+
raise NotImplementedError
43+
44+
def activate(self) -> Animation:
4145
if self.is_active:
4246
raise ValueError("The object is already active.")
4347
self.is_active = True
48+
return self._get_activation_anim()
49+
50+
@abc.abstractmethod
51+
def _get_deactivation_anim(self):
52+
raise NotImplementedError
53+
54+
def deactivate(self) -> Animation:
55+
if not self.is_active:
56+
raise ValueError("The object is already inactive.")
57+
self.is_active = False
58+
return self._get_deactivation_anim()
59+
4460

61+
class VActivable(ActivableMobject):
62+
def __init__(
63+
self,
64+
inactive_vmobject: VMobject,
65+
active_vmobject: VMobject,
66+
start_active: bool = False,
67+
activation_anim_run_time: float = 1.0,
68+
deactivation_anim_run_time: float = 1.0,
69+
group: T.Optional[int] = None,
70+
) -> None:
71+
self.active_stroke_opacities = [x.stroke_opacity for x in active_vmobject.get_family()]
72+
self.active_fill_opacities = [x.fill_opacity for x in active_vmobject.get_family()]
73+
self.inactive_stroke_opacities = [x.stroke_opacity for x in inactive_vmobject.get_family()]
74+
self.inactive_fill_opacities = [x.fill_opacity for x in inactive_vmobject.get_family()]
75+
76+
super().__init__(
77+
inactive_vmobject=inactive_vmobject,
78+
active_vmobject=active_vmobject,
79+
start_active=start_active,
80+
activation_anim_run_time=activation_anim_run_time,
81+
deactivation_anim_run_time=deactivation_anim_run_time,
82+
group=group,
83+
)
84+
85+
def _get_activation_anim(self) -> Animation:
4586
target = self.active_vmobject.copy()
4687
for subobj, stroke_opacity, fill_opacity in zip(
4788
target.get_family(), self.active_stroke_opacities, self.active_fill_opacities
@@ -51,11 +92,7 @@ def get_activation_anim(self) -> Animation:
5192
subobj.set_stroke(opacity=stroke_opacity, family=False, background=True)
5293
return Transform(self.obj, target, run_time=self.activation_anim_run_time)
5394

54-
def get_deactivation_anim(self) -> Animation:
55-
if not self.is_active:
56-
raise ValueError("The object is already inactive.")
57-
self.is_active = False
58-
95+
def _get_deactivation_anim(self) -> Animation:
5996
target = self.inactive_vmobject.copy()
6097
for subobj, stroke_opacity, fill_opacity in zip(
6198
target.get_family(), self.inactive_stroke_opacities, self.inactive_fill_opacities
@@ -67,7 +104,7 @@ def get_deactivation_anim(self) -> Animation:
67104
return Transform(self.obj, target, run_time=self.deactivation_anim_run_time)
68105

69106

70-
class AutoActivable(Activable):
107+
class VAutoActivable(VActivable):
71108
def __init__(
72109
self,
73110
vmobject: VMobject,
@@ -78,6 +115,8 @@ def __init__(
78115
scale_active: T.Optional[float] = 1.1,
79116
scale_about_point=None,
80117
scale_about_edge=LEFT,
118+
activation_anim_run_time: float = 1,
119+
deactivation_anim_run_time: float = 1,
81120
group: T.Optional[int] = None,
82121
) -> None:
83122
"""Activable component that automatically creates the active and inactive VMobjects.
@@ -91,6 +130,8 @@ def __init__(
91130
scale_active (float): The scale of the active VMobject.
92131
scale_about_point (np.ndarray): The point to scale about.
93132
scale_about_edge (np.ndarray): The edge to scale about.
133+
activation_anim_run_time (float): The run time of the activation animation.
134+
deactivation_anim_run_time (float): The run time of the deactivation animation.
94135
group (int): The group to which the object belongs.
95136
"""
96137
self.active_fill_opacity = active_fill_opacity
@@ -114,15 +155,80 @@ def __init__(
114155
inactive_obj.set_fill(opacity=self.inactive_fill_opacity)
115156
if self.inactive_stroke_opacity is not None:
116157
inactive_obj.set_stroke(opacity=self.inactive_stroke_opacity)
117-
super().__init__(inactive_vmobject=inactive_obj, active_vmobject=active_obj, start_active=False, group=group)
158+
super().__init__(
159+
inactive_vmobject=inactive_obj,
160+
active_vmobject=active_obj,
161+
start_active=False,
162+
group=group,
163+
activation_anim_run_time=activation_anim_run_time,
164+
deactivation_anim_run_time=deactivation_anim_run_time,
165+
)
166+
167+
168+
class ImageAutoActivable(ActivableMobject):
169+
def __init__(
170+
self,
171+
vmobject: Mobject,
172+
active_opacity: T.Optional[float] = 1.0,
173+
inactive_opacity: T.Optional[float] = 0.35,
174+
scale_active: T.Optional[float] = 1.1,
175+
scale_about_point=None,
176+
scale_about_edge=ORIGIN,
177+
activation_anim_run_time: float = 1.0,
178+
deactivation_anim_run_time: float = 1.0,
179+
group: T.Optional[int] = None,
180+
) -> None:
181+
"""Activable component that automatically creates the active and inactive Mobjects.
182+
183+
Args:
184+
vmobject (Mobject): The object to activate or deactivate Mobject.
185+
active_opacity (float): The fill opacity of the active Mobject.
186+
inactive_opacity (float): The fill opacity of the inactive Mobject.
187+
scale_active (float): The scale of the active Mobject.
188+
scale_about_point (np.ndarray): The point to scale about.
189+
scale_about_edge (np.ndarray): The edge to scale about.
190+
activation_anim_run_time (float): The run time of the activation animation.
191+
deactivation_anim_run_time (float): The run time of the deactivation animation.
192+
group (int): The group to which the object belongs.
193+
"""
194+
self.active_opacity = active_opacity
195+
self.inactive_opacity = inactive_opacity
196+
self.scale_active = scale_active
197+
self.scale_about_point = scale_about_point
198+
self.scale_about_edge = scale_about_edge
199+
200+
active_obj = vmobject.copy()
201+
if self.scale_active is not None:
202+
active_obj.scale(self.scale_active, about_point=self.scale_about_point, about_edge=self.scale_about_edge)
203+
if self.active_opacity is not None:
204+
active_obj.set_opacity(self.active_opacity)
205+
206+
inactive_obj = vmobject.copy()
207+
if self.inactive_opacity is not None:
208+
inactive_obj.set_opacity(self.inactive_opacity)
118209

210+
super().__init__(
211+
inactive_vmobject=inactive_obj,
212+
active_vmobject=active_obj,
213+
start_active=False,
214+
activation_anim_run_time=activation_anim_run_time,
215+
deactivation_anim_run_time=deactivation_anim_run_time,
216+
group=group,
217+
)
119218

120-
class VGroupActivable(VGroup):
219+
def _get_activation_anim(self) -> Animation:
220+
return self.obj.animate.set_opacity(self.active_opacity)
221+
222+
def _get_deactivation_anim(self) -> Animation:
223+
return self.obj.animate.set_opacity(self.inactive_opacity)
224+
225+
226+
class GroupActivable(Group):
121227
def __init__(
122228
self,
123-
*args: VMobject,
229+
*args: Mobject,
124230
anim_lag_ratio: float = 0,
125-
**kwargs: VMobject,
231+
**kwargs: Mobject,
126232
) -> None:
127233
"""Group component that can activate a subset of its submobjects.
128234
@@ -131,7 +237,14 @@ def __init__(
131237
anim_lag_ratio (float): The lag ratio of the animation.
132238
**kwargs: Keyword arguments to be passed to the VGroup.
133239
"""
134-
args_activable = (AutoActivable(x) if not isinstance(x, Activable) else x for x in args)
240+
args_activable = (
241+
(
242+
(VAutoActivable(x) if isinstance(x, VMobject) else ImageAutoActivable(x))
243+
if not isinstance(x, ActivableMobject)
244+
else x
245+
)
246+
for x in args
247+
)
135248
super().__init__(*args_activable, **kwargs)
136249

137250
self.anim_lag_ratio = anim_lag_ratio
@@ -146,7 +259,7 @@ def __init__(
146259
if (None in groups) and len(set(groups)) != 1:
147260
raise ValueError("The groups must be specified for all or no bullets at all.")
148261

149-
self.group2items: Dict[int, T.Set[Activable]] = defaultdict(set)
262+
self.group2items: Dict[int, T.Set[ActivableMobject]] = defaultdict(set)
150263
for i, obj in enumerate(self.submobjects):
151264
group = obj.group
152265
if group is None:
@@ -177,14 +290,12 @@ def activate(self, indices: T.Union[int, T.Sequence[int]]) -> AnimationGroup:
177290
for to_activate in indices:
178291
if to_activate in self.previously_active_idxs:
179292
continue
180-
anims.append(AnimationGroup(*(x.get_activation_anim() for x in self.group2items[to_activate])))
293+
anims.append(AnimationGroup(*(x.activate() for x in self.group2items[to_activate])))
181294

182295
if self.previously_active_idxs:
183296
for previously_active_idx in self.previously_active_idxs:
184297
if previously_active_idx not in indices:
185-
anims.append(
186-
AnimationGroup(*(x.get_deactivation_anim() for x in self.group2items[previously_active_idx]))
187-
)
298+
anims.append(AnimationGroup(*(x.deactivate() for x in self.group2items[previously_active_idx])))
188299

189300
self.previously_active_idxs = indices
190301

src/powermanim/showcase/components/vgroupactivable.py renamed to src/powermanim/showcase/components/groupactivable.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
from manim import *
22

3-
from powermanim import VGroupActivable
4-
from powermanim.components.vgroupactivable import AutoActivable
3+
from powermanim import GroupActivable
4+
from powermanim.components.groupactivable import VAutoActivable
55
from powermanim.showcase.showcasescene import ShowcaseScene
66

77

88
class VGroupActivableShowcase(ShowcaseScene):
99
@staticmethod
1010
def showcasing():
11-
return VGroupActivable
11+
return GroupActivable
1212

1313
def construct(self):
1414
dots = [
@@ -25,9 +25,9 @@ def construct(self):
2525
)
2626
]
2727

28-
group = VGroupActivable(
28+
group = GroupActivable(
2929
*map(
30-
lambda x: AutoActivable(
30+
lambda x: VAutoActivable(
3131
x,
3232
active_fill_opacity=1,
3333
active_stroke_opacity=1,

src/powermanim/templates/bulletlist.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
from manim import *
44

5-
from powermanim import ArrangedBullets, AutoActivable, MathBullet, VGroupActivable
5+
from powermanim import ArrangedBullets, GroupActivable, MathBullet, VAutoActivable
66

77

8-
class BulletList(VGroupActivable):
8+
class BulletList(GroupActivable):
99
def __init__(
1010
self,
1111
*rows: T.Union[MathBullet, Tex, Text],
@@ -41,7 +41,7 @@ def __init__(
4141

4242
super().__init__(
4343
*(
44-
AutoActivable(
44+
VAutoActivable(
4545
x,
4646
active_fill_opacity=active_opacity,
4747
active_stroke_opacity=active_opacity,

0 commit comments

Comments
 (0)