Repository: ManimCommunity/manim
File: manim/animation/transform_matching_parts.py (Python)

Code before:
                FadeIn(fade_target, target_position=fade_target, **kwargs),
            )

        super().__init__(*anims)

        self.to_remove = mobject
        self.to_add = target_mobject

    def get_shape_map(self, mobject: Mobject) -> dict:
        shape_map = {}
        for sm in self.get_mobject_parts(mobject):
            key = self.get_mobject_key(sm)
            if key not in shape_map:
                if config["renderer"] == "opengl":
                    shape_map[key] = OpenGLVGroup()
                else:
                    shape_map[key] = VGroup()
            shape_map[key].add(sm)
        return shape_map

    def clean_up_from_scene(self, scene: Scene) -> None:
        # Interpolate all animations back to 0 to ensure source mobjects remain unchanged.
        for anim in self.animations:
            # Skip interpolating FadeIn because fade_target is not a source.
            if not isinstance(anim, FadeIn):
                anim.interpolate(0)
        scene.remove(self.mobject)
        scene.remove(self.to_remove)
        scene.add(self.to_add)

    @staticmethod
    def get_mobject_parts(mobject: Mobject):
        raise NotImplementedError("To be implemented in subclass.")

    @staticmethod
    def get_mobject_key(mobject: Mobject):
        raise NotImplementedError("To be implemented in subclass.")


class TransformMatchingShapes(TransformMatchingAbstractBase):
    """An animation trying to transform groups by matching the shape
    of their submobjects.

    Two submobjects match if the hash of their point coordinates after
    normalization (i.e., after translation to the origin, fixing the submobject
    height at 1 unit, and rounding the coordinates to three decimal places)
    matches.

    See also
    --------
    :class:`~.TransformMatchingAbstractBase`

Review comment (suggestion):
I would leave this code block as it is because checking every animation if it is a `FadeIn` is a bit too much. As you said earlier the main problem is the cleanup so i would suggest changing that instead.
```suggestion
            anim.interpolate(0)
```

Code after:
            anims.append(
                FadeIn(fade_target_copy, target_position=fade_target, **kwargs),
            )

        super().__init__(*anims)

        self.to_remove = [mobject, fade_target_copy]
        self.to_add = target_mobject

    def get_shape_map(self, mobject: Mobject) -> dict:
        shape_map = {}
        for sm in self.get_mobject_parts(mobject):
            key = self.get_mobject_key(sm)
            if key not in shape_map:
                if config["renderer"] == "opengl":
                    shape_map[key] = OpenGLVGroup()
                else:
                    shape_map[key] = VGroup()
            shape_map[key].add(sm)
        return shape_map

    def clean_up_from_scene(self, scene: Scene) -> None:
        # Interpolate all animations back to 0 to ensure source mobjects remain unchanged.
        for anim in self.animations:
            anim.interpolate(0)
        scene.remove(self.mobject)
        scene.remove(*self.to_remove)
        scene.add(self.to_add)

    @staticmethod
    def get_mobject_parts(mobject: Mobject):
        raise NotImplementedError("To be implemented in subclass.")

    @staticmethod
    def get_mobject_key(mobject: Mobject):
        raise NotImplementedError("To be implemented in subclass.")


class TransformMatchingShapes(TransformMatchingAbstractBase):
    """An animation trying to transform groups by matching the shape
    of their submobjects.

    Two submobjects match if the hash of their point coordinates after
    normalization (i.e., after translation to the origin, fixing the submobject
    height at 1 unit, and rounding the coordinates to three decimal places)
    matches.

    See also
    --------
    :class:`~.TransformMatchingAbstractBase`