我正在尝试创建一个旋转轮子的动画(一个带有一些正方形的圆圈) 我试图通过让圆圈旋转并同时滑动来做到这一点,但是,它要么只旋转,要么只滑动。或者它先滑动,然后旋转,除了同时滑动和旋转之外,这就是轮子在地面上移动的错觉。
当前版本的代码可以在下面看到,但我尝试了很多方法,包括:
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)
好问题。
首先要做的事情:调用应用于同一个 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
将产生您最初一直在寻找的输出。快乐动画
!