ManimCE 0.19.0 and 0.18.1: Axes.c2p() intermittent problem
Description of bug / unexpected behavior
( this is a spin-off from issue 4153 )
During the process of self.play(Create(axes)) the functionality of .coords_to_point() is momentarily broken.
Expected behavior
The method .coords_to_point(x,y) does give the correct position on the scene when the corresponding Axes() object is not on the screen which can be used for coordinate transformations. It also works correctly when the Axes() object is placed on the scene. But during the process of the animated creation of the Axes() object the coordinate calculation is broken, which becomes apparent when .c2p() is used in an updater which is active during the creation of the Axes() object.
How to reproduce the issue
Code for reproducing the problem
class weird(Scene):
def construct(self):
axes = Axes(
x_range=(-1,4,1),
y_range=(-2,5,1),
tips=False,
).add_coordinates()
dotA=always_redraw(lambda: Dot(point=axes.c2p(2,2)))
self.add(dotA)
self.wait()
self.play(Create(axes))
self.wait(1)
Additional media files
rendered video ManimCE 0.19.0
https://github.com/user-attachments/assets/425e6779-9cf2-4e1a-bf7d-addada3d021c
System specifications
System Details
- OS (with version, e.g., Windows 10 v2004 or macOS 10.15 (Catalina)): Windows 10
- RAM:
- Python version (
python/py/python3 --version): 3.13 and 3.11
Additional comments
Hi @uwezi
I’d like to share my perspective on this issue.
I believe the observed behavior is not actually a bug but rather an expected behavior of Create(axes). During Create(axes), the Axes object is being drawn progressively frame by frame. This means that at every frame, the method .c2p() is applied to an Axes object that is still partially constructed. Since the Axes are growing over time, c2p(2,2) will return a different position in each frame, causing the dot to appear "move."
To illustrate, I made a simple example using NumberLine:
from manim import *
class NumLine(Scene):
def construct(self):
numline = NumberLine(
x_range=[-10, 10],
unit_size=0.5,
include_ticks = False,
)
dotA = always_redraw(lambda: Dot(point=numline.n2p(10)))
num = always_redraw(lambda: Tex("10").next_to(numline.n2p(10),DOWN))
self.add(dotA, num)
self.wait()
self.play(Create(numline), run_time=3)
self.wait()
In this example, n2p(10) changes dynamically because numline is still being created. The dot is not moving on its own; rather, it follows the correct position of n2p(10) at each frame based on the currently drawn portion of the NumberLine.
I think the same thing is happening with Axes:
.c2p(2,2)is working as expected, but its result changes because theAxesare still being created.- The dot is moving because it’s just updating its position based on
c2p(2,2), which naturally shifts asAxesgrows.
So, from my understanding, c2p() is not broken, but rather behaving as expected based on the state of Axes at each frame. The output video is below. What do you think?
https://github.com/user-attachments/assets/bc088e5a-b8e3-49bb-a703-edc06fcb70d2
I just tested it with Axes.
from manim import *
class Weird(Scene):
def construct(self):
axes = Axes(
x_range=(-1,4,1),
y_range=(-2,5,1),
tips=False,
axis_config = {"include_ticks": False}
)
dotA=always_redraw(lambda: Dot(point=axes.c2p(2,2)))
self.add(dotA)
self.wait()
self.play(Create(axes),run_time=3)
self.wait()
https://github.com/user-attachments/assets/0bf065f9-8523-469a-9ad2-f1f7b79a29ad