Skip to content

SVGMobject doesn't work with always_redraw #4311

@blackedout01

Description

@blackedout01

Description of bug / unexpected behavior

Using always_redraw with SVGMobject raises

TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'

when it tries to interpolate the colors of the current and next object it seems.

Expected behavior

Like for other Mobjects, it should just replace the old one with the next. Tbh I'm not entirely sure why it even calls the become method on the mobject. Is there a specific reason for that? I would assume always_redraw just inserts the object generated by func() for a single frame and removes it afterwards.

How to reproduce the issue

The following code is just an example of how to get the relevant error. In this specific case the desired output could also be achieved by animating the scale change differently.
An example where the code would be necessary (as far as I know) is when one has different SVGs for each frame. But this would make the code to reproduce more complex.

Code for reproducing the problem
class SvgRedrawIssue(Scene):
    def construct(self):
        value = ValueTracker(1)
        object = always_redraw(lambda: SVGMobject("something.svg").scale(value.get_value()))
        self.add(object)
        self.play(value.animate.set_value(2), rate_func=rate_functions.linear)

Additional media files

There isn't any output but I have attached the SVG file that is used in the code, even though any other file will probably produce the same error.

Images/GIFs

Image

Logs

Terminal output
Manim Community v0.19.0

[06/24/25 19:26:52] DEBUG    Hashing ...                                                                                           hashing.py:352
                    DEBUG    Hashing done in 0.002095 s.                                                                           hashing.py:364
                    DEBUG    Hash generated :  1457378895_2436127281_1583890234                                                    hashing.py:367
                    DEBUG    List of the first few animation hashes of the scene: ['1457378895_2436127281_1583890234']       cairo_renderer.py:98
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮                                             
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/cli/render/commands.py: │
│ 125 in render                                                                                    │
│                                                                                                  │
│   122 │   │   │   try:                                                                           │
│   123 │   │   │   │   with tempconfig({}):                                                       │
│   124 │   │   │   │   │   scene = SceneClass()                                                   │
│ ❱ 125 │   │   │   │   │   scene.render()                                                         │
│   126 │   │   │   except Exception:                                                              │
│   127 │   │   │   │   error_console.print_exception()                                            │
│   128 │   │   │   │   sys.exit(1)                                                                │
│                                                                                                  │
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/scene/scene.py:237 in   │
│ render                                                                                           │
│                                                                                                  │
│    234 │   │   """                                                                               │
│    235 │   │   self.setup()                                                                      │
│    236 │   │   try:                                                                              │
│ ❱  237 │   │   │   self.construct()                                                              │
│    238 │   │   except EndSceneEarlyException:                                                    │
│    239 │   │   │   pass                                                                          │
│    240 │   │   except RerunSceneException:                                                       │
│                                                                                                  │
│ [redacted].py:246 in construct                                                                   │
│                                                                                                  │
│   243 │   │   value = ValueTracker(1)                                                            │
│   244 │   │   object = always_redraw(lambda: SVGMobject("something.svg").scale(value.get_value   │
│   245 │   │   self.add(object)                                                                   │
│ ❱ 246 │   │   self.play(value.animate.set_value(2), rate_func=rate_functions.linear)             │
│   247                                                                                            │
│   248                                                                                            │
│   249 class SvgIssue2(Scene):                                                                    │
│                                                                                                  │
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/scene/scene.py:1125 in  │
│ play                                                                                             │
│                                                                                                  │
│   1122 │   │   │   return                                                                        │
│   1123 │   │                                                                                     │
│   1124 │   │   start_time = self.time                                                            │
│ ❱ 1125 │   │   self.renderer.play(self, *args, **kwargs)                                         │
│   1126 │   │   run_time = self.time - start_time                                                 │
│   1127 │   │   if subcaption:                                                                    │
│   1128 │   │   │   if subcaption_duration is None:                                               │
│                                                                                                  │
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/renderer/cairo_renderer │
│ .py:115 in play                                                                                  │
│                                                                                                  │
│   112 │   │   │   # In this case, as there is only a wait, it will be the length of the wait.    │
│   113 │   │   │   self.freeze_current_frame(scene.duration)                                      │
│   114 │   │   else:                                                                              │
│ ❱ 115 │   │   │   scene.play_internal()                                                          │
│   116 │   │   self.file_writer.end_animation(not self.skip_animations)                           │
│   117 │   │                                                                                      │
│   118 │   │   self.num_plays += 1                                                                │
│                                                                                                  │
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/scene/scene.py:1295 in  │
│ play_internal                                                                                    │
│                                                                                                  │
│   1292 │   │   │   self.duration,                                                                │
│   1293 │   │   )                                                                                 │
│   1294 │   │   for t in self.time_progression:                                                   │
│ ❱ 1295 │   │   │   self.update_to_time(t)                                                        │
│   1296 │   │   │   if not skip_rendering and not self.skip_animation_preview:                    │
│   1297 │   │   │   │   self.renderer.render(self, t, self.moving_mobjects)                       │
│   1298 │   │   │   if self.stop_condition is not None and self.stop_condition():                 │
│                                                                                                  │
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/scene/scene.py:1535 in  │
│ update_to_time                                                                                   │
│                                                                                                  │
│   1532 │   │   │   animation.update_mobjects(dt)                                                 │
│   1533 │   │   │   alpha = t / animation.run_time                                                │
│   1534 │   │   │   animation.interpolate(alpha)                                                  │
│ ❱ 1535 │   │   self.update_mobjects(dt)                                                          │
│   1536 │   │   self.update_meshes(dt)                                                            │
│   1537 │   │   self.update_self(dt)                                                              │
│   1538                                                                                           │
│                                                                                                  │
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/scene/scene.py:356 in   │
│ update_mobjects                                                                                  │
│                                                                                                  │
│    353 │   │   │   Change in time between updates. Defaults (mostly) to 1/frames_per_second      │
│    354 │   │   """                                                                               │
│    355 │   │   for mobject in self.mobjects:                                                     │
│ ❱  356 │   │   │   mobject.update(dt)                                                            │
│    357 │                                                                                         │
│    358 │   def update_meshes(self, dt):                                                          │
│    359 │   │   for obj in self.meshes:                                                           │
│                                                                                                  │
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/mobject/mobject.py:902  │
│ in update                                                                                        │
│                                                                                                  │
│    899 │   │   │   if "dt" in inspect.signature(updater).parameters:                             │
│    900 │   │   │   │   updater(self, dt)                                                         │
│    901 │   │   │   else:                                                                         │
│ ❱  902 │   │   │   │   updater(self)                                                             │
│    903 │   │   if recursive:                                                                     │
│    904 │   │   │   for submob in self.submobjects:                                               │
│    905 │   │   │   │   submob.update(dt, recursive)                                              │
│                                                                                                  │
│ [redacted].py:10 in <lambda>                                                                     │
│                                                                                                  │
│     7                                                                                            │
│     8 def always_redraw(func: Callable[[], Mobject]) -> Mobject:                                 │
│     9 │   mob = func()                                                                           │
│ ❱  10 │   mob.add_updater(lambda _: mob.become(func()))                                          │
│    11 │   return mob                                                                             │
│    12                                                                                            │
│    13 @dataclass                                                                                 │
│                                                                                                  │
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/mobject/mobject.py:2941 │
│ in become                                                                                        │
│                                                                                                  │
│   2938 │   │   self.align_data(mobject, skip_point_alignment=True)                               │
│   2939 │   │   for sm1, sm2 in zip(self.get_family(), mobject.get_family()):                     │
│   2940 │   │   │   sm1.points = np.array(sm2.points)                                             │
│ ❱ 2941 │   │   │   sm1.interpolate_color(sm1, sm2, 1)                                            │
│   2942 │   │   return self                                                                       │
│   2943 │                                                                                         │
│   2944 │   def match_points(self, mobject: Mobject, copy_submobjects: bool = True) -> Self:      │
│                                                                                                  │
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/mobject/types/vectorize │
│ d_mobject.py:1864 in interpolate_color                                                           │
│                                                                                                  │
│   1861 │   │   │   setattr(                                                                      │
│   1862 │   │   │   │   self,                                                                     │
│   1863 │   │   │   │   attr,                                                                     │
│ ❱ 1864 │   │   │   │   interpolate(getattr(mobject1, attr), getattr(mobject2, attr), alpha),     │
│   1865 │   │   │   )                                                                             │
│   1866 │   │   │   if alpha == 1.0:                                                              │
│   1867 │   │   │   │   val = getattr(mobject2, attr)                                             │
│                                                                                                  │
│ /opt/homebrew/Caskroom/miniconda/base/lib/python3.12/site-packages/manim/utils/bezier.py:1062 in │
│ interpolate                                                                                      │
│                                                                                                  │
│   1059 │   │   │   * ``alpha`` is a :class:`float`, the return is another :class:`~.Point3D`.    │
│   1060 │   │   │   * ``alpha`` is a :class:`~.ColVector`, the return is a :class:`~.Point3D_Arr  │
│   1061 │   """                                                                                   │
│ ❱ 1062 │   return (1 - alpha) * start + alpha * end                                              │
│   1063                                                                                           │
│   1064                                                                                           │
│   1065 def integer_interpolate(                                                                  │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'

System specifications

System Details macOS 15.5

Python 3.12.2

LaTeX details I don't think this is necessary. If it is I'll add it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    🆕 New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions