Skip to content

Commit

Permalink
Generalize ActivableMobject to non-vector objs (#29)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
lucmos committed Mar 25, 2024
1 parent 8bfb2dd commit 551c42a
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 44 deletions.
14 changes: 8 additions & 6 deletions src/powermanim/__init__.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
from .components import (
Activable,
AutoActivable,
ChartBars,
DirectionalArrow,
GroupActivable,
ImageAutoActivable,
Invariance,
NumberSlider,
PowerManim,
VGroupActivable,
VActivable,
VAutoActivable,
)
from .layouts import ArrangedBullets, Bullet, MathBullet, SwitchPalette
from .templates import BulletList, Reference, SectionTitle

__all__ = [
"AutoActivable",
"ImageAutoActivable",
"VAutoActivable",
"ChartBars",
"DirectionalArrow",
"Activable",
"VActivable",
"Invariance",
"NumberSlider",
"PowerManim",
"VGroupActivable",
"GroupActivable",
"ArrangedBullets",
"SwitchPalette",
"Bullet",
Expand Down
9 changes: 5 additions & 4 deletions src/powermanim/components/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
from .chartbars import ChartBars
from .directionalarrow import DirectionalArrow
from .groupactivable import GroupActivable, ImageAutoActivable, VActivable, VAutoActivable
from .invariance import Invariance
from .logo import PowerManim
from .numberslider import NumberSlider
from .vgroupactivable import Activable, AutoActivable, VGroupActivable

__all__ = [
"AutoActivable",
"ImageAutoActivable",
"VAutoActivable",
"ChartBars",
"DirectionalArrow",
"Activable",
"VActivable",
"Invariance",
"NumberSlider",
"PowerManim",
"VGroupActivable",
"GroupActivable",
]
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import abc
import typing as T
from collections import defaultdict

from manim import *


class Activable(VGroup):
class ActivableMobject(Group):
def __init__(
self,
inactive_vmobject: VMobject,
active_vmobject: VMobject,
inactive_vmobject: Mobject,
active_vmobject: Mobject,
start_active: bool = False,
activation_anim_run_time: float = 1.0,
deactivation_anim_run_time: float = 1.0,
group: T.Optional[int] = None,
) -> None:
self.active_stroke_opacities = [x.stroke_opacity for x in active_vmobject.get_family()]
self.active_fill_opacities = [x.fill_opacity for x in active_vmobject.get_family()]
self.inactive_stroke_opacities = [x.stroke_opacity for x in inactive_vmobject.get_family()]
self.inactive_fill_opacities = [x.fill_opacity for x in inactive_vmobject.get_family()]

self.is_active = start_active
self.activation_anim_run_time = activation_anim_run_time
self.deactivation_anim_run_time = deactivation_anim_run_time
self.group = group
super().__init__(
active_vmobject.copy() if start_active else inactive_vmobject.copy(),
active_vmobject.set_opacity(0),
Expand All @@ -34,14 +34,55 @@ def __init__(
self.group = group

@property
def obj(self) -> VMobject:
def obj(self) -> Mobject:
return self[0]

def get_activation_anim(self) -> Animation:
@abc.abstractmethod
def _get_activation_anim(self):
raise NotImplementedError

def activate(self) -> Animation:
if self.is_active:
raise ValueError("The object is already active.")
self.is_active = True
return self._get_activation_anim()

@abc.abstractmethod
def _get_deactivation_anim(self):
raise NotImplementedError

def deactivate(self) -> Animation:
if not self.is_active:
raise ValueError("The object is already inactive.")
self.is_active = False
return self._get_deactivation_anim()


class VActivable(ActivableMobject):
def __init__(
self,
inactive_vmobject: VMobject,
active_vmobject: VMobject,
start_active: bool = False,
activation_anim_run_time: float = 1.0,
deactivation_anim_run_time: float = 1.0,
group: T.Optional[int] = None,
) -> None:
self.active_stroke_opacities = [x.stroke_opacity for x in active_vmobject.get_family()]
self.active_fill_opacities = [x.fill_opacity for x in active_vmobject.get_family()]
self.inactive_stroke_opacities = [x.stroke_opacity for x in inactive_vmobject.get_family()]
self.inactive_fill_opacities = [x.fill_opacity for x in inactive_vmobject.get_family()]

super().__init__(
inactive_vmobject=inactive_vmobject,
active_vmobject=active_vmobject,
start_active=start_active,
activation_anim_run_time=activation_anim_run_time,
deactivation_anim_run_time=deactivation_anim_run_time,
group=group,
)

def _get_activation_anim(self) -> Animation:
target = self.active_vmobject.copy()
for subobj, stroke_opacity, fill_opacity in zip(
target.get_family(), self.active_stroke_opacities, self.active_fill_opacities
Expand All @@ -51,11 +92,7 @@ def get_activation_anim(self) -> Animation:
subobj.set_stroke(opacity=stroke_opacity, family=False, background=True)
return Transform(self.obj, target, run_time=self.activation_anim_run_time)

def get_deactivation_anim(self) -> Animation:
if not self.is_active:
raise ValueError("The object is already inactive.")
self.is_active = False

def _get_deactivation_anim(self) -> Animation:
target = self.inactive_vmobject.copy()
for subobj, stroke_opacity, fill_opacity in zip(
target.get_family(), self.inactive_stroke_opacities, self.inactive_fill_opacities
Expand All @@ -67,7 +104,7 @@ def get_deactivation_anim(self) -> Animation:
return Transform(self.obj, target, run_time=self.deactivation_anim_run_time)


class AutoActivable(Activable):
class VAutoActivable(VActivable):
def __init__(
self,
vmobject: VMobject,
Expand All @@ -78,6 +115,8 @@ def __init__(
scale_active: T.Optional[float] = 1.1,
scale_about_point=None,
scale_about_edge=LEFT,
activation_anim_run_time: float = 1,
deactivation_anim_run_time: float = 1,
group: T.Optional[int] = None,
) -> None:
"""Activable component that automatically creates the active and inactive VMobjects.
Expand All @@ -91,6 +130,8 @@ def __init__(
scale_active (float): The scale of the active VMobject.
scale_about_point (np.ndarray): The point to scale about.
scale_about_edge (np.ndarray): The edge to scale about.
activation_anim_run_time (float): The run time of the activation animation.
deactivation_anim_run_time (float): The run time of the deactivation animation.
group (int): The group to which the object belongs.
"""
self.active_fill_opacity = active_fill_opacity
Expand All @@ -114,15 +155,80 @@ def __init__(
inactive_obj.set_fill(opacity=self.inactive_fill_opacity)
if self.inactive_stroke_opacity is not None:
inactive_obj.set_stroke(opacity=self.inactive_stroke_opacity)
super().__init__(inactive_vmobject=inactive_obj, active_vmobject=active_obj, start_active=False, group=group)
super().__init__(
inactive_vmobject=inactive_obj,
active_vmobject=active_obj,
start_active=False,
group=group,
activation_anim_run_time=activation_anim_run_time,
deactivation_anim_run_time=deactivation_anim_run_time,
)


class ImageAutoActivable(ActivableMobject):
def __init__(
self,
vmobject: Mobject,
active_opacity: T.Optional[float] = 1.0,
inactive_opacity: T.Optional[float] = 0.35,
scale_active: T.Optional[float] = 1.1,
scale_about_point=None,
scale_about_edge=ORIGIN,
activation_anim_run_time: float = 1.0,
deactivation_anim_run_time: float = 1.0,
group: T.Optional[int] = None,
) -> None:
"""Activable component that automatically creates the active and inactive Mobjects.
Args:
vmobject (Mobject): The object to activate or deactivate Mobject.
active_opacity (float): The fill opacity of the active Mobject.
inactive_opacity (float): The fill opacity of the inactive Mobject.
scale_active (float): The scale of the active Mobject.
scale_about_point (np.ndarray): The point to scale about.
scale_about_edge (np.ndarray): The edge to scale about.
activation_anim_run_time (float): The run time of the activation animation.
deactivation_anim_run_time (float): The run time of the deactivation animation.
group (int): The group to which the object belongs.
"""
self.active_opacity = active_opacity
self.inactive_opacity = inactive_opacity
self.scale_active = scale_active
self.scale_about_point = scale_about_point
self.scale_about_edge = scale_about_edge

active_obj = vmobject.copy()
if self.scale_active is not None:
active_obj.scale(self.scale_active, about_point=self.scale_about_point, about_edge=self.scale_about_edge)
if self.active_opacity is not None:
active_obj.set_opacity(self.active_opacity)

inactive_obj = vmobject.copy()
if self.inactive_opacity is not None:
inactive_obj.set_opacity(self.inactive_opacity)

super().__init__(
inactive_vmobject=inactive_obj,
active_vmobject=active_obj,
start_active=False,
activation_anim_run_time=activation_anim_run_time,
deactivation_anim_run_time=deactivation_anim_run_time,
group=group,
)

class VGroupActivable(VGroup):
def _get_activation_anim(self) -> Animation:
return self.obj.animate.set_opacity(self.active_opacity)

def _get_deactivation_anim(self) -> Animation:
return self.obj.animate.set_opacity(self.inactive_opacity)


class GroupActivable(Group):
def __init__(
self,
*args: VMobject,
*args: Mobject,
anim_lag_ratio: float = 0,
**kwargs: VMobject,
**kwargs: Mobject,
) -> None:
"""Group component that can activate a subset of its submobjects.
Expand All @@ -131,7 +237,14 @@ def __init__(
anim_lag_ratio (float): The lag ratio of the animation.
**kwargs: Keyword arguments to be passed to the VGroup.
"""
args_activable = (AutoActivable(x) if not isinstance(x, Activable) else x for x in args)
args_activable = (
(
(VAutoActivable(x) if isinstance(x, VMobject) else ImageAutoActivable(x))
if not isinstance(x, ActivableMobject)
else x
)
for x in args
)
super().__init__(*args_activable, **kwargs)

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

self.group2items: Dict[int, T.Set[Activable]] = defaultdict(set)
self.group2items: Dict[int, T.Set[ActivableMobject]] = defaultdict(set)
for i, obj in enumerate(self.submobjects):
group = obj.group
if group is None:
Expand Down Expand Up @@ -177,14 +290,12 @@ def activate(self, indices: T.Union[int, T.Sequence[int]]) -> AnimationGroup:
for to_activate in indices:
if to_activate in self.previously_active_idxs:
continue
anims.append(AnimationGroup(*(x.get_activation_anim() for x in self.group2items[to_activate])))
anims.append(AnimationGroup(*(x.activate() for x in self.group2items[to_activate])))

if self.previously_active_idxs:
for previously_active_idx in self.previously_active_idxs:
if previously_active_idx not in indices:
anims.append(
AnimationGroup(*(x.get_deactivation_anim() for x in self.group2items[previously_active_idx]))
)
anims.append(AnimationGroup(*(x.deactivate() for x in self.group2items[previously_active_idx])))

self.previously_active_idxs = indices

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from manim import *

from powermanim import VGroupActivable
from powermanim.components.vgroupactivable import AutoActivable
from powermanim import GroupActivable
from powermanim.components.groupactivable import VAutoActivable
from powermanim.showcase.showcasescene import ShowcaseScene


class VGroupActivableShowcase(ShowcaseScene):
@staticmethod
def showcasing():
return VGroupActivable
return GroupActivable

def construct(self):
dots = [
Expand All @@ -25,9 +25,9 @@ def construct(self):
)
]

group = VGroupActivable(
group = GroupActivable(
*map(
lambda x: AutoActivable(
lambda x: VAutoActivable(
x,
active_fill_opacity=1,
active_stroke_opacity=1,
Expand Down
6 changes: 3 additions & 3 deletions src/powermanim/templates/bulletlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

from manim import *

from powermanim import ArrangedBullets, AutoActivable, MathBullet, VGroupActivable
from powermanim import ArrangedBullets, GroupActivable, MathBullet, VAutoActivable


class BulletList(VGroupActivable):
class BulletList(GroupActivable):
def __init__(
self,
*rows: T.Union[MathBullet, Tex, Text],
Expand Down Expand Up @@ -41,7 +41,7 @@ def __init__(

super().__init__(
*(
AutoActivable(
VAutoActivable(
x,
active_fill_opacity=active_opacity,
active_stroke_opacity=active_opacity,
Expand Down

0 comments on commit 551c42a

Please sign in to comment.