ManimCommunity / manim

A community-maintained Python framework for creating mathematical animations.
https://www.manim.community
MIT License
21.05k stars 1.53k forks source link

Decimal numbers don't work with `self.add_fixed_in_frame_mobjects` #1884

Open torannn opened 3 years ago

torannn commented 3 years ago

Description of bug / unexpected behavior

In 3D scene, I added a decimal number mobject by self.add_fixed_in_frame_mobjects() but the decimal number is still being affected by the camera view. Here is the test code:

class SquareToCircle(ThreeDScene):
    def construct(self):
        self.move_camera(theta = 30*DEGREES, phi = 70*DEGREES)
        tracker = ValueTracker(0)
        circle = Circle()
        running = DecimalNumber(np.rint(tracker.get_value())).add_updater(lambda v: v.set_value(np.rint(tracker.get_value())))
        self.add_fixed_in_frame_mobjects(running, circle)
        self.play(tracker.animate.set_value(100))
        self.play(Create(circle))
        self.wait()

Expected behavior

The decimal number has to be fixed in the frame, just like the circle.

Additional media files

https://user-images.githubusercontent.com/86940649/128658772-12db6262-9d16-4700-ae13-90b2cb27efd3.mp4

Viicos commented 2 years ago

This is how I usually do it:

class SquareToCircle(ThreeDScene):
    def construct(self):
        self.move_camera(
            theta=30 * DEGREES,
            phi=70 * DEGREES
        )
        tracker = ValueTracker()
        circle = Circle()
        running = DecimalNumber(
            round(tracker.get_value())
        ).add_updater(lambda v: v.set_value(round(tracker.get_value())))

        self.add_fixed_in_frame_mobjects(running)
        # As the DecimalNumber is being updated with the `set_value` updater, you have
        # to use an updater to fix it in frame.
        running.add_updater(lambda v: self.add_fixed_in_frame_mobjects(v))
        self.play(tracker.animate.set_value(100))
        self.add_fixed_in_frame_mobjects(circle)
        self.play(Create(circle))
        self.wait()

Also used round instead of np.rint. You might want to take a look to the num_decimal_places parameter of the DecimalNumber object (or maybe use Integer instead).

Notice that by writing self.add_fixed_in_frame_mobjects(object), the object will be added directly on the scene, so it's better to use it just before self.play.

BorisTheBrave commented 2 years ago

So the bug here is that add_fixed_in_frame evaluates the family (i.e. the mobject + recursive submobjects) eagerly, to store it in ThreeDCamera.fixed_in_frame_mobjects. But DecimalNumber changes it's submobjects every time it is updated, causing this to go out of date.

Viicos' workaround seems good thought it does cause some memory leaks.

I'd propose that a proper fix would be one of the following: 1) evaluate the family every frame instead of once 2) evaluate the family eagerly, but invalidate that cache when the submobjects change.