From 86aeeb861b70cf8b2efb7838291a4bcbd9df9bae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Eertmans?= <jeertmans@icloud.com>
Date: Wed, 19 Jul 2023 15:13:08 +0200
Subject: [PATCH] feat(lib): add wip transition (#215)

---
 manim_slides/manim.py | 22 ++++++++++---
 manim_slides/slide.py | 75 +++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 91 insertions(+), 6 deletions(-)

diff --git a/manim_slides/manim.py b/manim_slides/manim.py
index 873bc5b..2913235 100644
--- a/manim_slides/manim.py
+++ b/manim_slides/manim.py
@@ -5,6 +5,9 @@ from importlib.util import find_spec
 from typing import Iterator
 
 __all__ = [
+    # Constants
+    "FFMPEG_BIN",
+    "LEFT",
     "MANIM",
     "MANIM_PACKAGE_NAME",
     "MANIM_AVAILABLE",
@@ -13,11 +16,14 @@ __all__ = [
     "MANIMGL_PACKAGE_NAME",
     "MANIMGL_AVAILABLE",
     "MANIMGL_IMPORTED",
-    "logger",
+    # Classes
+    "AnimationGroup",
+    "Mobject",
     "Scene",
     "ThreeDScene",
+    # Objects
+    "logger",
     "config",
-    "FFMPEG_BIN",
 ]
 
 
@@ -67,13 +73,21 @@ else:
 
 
 if MANIMGL:
-    from manimlib import Scene, ThreeDScene, config
+    from manimlib import LEFT, AnimationGroup, Mobject, Scene, ThreeDScene, config
     from manimlib.constants import FFMPEG_BIN
     from manimlib.logger import log as logger
 
 else:
     with suppress_stdout():  # Avoids printing "Manim Community v..."
-        from manim import Scene, ThreeDScene, config, logger
+        from manim import (
+            LEFT,
+            AnimationGroup,
+            Mobject,
+            Scene,
+            ThreeDScene,
+            config,
+            logger,
+        )
 
         try:  # For manim<v0.16.0.post0
             from manim.constants import FFMPEG_BIN
diff --git a/manim_slides/slide.py b/manim_slides/slide.py
index efc01bf..7d97677 100644
--- a/manim_slides/slide.py
+++ b/manim_slides/slide.py
@@ -2,14 +2,25 @@ import os
 import platform
 import shutil
 import subprocess
-from typing import Any, List, Optional, Tuple
+from typing import Any, List, Optional, Sequence, Tuple
 from warnings import warn
 
+import numpy as np
 from tqdm import tqdm
 
 from .config import PresentationConfig, SlideConfig, SlideType
 from .defaults import FOLDER_PATH
-from .manim import FFMPEG_BIN, MANIMGL, Scene, ThreeDScene, config, logger
+from .manim import (
+    FFMPEG_BIN,
+    LEFT,
+    MANIMGL,
+    AnimationGroup,
+    Mobject,
+    Scene,
+    ThreeDScene,
+    config,
+    logger,
+)
 
 
 def reverse_video_file(src: str, dst: str) -> None:
@@ -54,6 +65,22 @@ class Slide(Scene):  # type:ignore
         self.__loop_start_animation: Optional[int] = None
         self.__pause_start_animation = 0
 
+    @property
+    def __frame_height(self) -> float:
+        """Returns the scene's frame height."""
+        if MANIMGL:
+            return self.frame_height  # type: ignore
+        else:
+            return config["frame_height"]  # type: ignore
+
+    @property
+    def __frame_width(self) -> float:
+        """Returns the scene's frame width."""
+        if MANIMGL:
+            return self.frame_width  # type: ignore
+        else:
+            return config["frame_width"]  # type: ignore
+
     @property
     def __background_color(self) -> str:
         """Returns the scene's background color."""
@@ -357,6 +384,50 @@ class Slide(Scene):  # type:ignore
 
         self.__save_slides()
 
+    def wipe(
+        self,
+        current: Sequence[Mobject] = [],
+        future: Sequence[Mobject] = [],
+        direction: np.ndarray = LEFT,
+        **kwargs: Any,
+    ) -> AnimationGroup:
+        """
+        Returns a wipe animation that will shift all the current objects outside
+        of the current scene's scope, and all the future objects inside.
+
+        :param current: A sequence of mobjects to remove from the scene.
+        :param future: A sequence of mobjects to add to the scene.
+        :direction: The wipe direction.
+
+        Examples
+        --------
+
+        .. code-block:: python
+
+            class WipeExample(Slide):
+                def construct(self):
+                    circle = Circle(radius=3, color=BLUE)
+                    square = Square()
+                    text = Text("This is a wipe example").next_to(square, DOWN)
+
+                    self.play(Create(circle))
+                    self.next_slide()
+
+                    self.play(self.wipe(circle, Group(square, text)))
+        """
+        shift_amount = np.asarray(direction) * np.array(
+            [self.__frame_width, self.__frame_height, 0.0]
+        )
+
+        for mobject in future:
+            mobject.shift(-shift_amount)
+
+        animations = [
+            mobject.animate.shift(shift_amount) for mobject in [*current, *future]
+        ]
+
+        return AnimationGroup(*animations, **kwargs)
+
 
 class ThreeDSlide(Slide, ThreeDScene):  # type: ignore
     """