1. 概要
最近用Manim
制作一个数学动画时,用到了一些旋转的动画。
使用Manim
来实现单个物体的旋转,或者绕着固定物体旋转都很简单,
但是实现多个物体之间旋转的联动则稍微麻烦一些,
这里借用太阳,地球,月亮的自转公转来介绍下多个物体旋转联动的实现思路,以供参考。
2. 地球自转
地球每天自己旋转一天就是自转 。
在Manim
中,自转就是绕着自己的中心点旋转。
python
from manim import *
class RevolutionRotation(Scene):
def construct(self):
self.rotation()
self.wait()
# 自转
def rotation(self):
# 这是一个表示地球的图片
earth = ImageMobject("assets\\earth")
self.add(earth)
self.play(
Rotate(
earth,
about_point=ORIGIN,
angle=2 * PI,
axis=IN,
run_time=2,
rate_func=linear,
),
)
3. 地球绕太阳公转
地球绕着太阳转就是公转 。
和自转的实现类似,公转就是围绕指定的中心点旋转。
python
from manim import *
class RevolutionRotation(Scene):
def construct(self):
self.revolution()
self.wait()
# 公转
def revolution(self):
earth = ImageMobject("assets\\earth")
sun = ImageMobject("assets\\sun")
earth.scale(0.3).shift(UP * 2)
self.add(earth, sun)
self.play(
Rotate(
earth,
about_point=ORIGIN,
angle=2 * PI,
axis=IN,
run_time=2,
rate_func=linear,
),
)
4. 太阳,地球和月亮联动
地球绕着太阳转,月亮绕着地球转。
地球绕着太阳旋转和上一步一样,重点在于如何控制月亮的旋转。
我实现的思路是:
- 计算地球相对于太阳旋转的角度 <math xmlns="http://www.w3.org/1998/Math/MathML"> α \alpha </math>α
- 设置此时月亮相对于地球旋转的角度 <math xmlns="http://www.w3.org/1998/Math/MathML"> β \beta </math>β
- 通过
Manim
的update
机制,当地球旋转 <math xmlns="http://www.w3.org/1998/Math/MathML"> α \alpha </math>α角度之后 - 更新月亮相对于地球旋转的角度 <math xmlns="http://www.w3.org/1998/Math/MathML"> β \beta </math>β
- 下面的示例中,设置了 <math xmlns="http://www.w3.org/1998/Math/MathML"> β = 5 α \beta = 5\alpha </math>β=5α(设置太大,月亮会旋转太快看不清)
- 所以地球绕太阳1圈 时,月亮绕了地球5圈。
python
from manim import *
import numpy as np
# 求两个向量的夹角
def arccos_angle(a, b):
cos_angle = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
angle = np.arccos(cos_angle)
return angle
class RevolutionRotation(Scene):
def construct(self):
self.rr()
self.wait()
def rr(self):
earth = ImageMobject("assets\\earth")
sun = ImageMobject("assets\\sun")
moon = ImageMobject("assets\\moon")
earth.scale(0.4).shift(UP * 2)
moon.scale(0.2).shift(UP * 2.8)
self.add(earth, sun, moon)
# 月亮的旋转角度
def moon_updater(z):
# a 是地球初始位置
a = UP * 2
# b 是地球当前的位置
b = earth.get_center()
# ang是地球相对于太阳旋转的角度
ang = arccos_angle(a, b)
# axis控制月亮左旋还是右旋
axis = IN
if b[0] < a[0]:
axis = OUT
# 不断更新月亮的位置
return z.become(
ImageMobject("assets\\moon")
.move_to(b + np.array([0, 0.8, 0]))
.scale(0.2)
.rotate(
angle=ang * 5, # 月亮更新的角度是地球旋转的5倍
about_point=b,
axis=axis,
)
)
moon.add_updater(moon_updater)
# 地球旋转了1/4暂停一下,为了看效果
# 此时月亮旋转了1+1/4圈。
self.play(
Rotate(
earth,
about_point=ORIGIN,
angle=PI / 2,
axis=IN,
run_time=2,
rate_func=linear,
),
)
self.wait()
# 地球旋转剩下来的3/4圈
self.play(
Rotate(
earth,
about_point=ORIGIN,
angle=3 * PI / 2,
axis=IN,
run_time=4,
rate_func=linear,
),
)
5. 总结
Manim
本身提供的Rotate
接口可以实现大部分常见的旋转操作,
本篇主要介绍的是多个物体联动的实现思路。
核心就是每个旋转的物体只要关心它所围绕的物体的位置,根据它所围绕物体的位置来更新自己的位置。
比如,上面的示例中,地球的旋转只要关注太阳的位置,月亮的旋转只要关注地球的位置。
如果有更多的物体联动旋转,也是一样的思路。