Manim 只播放两个同步动画之一,无论我如何尝试

问题描述 投票:0回答:1

我正在尝试创建一个旋转轮子的动画(一个带有一些正方形的圆圈) 我试图通过让圆圈旋转并同时滑动来做到这一点,但是,它要么只旋转,要么只滑动。或者它先滑动,然后旋转,除了同时滑动和旋转之外,这就是轮子在地面上移动的错觉。

当前版本的代码可以在下面看到,但我尝试了很多方法,包括:

self.play(旋转、滑动、run_time = 4 或者 self.play(AnimationGroup(旋转、滑动、lag_ratio=0), run_time=4)


来自马尼姆进口* 将 numpy 导入为 np

TwoDShapes 类(场景): def 构造(自身): 圆 = 圆(半径=3).shift(左 * 4)

    points = []
    squares = []
    
    num_squares = 20
    angle_increment = 2 * PI / num_squares  
   
    for i in range(num_squares):
        points.append(circle.point_at_angle(angle_increment * i))

        square = Square(side_length=0.25)
        square.move_to(points[i])
        squares.append(square)

    START = (-8, -3, 0)
    END = (8, -3, 0)
    line = Line(START, END)

    self.play(Create(line))
    self.play(Create(circle))

    for k in range(num_squares): 
        self.play(Create(squares[k], run_time=0.2))
        
    for k in range(num_squares): 
        self.play(Rotate(squares[k], angle_increment * k, run_time=0.2))

   
    wheel = Group(*squares, circle)

    rotation_animation = wheel.animate.rotate(4*2*PI) 
    shift_animation = wheel.animate.shift(RIGHT * 8)  

    self.play(shift_animation, run_time=4)
    self.wait(0.1)
    self.play(rotation_animation, run_time=3.9)

    self.wait(1)

    text = Text("Why won't it rotate? :(").move_to(LEFT * 3)
    self.play(Write(text))

    self.wait(1)

animation manim
1个回答
0
投票

好问题。

为什么您当前的方法不起作用

首先要做的事情:调用应用于同一个 VMobject 的多个

同时
动画不起作用。无论您是否使用
AnimationGroup()
,这都适用。所以都不是,

self.play(
    Rotate(example_mobject, PI),
    example_mobject.animate.shift(RIGHT)
)

也不,

self.play(
    AnimationGroup(
        Rotate(example_mobject, PI),
        example_mobject.animate.shift(RIGHT),
        lag_ratio=0.0
    )
)

在任何情况下都可以工作,即,它对于

Rotate()
.animate.shift(RIGHT)
相互作用的情况没有任何特殊意义。通常,上述对
play()
方法的两次调用都会导致仅播放第二个(或者通常是最后一个)动画 actual。应该注意的是,这两个选项都不会导致引发异常

这种方法不起作用的原因是因为manim需要做出某些假设才能播放预定义的动画,例如

.rotate()
.shift()
。例如,如果
VMobject
从帧到帧移动到
RIGHT
,则与(假设为 同时)旋转动画相关的旋转中心也在帧到帧之间不断变化。这并非不可想象,但要点是:默认的
Rotate()
动画不考虑此类更改。当然,您也可以应用类似的论点来解释任何其他一对“同时”动画的“不兼容性”。 OP 对第一个评论者的回复大致证实了

here

,他们在其中提出了与我上面提出的类似的观点。 所以,简而言之:这并不是说你错过了某种技巧;而是你错过了一些技巧。这并不是

manim

设计来渲染动画的。 解决方案:如何正确地为您的特定问题制作动画

首先,我调整并扩展了一些常量,以实现泛化和易于快速更改的目的。

RADIUS = 2.5 LINE_LENGTH = 14 START = 7*LEFT + 3*DOWN END = START + LINE_LENGTH * RIGHT line = Line(START, END) wheel = VGroup().add(Circle(radius=RADIUS).move_to(START + RADIUS * UR)) num_squares = 20 angle_increment = 2*PI / num_squares points = [] for i in range(num_squares): points.append(wheel[0].point_at_angle(i * angle_increment)) square = Square(side_length=RADIUS/12) square.move_to(points[i]).rotate(i * angle_increment) wheel.add(square)

您可以使用 
.generate_target()

然后使用

MoveToTarget()
,
 出人意料地接近一个好的解决方案
wheel.generate_target() wheel.target.shift((LINE_LENGTH - 2*RADIUS) * RIGHT).rotate(-(LINE_LENGTH - 2*RADIUS) / RADIUS) self.play( MoveToTarget(wheel), run_time=5.0 )

优点

:这在代码行方面非常简洁,并且显然它使用了预先存在的动画。

缺点

:然而,由于 .animate.rotate() 方法的怪癖(顺便说一句,即使我也不完全理解!),这种实现会导致

wheel
在滚动时看似“收缩” - 并且因此,它在移动时会悬浮在地面(线)上!

实现轮子沿线滚动的视觉错觉的唯一真正方法(至少据我理解您的查询)是

编写自定义动画 我为您编写了一个名为 RollWheelAlongLine()

的自定义动画,我认为

满足要求:请参阅下面的完整代码块

。我还编写了一个名为 
RotatingWheel() 的简单演示场景,它模仿问题中原始代码片段的(预期)输出。
import numpy as np
from manim import *


class RollWheelAlongLine(Animation):
    def __init__(
            self, mobject: VMobject, how_far_to_roll: float,
            roll_direction: Vector = RIGHT, **kwargs
        ):
        super().__init__(mobject, **kwargs)
        self.how_far_to_roll = how_far_to_roll
        self.roll_direction = roll_direction
        self.start_position = self.mobject.get_center()

        # Assume that the first element of the mobject (VGroup) is a Circle, or at the very least, a Polygon of some sort
        self.radius = self.mobject[0].get_arc_length(255) / (2*PI)
        self.current_rotation = 0.0

        # evaluate the angle of rotation required, CW or CCW depending on roll direction
        if np.array_equal(self.roll_direction, LEFT) or np.array_equal(self.roll_direction, DOWN):
            self.theta = self.how_far_to_roll / self.radius
        elif np.array_equal(self.roll_direction, RIGHT) or np.array_equal(self.roll_direction, UP):
            self.theta = -self.how_far_to_roll / self.radius
        else:
            self.theta = 0.0
    
    def interpolate_mobject(self, alpha: float) -> None:
        # when you overwrite the interpolate_mobject method, you must manually apply any rate functions
        alpha = self.rate_func(alpha)
        angle = alpha * self.theta

        self.mobject.rotate(angle - self.current_rotation)
        self.mobject.move_to(self.start_position + alpha * self.how_far_to_roll * self.roll_direction)
        self.current_rotation = angle


class RotatingWheel(Scene):
    def construct(self):
        RADIUS = 2.5
        LINE_LENGTH = 14
        START = 7*LEFT + 3*DOWN
        END = START + LINE_LENGTH * RIGHT
        line = Line(START, END)
        wheel = VGroup().add(Circle(radius=RADIUS).move_to(START + RADIUS * UR))

        num_squares = 20
        angle_increment = 2*PI / num_squares
        points = []

        for i in range(num_squares):
            points.append(wheel[0].point_at_angle(i * angle_increment))
            square = Square(side_length=RADIUS/12)
            square.move_to(points[i]).rotate(i * angle_increment)
            wheel.add(square)

        self.play(Create(line, run_time=1.5))
        self.play(Create(wheel[0], run_time=1.5))

        self.play(
            AnimationGroup(
                *[Create(sq) for sq in wheel[1:]],
                lag_ratio=0.05
            ),
            run_time=2.0
        )

        self.wait(0.5)

        self.play(
            RollWheelAlongLine(
                wheel,
                LINE_LENGTH - 2*RADIUS  # use default values for the remaining parameters
            ),
            run_time=5.0
        )

        self.wait(2.0)

底线(TL;DR)
复制并粘贴上面的代码块后,运行,

manim -pqh <filename>.py RotatingWheel

将产生您最初一直在寻找的输出。

快乐

动画

© www.soinside.com 2019 - 2024. All rights reserved.