diff --git a/manim/scene/scene.py b/manim/scene/scene.py index 845fafd0b9..cc7ee8bfb5 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -994,8 +994,35 @@ def compile_animations( for k, v in kwargs.items(): setattr(animation, k, v) + self._warn_if_overlapping_animate_targets(animations) + return animations + @staticmethod + def _warn_if_overlapping_animate_targets(animations: list[Animation]) -> None: + from ..animation.transform import _MethodAnimation + + method_anims = [ + anim for anim in animations if isinstance(anim, _MethodAnimation) + ] + if len(method_anims) < 2: + return + + seen: set[int] = set() + for anim in method_anims: + for member in anim.mobject.get_family(): + member_id = id(member) + if member_id in seen: + logger.warning( + "Multiple .animate animations in the same play() affect " + "the same mobject (%s). Later animations override earlier " + "ones for unchanged attributes; reorder arguments or " + "animate mobjects individually.", + member.__class__.__name__, + ) + return + seen.add(member_id) + def _get_animation_time_progression( self, animations: list[Animation], duration: float ) -> tqdm[float]: diff --git a/tests/module/scene/test_scene.py b/tests/module/scene/test_scene.py index c64fbd46ac..4ad0ae5434 100644 --- a/tests/module/scene/test_scene.py +++ b/tests/module/scene/test_scene.py @@ -1,10 +1,22 @@ from __future__ import annotations import datetime +from unittest.mock import patch import pytest -from manim import Circle, Dot, FadeIn, Group, Mobject, Scene, Square +from manim import ( + RIGHT, + Circle, + Dot, + FadeIn, + Group, + Mobject, + Scene, + Square, + VGroup, + logger, +) from manim.animation.animation import Wait @@ -146,3 +158,19 @@ def test_random_color_reproducibility_with_seed(dry_run): # The interrupted colors should be different (seeded with 999) assert colors_interrupted != colors_first_run[:3] + + +def test_warn_overlapping_animate_targets() -> None: + scene = Scene() + square = Square() + circle = Circle() + group = VGroup(square, circle) + + with patch.object(logger, "warning") as warn: + scene.compile_animations( + group.animate.shift(RIGHT), + circle.animate.set_opacity(0.5), + ) + + warn.assert_called_once() + assert "Multiple .animate animations" in warn.call_args[0][0]