Repository: ManimCommunity/manim

Issue:
`~.get_axis_labels()` implemented for `Axes`, but not `ThreeDAxes`
## Description of bug / unexpected behavior
<!-- Add a clear and concise description of the problem you encountered. -->
Shouldn't the method `~.get_axis_labels()` work for both `Axes` and `ThreeDAxes`?  At the moment it only works for `Axes` and returns an error when trying to use with `ThreeDAxes`.  This doesn't make sense, it should work for both.

## Expected behavior
<!-- Add a clear and concise description of what you expected to happen. -->
The method `~.get_axis_labels()` should work the same for both `Axes` and `ThreeDAxes`.

## How to reproduce the issue
<!-- Provide a piece of code illustrating the undesired behavior. -->

<details><summary>Code for reproducing the problem</summary>

```py
class Test(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes()
        axes_labels = axes.get_axis_labels(Tex("x"), Tex("y"), Tex("z"))
        self.add(VGroup(axes, axes_labels))
```

</details>


## Logs
<details><summary>Terminal output</summary>
<!-- Add "-v DEBUG" when calling manim to generate more detailed logs -->

```
Manim Community v0.15.2

╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│                                                                                                  │
│ /home/alexlembcke/.local/lib/python3.10/site-packages/manim/cli/render/commands.py:118 in render │
│                                                                                                  │
│   115 │   │   for SceneClass in scene_classes_from_file(file):                                   │
│   116 │   │   │   try:                                                                           │
│   117 │   │   │   │   scene = SceneClass()                                                       │
│ ❱ 118 │   │   │   │   scene.render()                                                             │
│   119 │   │   │   except Exception:                                                              │
│   120 │   │   │   │   error_console.print_exception()                                            │
│   121 │   │   │   │   sys.exit(1)                                                                │
│ /home/alexlembcke/.local/lib/python3.10/site-packages/manim/scene/scene.py:222 in render         │
│                                                                                                  │
│    219 │   │   """                                                                               │
│    220 │   │   self.setup()                                                                      │
│    221 │   │   try:                                                                              │
│ ❱  222 │   │   │   self.construct()                                                              │
│    223 │   │   except EndSceneEarlyException:                                                    │
│    224 │   │   │   pass                                                                          │
│    225 │   │   except RerunSceneException as e:                                                  │
│                                                                                                  │
│ /home/alexlembcke/video.py:877 in construct                     │
│                                                                                                  │
│   874 class Test(ThreeDScene):                                                                   │
│   875 │   def construct(self):                                                                   │
│   876 │   │   axes = ThreeDAxes()                                                                │
│ ❱ 877 │   │   axes_labels = axes.get_axis_labels(Tex("x"), Tex("y"), Tex("z"))                  │
│   878 │   │   self.add(VGroup(axes, axes_labels))                                                │
│   879                                                                                            │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
TypeError: CoordinateSystem.get_axis_labels() takes from 1 to 3 positional arguments but 4 were given
```

<!-- Insert screenshots here (only when absolutely necessary, we prefer copy/pasted output!) -->

</details>


## System specifications

<details><summary>System Details</summary>

- OS (with version, e.g., Windows 10 v2004 or macOS 10.15 (Catalina)):
- RAM:
- Python version (`python/py/python3 --version`):
- Installed modules (provide output from `pip list`):
```
PASTE HERE
```
</details>

<details><summary>LaTeX details</summary>

+ LaTeX distribution (e.g. TeX Live 2020):
+ Installed LaTeX packages:
<!-- output of `tlmgr list --only-installed` for TeX Live or a screenshot of the Packages page for MikTeX -->
</details>

<details><summary>FFMPEG</summary>

Output of `ffmpeg -version`:

```
PASTE HERE
```
</details>

## Additional comments
<!-- Add further context that you think might be relevant for this issue here. -->



Discussion:
Yes and no, there is a degree of freedom associated with labels in 3D space. Do you want them always facing the camera, aligned with the axis itself. At the end of the axis or above that you can always read it no matter the orientation.

In 2D it's simply just putting it beside the axis. So unless you propose a system which is sticking to the original function behavior but also solves the problem, sure we can add it!
I would say there should be reasonable defaults for those parameters.  You can already add numbers to the axes by passing `axis_config = {"include_numbers": True}` when creating `ThreeDAxes`, so why not use the defaults used by the numbers?

Resolving patch:
diff --git a/manim/mobject/graphing/coordinate_systems.py b/manim/mobject/graphing/coordinate_systems.py
--- a/manim/mobject/graphing/coordinate_systems.py
+++ b/manim/mobject/graphing/coordinate_systems.py
@@ -364,52 +364,8 @@ def _get_axis_label(
         label.shift_onto_screen(buff=MED_SMALL_BUFF)
         return label
 
-    def get_axis_labels(
-        self,
-        x_label: float | str | Mobject = "x",
-        y_label: float | str | Mobject = "y",
-    ) -> VGroup:
-        """Defines labels for the x_axis and y_axis of the graph.
-
-        For increased control over the position of the labels,
-        use :meth:`get_x_axis_label` and :meth:`get_y_axis_label`.
-
-        Parameters
-        ----------
-        x_label
-            The label for the x_axis. Defaults to :class:`~.MathTex` for ``str`` and ``float`` inputs.
-        y_label
-            The label for the y_axis. Defaults to :class:`~.MathTex` for ``str`` and ``float`` inputs.
-
-        Returns
-        -------
-        :class:`~.VGroup`
-            A :class:`~.Vgroup` of the labels for the x_axis and y_axis.
-
-
-        .. seealso::
-            :class:`get_x_axis_label`
-            :class:`get_y_axis_label`
-
-        Examples
-        --------
-        .. manim:: GetAxisLabelsExample
-            :save_last_frame:
-
-            class GetAxisLabelsExample(Scene):
-                def construct(self):
-                    ax = Axes()
-                    labels = ax.get_axis_labels(
-                        Tex("x-axis").scale(0.7), Text("y-axis").scale(0.45)
-                    )
-                    self.add(ax, labels)
-        """
-
-        self.axis_labels = VGroup(
-            self.get_x_axis_label(x_label),
-            self.get_y_axis_label(y_label),
-        )
-        return self.axis_labels
+    def get_axis_labels(self):
+        raise NotImplementedError()
 
     def add_coordinates(
         self,
@@ -2124,6 +2080,53 @@ def get_axes(self) -> VGroup:
         """
         return self.axes
 
+    def get_axis_labels(
+        self,
+        x_label: float | str | Mobject = "x",
+        y_label: float | str | Mobject = "y",
+    ) -> VGroup:
+        """Defines labels for the x_axis and y_axis of the graph.
+
+        For increased control over the position of the labels,
+        use :meth:`get_x_axis_label` and :meth:`get_y_axis_label`.
+
+        Parameters
+        ----------
+        x_label
+            The label for the x_axis. Defaults to :class:`~.MathTex` for ``str`` and ``float`` inputs.
+        y_label
+            The label for the y_axis. Defaults to :class:`~.MathTex` for ``str`` and ``float`` inputs.
+
+        Returns
+        -------
+        :class:`~.VGroup`
+            A :class:`~.VGroup` of the labels for the x_axis and y_axis.
+
+
+        .. seealso::
+            :meth:`.get_x_axis_label`
+            :meth:`.get_y_axis_label`
+
+        Examples
+        --------
+        .. manim:: GetAxisLabelsExample
+            :save_last_frame:
+
+            class GetAxisLabelsExample(Scene):
+                def construct(self):
+                    ax = Axes()
+                    labels = ax.get_axis_labels(
+                        Tex("x-axis").scale(0.7), Text("y-axis").scale(0.45)
+                    )
+                    self.add(ax, labels)
+        """
+
+        self.axis_labels = VGroup(
+            self.get_x_axis_label(x_label),
+            self.get_y_axis_label(y_label),
+        )
+        return self.axis_labels
+
     def plot_line_graph(
         self,
         x_values: Iterable[float],
@@ -2361,6 +2364,57 @@ def make_func(axis):
                 submob.get_unit_normal = lambda a: np.ones(3)
                 submob.set_sheen(0.2)
 
+    def get_y_axis_label(
+        self,
+        label: float | str | Mobject,
+        edge: Sequence[float] = UR,
+        direction: Sequence[float] = UR,
+        buff: float = SMALL_BUFF,
+        rotation=PI / 2,
+        rotation_axis=OUT,
+        **kwargs,
+    ) -> Mobject:
+        """Generate a y-axis label.
+
+        Parameters
+        ----------
+        label
+            The label. Defaults to :class:`~.MathTex` for ``str`` and ``float`` inputs.
+        edge
+            The edge of the y-axis to which the label will be added, by default ``UR``.
+        direction
+            Allows for further positioning of the label from an edge, by default ``UR``.
+        buff
+            The distance of the label from the line, by default ``SMALL_BUFF``.
+        rotation
+            The angle at which to rotate the label, by default ``PI/2``.
+        rotation_axis
+            The axis about which to rotate the label, by default ``OUT``.
+
+        Returns
+        -------
+        :class:`~.Mobject`
+            The positioned label.
+
+        Examples
+        --------
+        .. manim:: GetYAxisLabelExample
+            :save_last_frame:
+
+            class GetYAxisLabelExample(ThreeDScene):
+                def construct(self):
+                    ax = ThreeDAxes()
+                    lab = ax.get_y_axis_label(Tex("$y$-label"))
+                    self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
+                    self.add(ax, lab)
+        """
+
+        positioned_label = self._get_axis_label(
+            label, self.get_y_axis(), edge, direction, buff=buff, **kwargs
+        )
+        positioned_label.rotate(rotation, axis=rotation_axis)
+        return positioned_label
+
     def get_z_axis_label(
         self,
         label: float | str | Mobject,
@@ -2378,11 +2432,11 @@ def get_z_axis_label(
         label
             The label. Defaults to :class:`~.MathTex` for ``str`` and ``float`` inputs.
         edge
-            The edge of the x-axis to which the label will be added, by default ``UR``.
+            The edge of the z-axis to which the label will be added, by default ``OUT``.
         direction
-            Allows for further positioning of the label from an edge, by default ``UR``.
+            Allows for further positioning of the label from an edge, by default ``RIGHT``.
         buff
-            The distance of the label from the line.
+            The distance of the label from the line, by default ``SMALL_BUFF``.
         rotation
             The angle at which to rotate the label, by default ``PI/2``.
         rotation_axis
@@ -2412,6 +2466,59 @@ def construct(self):
         positioned_label.rotate(rotation, axis=rotation_axis)
         return positioned_label
 
+    def get_axis_labels(
+        self,
+        x_label: float | str | Mobject = "x",
+        y_label: float | str | Mobject = "y",
+        z_label: float | str | Mobject = "z",
+    ) -> VGroup:
+        """Defines labels for the x_axis and y_axis of the graph.
+
+        For increased control over the position of the labels,
+        use :meth:`.get_x_axis_label` and :meth:`.get_y_axis_label`.
+
+        Parameters
+        ----------
+        x_label
+            The label for the x_axis. Defaults to :class:`~.MathTex` for ``str`` and ``float`` inputs.
+        y_label
+            The label for the y_axis. Defaults to :class:`~.MathTex` for ``str`` and ``float`` inputs.
+        z_label
+            The label for the z_axis. Defaults to :class:`~.MathTex` for ``str`` and ``float`` inputs.
+
+        Returns
+        -------
+        :class:`~.VGroup`
+            A :class:`~.VGroup` of the labels for the x_axis, y_axis, and z_axis.
+
+
+        .. seealso::
+            :meth:`.get_x_axis_label`
+            :meth:`.get_y_axis_label`
+            :meth:`.get_z_axis_label`
+
+        Examples
+        --------
+        .. manim:: GetAxisLabelsExample
+            :save_last_frame:
+
+            class GetAxisLabelsExample(ThreeDScene):
+                def construct(self):
+                    self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
+                    axes = ThreeDAxes()
+                    labels = axes.get_axis_labels(
+                        Tex("x-axis").scale(0.7), Text("y-axis").scale(0.45), Text("z-axis").scale(0.45)
+                    )
+                    self.add(axes, labels)
+        """
+
+        self.axis_labels = VGroup(
+            self.get_x_axis_label(x_label),
+            self.get_y_axis_label(y_label),
+            self.get_z_axis_label(z_label),
+        )
+        return self.axis_labels
+
 
 class NumberPlane(Axes):
     """Creates a cartesian plane with background lines.


Test patch: