From eb389d0f518b7e6b6caf4900f18a7067668431b5 Mon Sep 17 00:00:00 2001 From: Rotem Date: Sun, 26 Apr 2026 18:02:50 +0300 Subject: [PATCH 1/9] Add last reference option to arrange --- manim/mobject/mobject.py | 21 ++++++++++++++++++-- tests/module/mobject/mobject/test_mobject.py | 15 ++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 721ce57356..773884dcbf 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -2555,16 +2555,28 @@ def family_members_with_points(self) -> list[Mobject]: """ return [m for m in self.get_family() if m.get_num_points() > 0] - + def arrange( self, direction: Vector3DLike = RIGHT, buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, center: bool = True, + last: bool = False, **kwargs: Any, ) -> Self: """Sorts :class:`~.Mobject` next to each other on screen. + Parameters + ---------- + direction + The direction in which the submobjects should be arranged. + buff + The distance between neighboring submobjects. + center + Whether to center the mobject after arranging. + last + If ``True``, arrange submobjects using the last submobject as the reference. + Examples -------- @@ -2580,8 +2592,13 @@ def construct(self): x = VGroup(s1, s2, s3, s4).set_x(0).arrange(buff=1.0) self.add(x) """ - for m1, m2 in zip(self.submobjects[:-1], self.submobjects[1:], strict=True): + submobjects = list(self.submobjects) + if last: + submobjects.reverse() + + for m1, m2 in zip(submobjects[:-1], submobjects[1:], strict=True): m2.next_to(m1, direction, buff, **kwargs) + if center: self.center() return self diff --git a/tests/module/mobject/mobject/test_mobject.py b/tests/module/mobject/mobject/test_mobject.py index 203d312627..6f0b0fc64e 100644 --- a/tests/module/mobject/mobject/test_mobject.py +++ b/tests/module/mobject/mobject/test_mobject.py @@ -243,3 +243,18 @@ def test_apply_matrix_about_vertex_view(): # The first vertex should remain in the same position (within numerical precision) transformed_vertices = triangle.get_vertices() np.testing.assert_allclose(transformed_vertices[0], first_vertex, atol=1e-6) + +def test_mobject_arrange_last_reference(): + """Test Mobject.arrange() can use the last submobject as the reference.""" + square1 = Square() + square2 = Square() + square3 = Square() + + group = VGroup(square1, square2, square3) + original_last_center = square3.get_center().copy() + + group.arrange(last=True, center=False) + + assert np.allclose(square3.get_center(), original_last_center) + assert square2.get_x() < square3.get_x() + assert square1.get_x() < square2.get_x() \ No newline at end of file From 475f85438409004cb3701befec43fd978cc08aca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 15:03:52 +0000 Subject: [PATCH 2/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- manim/mobject/mobject.py | 2 +- tests/module/mobject/mobject/test_mobject.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 773884dcbf..199dcb39dd 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -2555,7 +2555,7 @@ def family_members_with_points(self) -> list[Mobject]: """ return [m for m in self.get_family() if m.get_num_points() > 0] - + def arrange( self, direction: Vector3DLike = RIGHT, diff --git a/tests/module/mobject/mobject/test_mobject.py b/tests/module/mobject/mobject/test_mobject.py index 6f0b0fc64e..5489bebebf 100644 --- a/tests/module/mobject/mobject/test_mobject.py +++ b/tests/module/mobject/mobject/test_mobject.py @@ -244,6 +244,7 @@ def test_apply_matrix_about_vertex_view(): transformed_vertices = triangle.get_vertices() np.testing.assert_allclose(transformed_vertices[0], first_vertex, atol=1e-6) + def test_mobject_arrange_last_reference(): """Test Mobject.arrange() can use the last submobject as the reference.""" square1 = Square() @@ -257,4 +258,4 @@ def test_mobject_arrange_last_reference(): assert np.allclose(square3.get_center(), original_last_center) assert square2.get_x() < square3.get_x() - assert square1.get_x() < square2.get_x() \ No newline at end of file + assert square1.get_x() < square2.get_x() From 354a8b7e29782144957fb2843ff365ddd19efee8 Mon Sep 17 00:00:00 2001 From: Rotem Date: Sun, 26 Apr 2026 18:13:09 +0300 Subject: [PATCH 3/9] Fix arrange last reference test --- tests/module/mobject/mobject/test_mobject.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/module/mobject/mobject/test_mobject.py b/tests/module/mobject/mobject/test_mobject.py index 5489bebebf..6ee00c708c 100644 --- a/tests/module/mobject/mobject/test_mobject.py +++ b/tests/module/mobject/mobject/test_mobject.py @@ -257,5 +257,5 @@ def test_mobject_arrange_last_reference(): group.arrange(last=True, center=False) assert np.allclose(square3.get_center(), original_last_center) - assert square2.get_x() < square3.get_x() - assert square1.get_x() < square2.get_x() + assert square3.get_x() < square2.get_x() + assert square2.get_x() < square1.get_x() From 82abb415e16489db8612e61379188a6a8fa68d7d Mon Sep 17 00:00:00 2001 From: Rotem Date: Sun, 26 Apr 2026 18:32:04 +0300 Subject: [PATCH 4/9] Use reference submobject for arrange --- manim/mobject/mobject.py | 29 +++++++++++++++----- tests/module/mobject/mobject/test_mobject.py | 14 +++++----- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 199dcb39dd..62ee3d1ad3 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -2556,12 +2556,12 @@ def family_members_with_points(self) -> list[Mobject]: """ return [m for m in self.get_family() if m.get_num_points() > 0] - def arrange( + def arrange( self, direction: Vector3DLike = RIGHT, buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, center: bool = True, - last: bool = False, + reference: Mobject | None = None, **kwargs: Any, ) -> Self: """Sorts :class:`~.Mobject` next to each other on screen. @@ -2574,8 +2574,9 @@ def arrange( The distance between neighboring submobjects. center Whether to center the mobject after arranging. - last - If ``True``, arrange submobjects using the last submobject as the reference. + reference + The submobject to use as the reference for the arrangement. If not + set, the first submobject is used. Examples -------- @@ -2593,12 +2594,26 @@ def construct(self): self.add(x) """ submobjects = list(self.submobjects) - if last: - submobjects.reverse() - for m1, m2 in zip(submobjects[:-1], submobjects[1:], strict=True): + if reference is None: + reference_index = 0 + else: + reference_index = submobjects.index(reference) + + for m1, m2 in zip( + submobjects[reference_index:], + submobjects[reference_index + 1 :], + strict=True, + ): m2.next_to(m1, direction, buff, **kwargs) + for m1, m2 in zip( + reversed(submobjects[:reference_index]), + reversed(submobjects[1 : reference_index + 1]), + strict=True, + ): + m1.next_to(m2, -direction, buff, **kwargs) + if center: self.center() return self diff --git a/tests/module/mobject/mobject/test_mobject.py b/tests/module/mobject/mobject/test_mobject.py index 6ee00c708c..765739bc5b 100644 --- a/tests/module/mobject/mobject/test_mobject.py +++ b/tests/module/mobject/mobject/test_mobject.py @@ -245,17 +245,17 @@ def test_apply_matrix_about_vertex_view(): np.testing.assert_allclose(transformed_vertices[0], first_vertex, atol=1e-6) -def test_mobject_arrange_last_reference(): - """Test Mobject.arrange() can use the last submobject as the reference.""" +def test_mobject_arrange_reference(): + """Test Mobject.arrange() can use a submobject as the reference.""" square1 = Square() square2 = Square() square3 = Square() group = VGroup(square1, square2, square3) - original_last_center = square3.get_center().copy() + original_reference_center = square3.get_center().copy() - group.arrange(last=True, center=False) + group.arrange(reference=square3, center=False) - assert np.allclose(square3.get_center(), original_last_center) - assert square3.get_x() < square2.get_x() - assert square2.get_x() < square1.get_x() + assert np.allclose(square3.get_center(), original_reference_center) + assert square1.get_x() < square2.get_x() + assert square2.get_x() < square3.get_x() From cc1142eeb2f5b16bae79aa2f456fe034fccfc00e Mon Sep 17 00:00:00 2001 From: Rotem Date: Sun, 26 Apr 2026 18:39:05 +0300 Subject: [PATCH 5/9] Fix arrange indentation --- manim/mobject/mobject.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 62ee3d1ad3..94c07b5c73 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -2564,19 +2564,19 @@ def arrange( reference: Mobject | None = None, **kwargs: Any, ) -> Self: - """Sorts :class:`~.Mobject` next to each other on screen. + """Sorts :class:`~.Mobject` next to each other on screen. - Parameters - ---------- - direction - The direction in which the submobjects should be arranged. + Parameters + ---------- + direction + The direction in which the submobjects should be arranged. buff - The distance between neighboring submobjects. + The distance between neighboring submobjects. center - Whether to center the mobject after arranging. + Whether to center the mobject after arranging. reference - The submobject to use as the reference for the arrangement. If not - set, the first submobject is used. + The submobject to use as the reference for the arrangement. If not + set, the first submobject is used. Examples -------- @@ -2617,7 +2617,7 @@ def construct(self): if center: self.center() return self - + def arrange_in_grid( self, rows: int | None = None, From 929d321db35ae45e376dc0bf996898a1d8aa4a4e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 15:40:06 +0000 Subject: [PATCH 6/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- manim/mobject/mobject.py | 69 ++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 94c07b5c73..c9ab56af67 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -2557,42 +2557,43 @@ def family_members_with_points(self) -> list[Mobject]: return [m for m in self.get_family() if m.get_num_points() > 0] def arrange( - self, - direction: Vector3DLike = RIGHT, - buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, - center: bool = True, - reference: Mobject | None = None, - **kwargs: Any, - ) -> Self: - """Sorts :class:`~.Mobject` next to each other on screen. + self, + direction: Vector3DLike = RIGHT, + buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, + center: bool = True, + reference: Mobject | None = None, + **kwargs: Any, + ) -> Self: + """Sorts :class:`~.Mobject` next to each other on screen. Parameters - ---------- - direction - The direction in which the submobjects should be arranged. - buff - The distance between neighboring submobjects. - center - Whether to center the mobject after arranging. - reference - The submobject to use as the reference for the arrangement. If not - set, the first submobject is used. - - Examples - -------- - - .. manim:: Example - :save_last_frame: + --------- + direction + The direction in which the submobjects should be arranged. + buff + The distance between neighboring submobjects. + center + Whether to center the mobject after arranging. + reference + The submobject to use as the reference for the arrangement. If not + set, the first submobject is used. + + Examples + -------- + + .. manim:: Example + :save_last_frame: + + class Example(Scene): + def construct(self): + s1 = Square() + s2 = Square() + s3 = Square() + s4 = Square() + x = VGroup(s1, s2, s3, s4).set_x(0).arrange(buff=1.0) + self.add(x) + """ - class Example(Scene): - def construct(self): - s1 = Square() - s2 = Square() - s3 = Square() - s4 = Square() - x = VGroup(s1, s2, s3, s4).set_x(0).arrange(buff=1.0) - self.add(x) - """ submobjects = list(self.submobjects) if reference is None: @@ -2617,7 +2618,7 @@ def construct(self): if center: self.center() return self - + def arrange_in_grid( self, rows: int | None = None, From f0c195b1246a036abbc2d78e9d1e78cab1be4ed7 Mon Sep 17 00:00:00 2001 From: Rotem Date: Sun, 26 Apr 2026 21:41:45 +0300 Subject: [PATCH 7/9] Restore arrange method indentation --- manim/mobject/mobject.py | 75 ++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index c9ab56af67..564011b1dd 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -2530,7 +2530,7 @@ def get_family(self, recurse: bool = True) -> list[Mobject]: all_mobjects = [self] + list(it.chain(*sub_families)) return remove_list_redundancies(all_mobjects) - def family_members_with_points(self) -> list[Mobject]: + def family_members_with_points(self) -> list[Mobject]: """Filters the list of family members (generated by :meth:`.get_family`) to include only mobjects with points. Returns @@ -2556,44 +2556,43 @@ def family_members_with_points(self) -> list[Mobject]: """ return [m for m in self.get_family() if m.get_num_points() > 0] - def arrange( - self, - direction: Vector3DLike = RIGHT, - buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, - center: bool = True, - reference: Mobject | None = None, - **kwargs: Any, - ) -> Self: - """Sorts :class:`~.Mobject` next to each other on screen. - - Parameters - --------- - direction - The direction in which the submobjects should be arranged. - buff - The distance between neighboring submobjects. - center - Whether to center the mobject after arranging. - reference - The submobject to use as the reference for the arrangement. If not - set, the first submobject is used. - - Examples - -------- - - .. manim:: Example - :save_last_frame: - - class Example(Scene): - def construct(self): - s1 = Square() - s2 = Square() - s3 = Square() - s4 = Square() - x = VGroup(s1, s2, s3, s4).set_x(0).arrange(buff=1.0) - self.add(x) - """ + def arrange( + self, + direction: Vector3DLike = RIGHT, + buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, + center: bool = True, + reference: Mobject | None = None, + **kwargs: Any, + ) -> Self: + """Sorts :class:`~.Mobject` next to each other on screen. + + Parameters + ---------- + direction + The direction in which the submobjects should be arranged. + buff + The distance between neighboring submobjects. + center + Whether to center the mobject after arranging. + reference + The submobject to use as the reference for the arrangement. If not + set, the first submobject is used. + + Examples + -------- + .. manim:: Example + :save_last_frame: + + class Example(Scene): + def construct(self): + s1 = Square() + s2 = Square() + s3 = Square() + s4 = Square() + x = VGroup(s1, s2, s3, s4).set_x(0).arrange(buff=1.0) + self.add(x) + """ submobjects = list(self.submobjects) if reference is None: From d58dfc47ee7a375999b823d6ded03c244abff958 Mon Sep 17 00:00:00 2001 From: Rotem Date: Sun, 26 Apr 2026 21:46:25 +0300 Subject: [PATCH 8/9] Fix arrange reference implementation --- manim/mobject/mobject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 564011b1dd..b57ad1f239 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -2530,7 +2530,7 @@ def get_family(self, recurse: bool = True) -> list[Mobject]: all_mobjects = [self] + list(it.chain(*sub_families)) return remove_list_redundancies(all_mobjects) - def family_members_with_points(self) -> list[Mobject]: + def family_members_with_points(self) -> list[Mobject]: """Filters the list of family members (generated by :meth:`.get_family`) to include only mobjects with points. Returns From 9b30e0a8eabd7e8e1d96e9eb4f0b3f5ba356471e Mon Sep 17 00:00:00 2001 From: Rotem Date: Sun, 26 Apr 2026 21:57:45 +0300 Subject: [PATCH 9/9] Fix arrange reference zip iteration --- manim/mobject/mobject.py | 1 - 1 file changed, 1 deletion(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index b57ad1f239..5b83908fff 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -2603,7 +2603,6 @@ def construct(self): for m1, m2 in zip( submobjects[reference_index:], submobjects[reference_index + 1 :], - strict=True, ): m2.next_to(m1, direction, buff, **kwargs)