Repository: ManimCommunity/manim

Issue:
`Uncreate` does not work as expected when overriding the default argument for `rate_func`
## Description of bug / unexpected behavior
When passing any argument for `rate_func` to the `Uncreate` animation, the result is exactly the same as would be seen from using the `Create` animation. For instance,

```py
class Bug(Scene):
    def construct(self):
      s = Square()
      self.add(s)
      self.play(Uncreate(s), rate_func=smooth)
      self.wait()
```

produces:

https://user-images.githubusercontent.com/9102529/150136839-6073c3be-01cb-4bf7-8931-3968a468aa49.mp4

Currently, `Uncreate` just inherits from `Create` and overrides the default `rate_func` argument. This is a poor implementation; it should either inherit directly from `ShowPartial`, or a general method to reverse animations should be implemented. The `Uncreate` animation will then just be a special case of a reversed animation.

## Expected behavior
<!-- Add a clear and concise description of what you expected to happen. -->


## How to reproduce the issue
<!-- Provide a piece of code illustrating the undesired behavior. -->

<details><summary>Code for reproducing the problem</summary>

```py
Paste your code here.
```

</details>


## Additional media files
<!-- Paste in the files manim produced on rendering the code above. -->

<details><summary>Images/GIFs</summary>

<!-- PASTE MEDIA HERE -->

</details>


## Logs
<details><summary>Terminal output</summary>
<!-- Add "-v DEBUG" when calling manim to generate more detailed logs -->

```
PASTE HERE OR PROVIDE LINK TO https://pastebin.com/ OR SIMILAR
```

<!-- Insert screenshots here (only when absolutely necessary, we prefer copy/pasted output!) -->

</details>


## System specifications

<details><summary>System Details</summary>

- OS (with version, e.g Windows 10 v2004 or macOS 10.15 (Catalina)):
- RAM:
- Python version (`python/py/python3 --version`):
- Installed modules (provide output from `pip list`):
```
PASTE HERE
```
</details>

<details><summary>LaTeX details</summary>

+ LaTeX distribution (e.g. TeX Live 2020):
+ Installed LaTeX packages:
<!-- output of `tlmgr list --only-installed` for TeX Live or a screenshot of the Packages page for MikTeX -->
</details>

<details><summary>FFMPEG</summary>

Output of `ffmpeg -version`:

```
PASTE HERE
```
</details>

## Additional comments
<!-- Add further context that you think might be relevant for this issue here. -->

`Uncreate` does not work as expected when overriding the default argument for `rate_func`
## Description of bug / unexpected behavior
When passing any argument for `rate_func` to the `Uncreate` animation, the result is exactly the same as would be seen from using the `Create` animation. For instance,

```py
class Bug(Scene):
    def construct(self):
      s = Square()
      self.add(s)
      self.play(Uncreate(s), rate_func=smooth)
      self.wait()
```

produces:

https://user-images.githubusercontent.com/9102529/150136839-6073c3be-01cb-4bf7-8931-3968a468aa49.mp4

Currently, `Uncreate` just inherits from `Create` and overrides the default `rate_func` argument. This is a poor implementation; it should either inherit directly from `ShowPartial`, or a general method to reverse animations should be implemented. The `Uncreate` animation will then just be a special case of a reversed animation.

## Expected behavior
<!-- Add a clear and concise description of what you expected to happen. -->


## How to reproduce the issue
<!-- Provide a piece of code illustrating the undesired behavior. -->

<details><summary>Code for reproducing the problem</summary>

```py
Paste your code here.
```

</details>


## Additional media files
<!-- Paste in the files manim produced on rendering the code above. -->

<details><summary>Images/GIFs</summary>

<!-- PASTE MEDIA HERE -->

</details>


## Logs
<details><summary>Terminal output</summary>
<!-- Add "-v DEBUG" when calling manim to generate more detailed logs -->

```
PASTE HERE OR PROVIDE LINK TO https://pastebin.com/ OR SIMILAR
```

<!-- Insert screenshots here (only when absolutely necessary, we prefer copy/pasted output!) -->

</details>


## System specifications

<details><summary>System Details</summary>

- OS (with version, e.g Windows 10 v2004 or macOS 10.15 (Catalina)):
- RAM:
- Python version (`python/py/python3 --version`):
- Installed modules (provide output from `pip list`):
```
PASTE HERE
```
</details>

<details><summary>LaTeX details</summary>

+ LaTeX distribution (e.g. TeX Live 2020):
+ Installed LaTeX packages:
<!-- output of `tlmgr list --only-installed` for TeX Live or a screenshot of the Packages page for MikTeX -->
</details>

<details><summary>FFMPEG</summary>

Output of `ffmpeg -version`:

```
PASTE HERE
```
</details>

## Additional comments
<!-- Add further context that you think might be relevant for this issue here. -->



Discussion:
`rate_func=lambda t, inflection=10.0: smooth((1-t), inflection)`
That's a workaround, but still doesn't resolve the unexpected behavior.
`rate_func=lambda t, inflection=10.0: smooth((1-t), inflection)`
That's a workaround, but still doesn't resolve the unexpected behavior.

Resolving patch:
diff --git a/manim/animation/animation.py b/manim/animation/animation.py
--- a/manim/animation/animation.py
+++ b/manim/animation/animation.py
@@ -49,6 +49,13 @@ class Animation:
 
         For example ``rate_func(0.5)`` is the proportion of the animation that is done
         after half of the animations run time.
+
+
+    reverse_rate_function
+        Reverses the rate function of the animation. Setting ``reverse_rate_function``
+        does not have any effect on ``remover`` or ``introducer``. These need to be
+        set explicitly if an introducer-animation should be turned into a remover one
+        and vice versa.
     name
         The name of the animation. This gets displayed while rendering the animation.
         Defaults to <class-name>(<Mobject-name>).
@@ -123,6 +130,7 @@ def __init__(
         lag_ratio: float = DEFAULT_ANIMATION_LAG_RATIO,
         run_time: float = DEFAULT_ANIMATION_RUN_TIME,
         rate_func: Callable[[float], float] = smooth,
+        reverse_rate_function: bool = False,
         name: str = None,
         remover: bool = False,  # remove a mobject from the screen?
         suspend_mobject_updating: bool = True,
@@ -134,6 +142,7 @@ def __init__(
         self._typecheck_input(mobject)
         self.run_time: float = run_time
         self.rate_func: Callable[[float], float] = rate_func
+        self.reverse_rate_function: bool = reverse_rate_function
         self.name: str | None = name
         self.remover: bool = remover
         self.introducer: bool = introducer
@@ -361,7 +370,10 @@ def get_sub_alpha(self, alpha: float, index: int, num_submobjects: int) -> float
         full_length = (num_submobjects - 1) * lag_ratio + 1
         value = alpha * full_length
         lower = index * lag_ratio
-        return self.rate_func(value - lower)
+        if self.reverse_rate_function:
+            return self.rate_func(1 - (value - lower))
+        else:
+            return self.rate_func(value - lower)
 
     # Getters and setters
     def set_run_time(self, run_time: float) -> Animation:
diff --git a/manim/animation/creation.py b/manim/animation/creation.py
--- a/manim/animation/creation.py
+++ b/manim/animation/creation.py
@@ -191,13 +191,13 @@ def construct(self):
     def __init__(
         self,
         mobject: VMobject | OpenGLVMobject,
-        rate_func: Callable[[float], float] = lambda t: smooth(1 - t),
+        reverse_rate_function: bool = True,
         remover: bool = True,
         **kwargs,
     ) -> None:
         super().__init__(
             mobject,
-            rate_func=rate_func,
+            reverse_rate_function=reverse_rate_function,
             introducer=False,
             remover=remover,
             **kwargs,
@@ -405,7 +405,7 @@ def __init__(
             vmobject,
             run_time=run_time,
             lag_ratio=lag_ratio,
-            rate_func=lambda t: -rate_func(t) + 1,
+            reverse_rate_function=True,
             reverse=reverse,
             **kwargs,
         )


Test patch:
diff --git a/tests/test_graphical_units/control_data/creation/uncreate_rate_func.npz b/tests/test_graphical_units/control_data/creation/uncreate_rate_func.npz
new file mode 100644
Binary files /dev/null and b/tests/test_graphical_units/control_data/creation/uncreate_rate_func.npz differ
diff --git a/tests/test_graphical_units/test_creation.py b/tests/test_graphical_units/test_creation.py
--- a/tests/test_graphical_units/test_creation.py
+++ b/tests/test_graphical_units/test_creation.py
@@ -19,6 +19,13 @@ def test_uncreate(scene):
     scene.play(Uncreate(square))
 
 
+@frames_comparison(last_frame=False)
+def test_uncreate_rate_func(scene):
+    square = Square()
+    scene.add(square)
+    scene.play(Uncreate(square), rate_func=linear)
+
+
 @frames_comparison(last_frame=False)
 def test_DrawBorderThenFill(scene):
     square = Square(fill_opacity=1)