Repository: ManimCommunity/manim

Issue:
Add ability to remove pairs of ghost vectors in LinearTransformationScene
 If one transformation is applied to `self.plane`, the plane is reset, and we want to show another transformation, the old ghost vectors from the first transformation will still remain. 
If ghost vectors in LinearTransformationScene were, say lists of rules tuples, or possibly a VGroup, then it would allow us to write something like `self.remove(self.ghostVectors[0])`, and remove the first pair(s) of ghost vectors created.



Discussion:

Resolving patch:
diff --git a/manim/scene/vector_space_scene.py b/manim/scene/vector_space_scene.py
--- a/manim/scene/vector_space_scene.py
+++ b/manim/scene/vector_space_scene.py
@@ -572,7 +572,7 @@ def __init__(self, **kwargs):
                     self,
                     show_coordinates=True,
                     leave_ghost_vectors=True,
-                    *kwargs
+                    **kwargs
                 )
 
             def construct(self):
@@ -616,6 +616,8 @@ def __init__(
             },
         }
 
+        self.ghost_vectors = VGroup()
+
         self.foreground_plane_kwargs = {
             "x_range": np.array([-config["frame_width"], config["frame_width"], 1.0]),
             "y_range": np.array([-config["frame_width"], config["frame_width"], 1.0]),
@@ -741,6 +743,13 @@ def add_moving_mobject(
         mobject.target = target_mobject
         self.add_special_mobjects(self.moving_mobjects, mobject)
 
+    def get_ghost_vectors(self) -> VGroup:
+        """
+        Returns all ghost vectors ever added to ``self``. Each element is a ``VGroup`` of
+        two ghost vectors.
+        """
+        return self.ghost_vectors
+
     def get_unit_square(
         self, color: str = YELLOW, opacity: float = 0.3, stroke_width: float = 3
     ):
@@ -996,8 +1005,11 @@ def get_piece_movement(self, pieces: list | tuple | np.ndarray):
         """
         start = VGroup(*pieces)
         target = VGroup(*(mob.target for mob in pieces))
-        if self.leave_ghost_vectors:
-            self.add(start.copy().fade(0.7))
+        # don't add empty VGroups
+        if self.leave_ghost_vectors and start.submobjects:
+            # start.copy() gives a VGroup of Vectors
+            self.ghost_vectors.add(start.copy().fade(0.7))
+            self.add(self.ghost_vectors[-1])
         return Transform(start, target, lag_ratio=0)
 
     def get_moving_mobject_movement(self, func: Callable[[np.ndarray], np.ndarray]):


Test patch:
diff --git a/tests/test_linear_transformation_scene.py b/tests/test_linear_transformation_scene.py
new file mode 100644
--- /dev/null
+++ b/tests/test_linear_transformation_scene.py
@@ -0,0 +1,24 @@
+from manim import RIGHT, UP, LinearTransformationScene, Vector, VGroup
+
+__module_test__ = "vector_space_scene"
+
+
+def test_ghost_vectors_len_and_types():
+    scene = LinearTransformationScene()
+    scene.leave_ghost_vectors = True
+
+    # prepare vectors (they require a vmobject as their target)
+    v1, v2 = Vector(RIGHT), Vector(RIGHT)
+    v1.target, v2.target = Vector(UP), Vector(UP)
+
+    # ghost_vector addition is in this method
+    scene.get_piece_movement((v1, v2))
+
+    ghosts = scene.get_ghost_vectors()
+    assert len(ghosts) == 1
+    # check if there are two vectors in the ghost vector VGroup
+    assert len(ghosts[0]) == 2
+
+    # check types of ghost vectors
+    assert isinstance(ghosts, VGroup) and isinstance(ghosts[0], VGroup)
+    assert all(isinstance(x, Vector) for x in ghosts[0])