manim icon indicating copy to clipboard operation
manim copied to clipboard

get_axis_labels() not working properly for rotated axis

Open BayezidBostami06 opened this issue 5 months ago • 1 comments

Description of bug / unexpected behavior

get_axis_labels() is the function needed to add labels to the axes. It is not working properly when I rotated the axis. This problem is especially prominent for:

  1. Rotations not integer multiple of PI/2
  2. Axis that has only positive direction

Expected behavior

The labels should be near the tips of each axis.

How to reproduce the issue

Case 1
class GetAxisLabelsExample(Scene):
    def construct(self):
        ax = Axes(x_length=4, y_length=4).rotate(0.5*PI/2)
        labels = ax.get_axis_labels(Tex("x"), Text("y"))
        self.add(ax, labels)
Case 2
class GetAxisLabelsExample(Scene):
    def construct(self):
        ax = Axes(x_length=4, y_length=4, x_range=[0, 12]).rotate(2*PI/2)
        labels = ax.get_axis_labels(Tex("x"), Text("y"))
        self.add(ax, labels)

Additional media files

Case 1

Image

Case 2

Image

BayezidBostami06 avatar Jun 12 '25 04:06 BayezidBostami06

I solved case 2 using get_x_axis_label() and get_y_axis_label():

class GetAxisLabelsExample(Scene):
    def construct(self):
        ax = Axes(x_length=4, y_length=4, x_range=[0, 12]).rotate(2*PI/2)
        labels = [ax.get_x_axis_label(Tex("x"), edge=UL, direction=UL), ax.get_y_axis_label(Tex("y"))]
        self.add(ax, *labels)

Image

But this whole process needs a huge refinement. I should be able to set the labels using Axes.label(x_label = 'x', y_label = 'y').

BayezidBostami06 avatar Jun 12 '25 04:06 BayezidBostami06

The issue is that the method get_axis_labels assumes that the Axes are not rotated.

By default the label on the x-axis is put on the top right corner of the bounding box of the axis, the label is shifted in the UP + RIGHT = UR direction. This can be found on line 259 in coordinate_systems.py

    def get_x_axis_label(
        self,
        label: float | str | Mobject,
        edge: Sequence[float] = UR,
        direction: Sequence[float] = UR,
        buff: float = SMALL_BUFF,
        **kwargs,
    ) -> Mobject:

Similarly the label on the y-axis is put on the upper right corner of the bounding box, but now shifted more to the right than upwards. This is found line 302 in coordinate_systems.py

    def get_y_axis_label(
        self,
        label: float | str | Mobject,
        edge: Sequence[float] = UR,
        direction: Sequence[float] = UP * 0.5 + RIGHT,
        buff: float = SMALL_BUFF,
        **kwargs,
    ) -> Mobject:

If you want more control over the placement of the axis labels you have to call these methods directly.

Please see this example below:

from manim import *

class GetAxisLabelsExample(Scene):
    def construct(self):
        ax1 = Axes(x_length=2, y_length=2, x_range=[0, 12]).move_to(3*LEFT + 2*UP)
        labels1 = ax1.get_axis_labels(x_label=Tex("x"), y_label=Tex("y"))
        self.add(ax1, labels1)
        
        ax2 = Axes(x_length=2, y_length=2, x_range=[0, 12]).rotate(PI/2).move_to(2*UP)
        labels2 = ax2.get_axis_labels(x_label=Tex("x"), y_label=Tex("y"))
        self.add(ax2, labels2)

        ax3 = Axes(x_length=2, y_length=2, x_range=[0, 12]).rotate(2*PI/2).move_to(3*RIGHT + 2*UP)
        labels3 = ax3.get_axis_labels(x_label=Tex("x"), 
                                      y_label=Tex("y"))
        self.add(ax3, labels3)


        ax4 = Axes(x_length=2, y_length=2, x_range=[0, 12]).move_to(3*LEFT + 2*DOWN)
        labels4x = ax4.get_x_axis_label(Tex("x"))
        labels4y = ax4.get_y_axis_label(Tex("y"))
        self.add(ax4, labels4x, labels4y)
        
        ax5 = Axes(x_length=2, y_length=2, x_range=[0, 12]).rotate(PI/2).move_to(2*DOWN)
        labels5x = ax5.get_x_axis_label(Tex("x"))
        labels5y = ax5.get_y_axis_label(Tex("y"), edge=LEFT+UP)
        self.add(ax5, labels5x, labels5y)

        ax6 = Axes(x_length=2, y_length=2, x_range=[0, 12]).rotate(2*PI/2).move_to(3*RIGHT + 2*DOWN)
        labels6x = ax6.get_x_axis_label(Tex("x"), edge=LEFT + UP)
        labels6y = ax6.get_y_axis_label(Tex("y"), edge=RIGHT + DOWN)
        self.add(ax6, labels6x, labels6y)

Which renders as

Image

henrikmidtiby avatar Jul 28 '25 13:07 henrikmidtiby