Repository: ManimCommunity/manim

Issue:
FadeOut on DiGraph does not work smoothly
## Description of bug / unexpected behavior
ManimCE v0.18.1

Using `self.play(Fadeout())` on a `DiGraph()` object does not correctly fade out the edges, only the nodes and arrow tips. Removing the individual subobjects works.

Link to the [Discord discussion](https://discord.com/channels/581738731934056449/1236723849937158145/1236723849937158145)

## Expected behavior
All elements of a `DiGraph()` should fade smoothly out.

## How to reproduce the issue
<details><summary>Code for reproducing the problem</summary>

```py
class IntroductionToGraphs(Scene):
    def construct(self):
        vertexs = [num for num in range(1,7)]
        arestes = [(1,2), (3,1), (1,5), (1,6), (5,1), (5,6), (3,4)]
        layout = "circular"

        graf_arestes1 = Graph(vertexs, arestes, labels = True, layout = layout)
        graf_arestes2 = DiGraph(vertexs, arestes, labels = True, layout = layout)
        graf_arestes3 = graf_arestes2.copy()

        graf_arestes1.scale_to_fit_width(14/3).to_edge(LEFT, buff=0)
        graf_arestes2.scale_to_fit_width(14/3)
        graf_arestes3.scale_to_fit_width(14/3).to_edge(RIGHT, buff=0)

        self.play(
            Create(graf_arestes1),
            Create(graf_arestes2),
            Create(graf_arestes3),
        )
        self.wait()
        self.play(
            FadeOut(graf_arestes1),
            FadeOut(graf_arestes2),
            FadeOut(*[obj for obj in graf_arestes3])
        )
```
</details>


## Additional media files
<details><summary>Images/GIFs</summary>

https://github.com/ManimCommunity/manim/assets/8582807/20f06405-6c81-453c-ac62-c6d32f16635c


</details>


## System specifications

<details><summary>System Details</summary>
irrelevant - the problem occurs on both Windows 10 and Manimator
</details>




Discussion:
This is an issue with the edge updater in the DiGraph. I'll take a look at it.

Resolving patch:
diff --git a/manim/mobject/geometry/line.py b/manim/mobject/geometry/line.py
--- a/manim/mobject/geometry/line.py
+++ b/manim/mobject/geometry/line.py
@@ -41,8 +41,8 @@
 class Line(TipableVMobject):
     def __init__(
         self,
-        start: Point3D = LEFT,
-        end: Point3D = RIGHT,
+        start: Point3D | Mobject = LEFT,
+        end: Point3D | Mobject = RIGHT,
         buff: float = 0,
         path_arc: float | None = None,
         **kwargs,
@@ -63,16 +63,32 @@ def generate_points(self) -> None:
 
     def set_points_by_ends(
         self,
-        start: Point3D,
-        end: Point3D,
+        start: Point3D | Mobject,
+        end: Point3D | Mobject,
         buff: float = 0,
         path_arc: float = 0,
     ) -> None:
+        """Sets the points of the line based on its start and end points.
+        Unlike :meth:`put_start_and_end_on`, this method respects `self.buff` and
+        Mobject bounding boxes.
+
+        Parameters
+        ----------
+        start
+            The start point or Mobject of the line.
+        end
+            The end point or Mobject of the line.
+        buff
+            The empty space between the start and end of the line, by default 0.
+        path_arc
+            The angle of a circle spanned by this arc, by default 0 which is a straight line.
+        """
+        self._set_start_and_end_attrs(start, end)
         if path_arc:
             arc = ArcBetweenPoints(self.start, self.end, angle=self.path_arc)
             self.set_points(arc.points)
         else:
-            self.set_points_as_corners([start, end])
+            self.set_points_as_corners([self.start, self.end])
 
         self._account_for_buff(buff)
 
@@ -93,7 +109,9 @@ def _account_for_buff(self, buff: float) -> Self:
         self.pointwise_become_partial(self, buff_proportion, 1 - buff_proportion)
         return self
 
-    def _set_start_and_end_attrs(self, start: Point3D, end: Point3D) -> None:
+    def _set_start_and_end_attrs(
+        self, start: Point3D | Mobject, end: Point3D | Mobject
+    ) -> None:
         # If either start or end are Mobjects, this
         # gives their centers
         rough_start = self._pointify(start)
diff --git a/manim/mobject/graph.py b/manim/mobject/graph.py
--- a/manim/mobject/graph.py
+++ b/manim/mobject/graph.py
@@ -561,6 +561,7 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
         all other configuration options for a vertex.
     edge_type
         The mobject class used for displaying edges in the scene.
+        Must be a subclass of :class:`~.Line` for default updaters to work.
     edge_config
         Either a dictionary containing keyword arguments to be passed
         to the class specified via ``edge_type``, or a dictionary whose
@@ -1559,7 +1560,12 @@ def _populate_edge_dict(
     def update_edges(self, graph):
         for (u, v), edge in graph.edges.items():
             # Undirected graph has a Line edge
-            edge.put_start_and_end_on(graph[u].get_center(), graph[v].get_center())
+            edge.set_points_by_ends(
+                graph[u].get_center(),
+                graph[v].get_center(),
+                buff=self._edge_config.get("buff", 0),
+                path_arc=self._edge_config.get("path_arc", 0),
+            )
 
     def __repr__(self: Graph) -> str:
         return f"Undirected graph on {len(self.vertices)} vertices and {len(self.edges)} edges"
@@ -1768,10 +1774,15 @@ def update_edges(self, graph):
         deformed.
         """
         for (u, v), edge in graph.edges.items():
-            edge_type = type(edge)
             tip = edge.pop_tips()[0]
-            new_edge = edge_type(self[u], self[v], **self._edge_config[(u, v)])
-            edge.become(new_edge)
+            # Passing the Mobject instead of the vertex makes the tip
+            # stop on the bounding box of the vertex.
+            edge.set_points_by_ends(
+                graph[u],
+                graph[v],
+                buff=self._edge_config.get("buff", 0),
+                path_arc=self._edge_config.get("path_arc", 0),
+            )
             edge.add_tip(tip)
 
     def __repr__(self: DiGraph) -> str:


Test patch: