manim icon indicating copy to clipboard operation
manim copied to clipboard

Select the last object of the group as the alignment target when using `Mobject.arrange()`

Open zhanbao2000 opened this issue 10 months ago • 1 comments

Description of proposed feature

My motivation

Hello, I want to be able to select the last object in a group as the target for alignment when using Mobject.arrange(), instead of the default first object.

Like this:

https://github.com/user-attachments/assets/abd9c835-6a4e-4182-be2c-b71372e7c2a9

Why

Mobject.arrange() always uses the first object as the alignment target:

https://github.com/ManimCommunity/manim/blob/2ab99b53d3e8f43b1cd7245208d78c5dd6f9414a/manim/mobject/mobject.py#L2368-L2372

If I want to implement reverse-arrange, I must use the Mobject.next_to() method one by one (in reverse order) for the objects within the group, as implemented in above code.

How can the new feature be used?

By adding a new param last in Mobject.arrange():

group.animate.arrange(center=False, last=True, direction=LEFT)

Additional comments

Here is my change of Mobject.arrange() to implement this feature:

def arrange(
    self,
    direction: Vector3D = RIGHT,
    buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
    center: bool = True,
    last: bool = False,
    **kwargs,
) -> Self:
    if last:
        submobjects = list(reversed(self.submobjects))
    else:
        submobjects = self.submobjects
    
    for m1, m2 in zip(submobjects, submobjects[1:]):
        m2.next_to(m1, direction, buff, **kwargs)
    if center:
        self.center()
    return self

Here is my code to test this feature:

from manim import *

class ReversedArrange(Scene):

    def construct(self):
        square_r = Square(fill_color=RED, fill_opacity=1).move_to(ORIGIN + LEFT * 5)
        square_g = Square(fill_color=GREEN, fill_opacity=1).move_to(ORIGIN)
        square_b = Square(fill_color=BLUE, fill_opacity=1).move_to(ORIGIN + RIGHT * 5)

        group = Group(square_r, square_g, square_b)

        self.play(FadeIn(group))
        self.wait(1)

        self.play(group.animate.arrange(center=False, last=True, direction=LEFT))

If modify Mobject.arrange() as I did, you have to specify the direction param as LEFT when call this method, otherwise the other object in the group will move to the right of the target object (which is assumed to be the rightmost one). Like this:

self.play(group.animate.arrange(center=False, last=True))  # without direction=LEFT

https://github.com/user-attachments/assets/62a1595f-1fff-4405-a313-1fdd9396296b

zhanbao2000 avatar Mar 07 '25 09:03 zhanbao2000

Thanks for the suggestion! I like the idea in principle -- but I think it should go further. Instead of only allowing to keep the first or last mobject fixed, why not allow specifying the index of the mobject that should be kept fixed and rearrange all others around it?

This actually can also be implemented a bit easier: just remember the position of the i-th mobject, arrange with respect to the first and then shift the arranged group such that the i-th object is in its original position.

behackl avatar Aug 03 '25 11:08 behackl