from manim import *
import sympy as sym
import numpy as np
if type("xxp's manim") == str:
# 不写在下方 construct 函数中,因为这可能使用到其内部的函数,我想与其分离,因为如果 construct 内部的函数改写了,我此处的类也需要改写
if type('remarks') == str:
pass
# 代码主要由三部分构成:1、自定义类;2、manim 类;3、执行代码部分
# 其中代码执行部分又由三部分构成:1、变量定义区(包括物件和动画);2、变量初始化区;3、动画区
if type('function') == str:
def harmonic_func(u, v, l, m):
# 请不要使用 if l == x & m == y 这种形式,会报错
if l == 0 & m == 0:
r = np.sqrt(1/(4*PI))
elif l == 1:
if m == 0:
r = np.sqrt(3/(4*PI))*np.cos(u)
elif m == 1:
r = np.sqrt(3/(4*PI))*np.sin(u)*np.cos(v)
elif m == -1:
r = np.sqrt(3/(4*PI))*np.sin(u)*np.sin(v)
elif l == 2:
if m == 0:
r = np.sqrt(5/(16*PI))*(3*np.cos(u)**2 - 1)
if m == 1:
r = np.sqrt(15/(4*PI))*np.sin(u)*np.cos(u)*np.cos(v)
if m == -1:
r = np.sqrt(15/(4*PI))*np.sin(u)*np.cos(u)*np.sin(v)
if m == 2:
r = np.sqrt(15/(16*PI))*np.sin(u)**2*np.sin(2*v)
if m == -2:
r = np.sqrt(15/(16*PI))*np.sin(u)**2*np.cos(2*v)
elif l == 3:
if m == 0:
r = 1/4*np.sqrt(7/PI)*(5*np.cos(u)**3 - 3*np.cos(u))
if m == 1:
r = 1/8*np.sqrt(42/PI)*np.sin(u)*(5*np.cos(u)**2 - 1)*np.cos(v)
if m == -1:
r = 1/8*np.sqrt(42/PI)*np.sin(u)*(5*np.cos(u)**2 - 1)*np.sin(v)
if m == 2:
r = 1/4*np.sqrt(105/PI)*np.sin(u)**2*np.cos(u)*np.sin(2*v)
if m == -2:
r = 1/4*np.sqrt(105/PI)*np.sin(u)**2*np.cos(u)*np.cos(2*v)
if m == 3:
r = 1/8*np.sqrt(70/PI)*np.sin(u)**3*np.cos(3*v)
if m == -3:
r = 1/8*np.sqrt(70/PI)*np.sin(u)**3*np.sin(3*v)
else:
raise Exception('l or m is too large')
return r
if type('mobject') == str:
class Atom(VGroup):
def __init__(
self,
quantum_num_li: list, # each item in this list is a list
position_correction: bool = False, # 这个是专门为 sp2 杂化设计的,因为我发现 sp2 杂化时原子核不在中心,这是因为正三角形放置在画布中时不会以自身的中心为中心点,而是会产生一些偏移,于是我们使用 p 轨道定位
show_nucleus: bool = True,
**kwargs
):
super().__init__(**kwargs) # 如果不使用这句话,那么 self.add 会失效
nucleus = Sphere(fill_opacity=0.7, stroke_width=0, radius=0.3).set_color(
"#9AFF9A")
axes = ThreeDAxes(x_range=(-5, 5, 1), y_range=(-5, 5, 1), z_range=(-5, 5, 1),
x_length=5, y_length=5, z_length=5) # 注意默认的 x, y, z 轴长度不相等,图像会拉伸
orbits = VGroup()
for num in quantum_num_li:
orbit = Surface(
# 在不杂化的原子中,num 是含有两个元素的列表,分布代表 l 和 m;在杂化的原子中,num 是含有大于等于六个元素的列表,一部分包括杂化轨道的系数
lambda u, v: axes.c2p(*self.specific_harmonic_func(u, v, num)),
resolution=(20, 20),
v_range=[0, 2*PI],
u_range=[0, 2*PI],
checkerboard_colors=[BLUE],
fill_opacity=0.3,
stroke_width=0,
)
orbits.add(orbit)
nucleus_radius = nucleus.width
# if orbits.depth > orbits.width: # 事实上,三维物体要考虑 width, height, depth
# orbits.set(depth=6*nucleus_radius)
# else:
# orbits.set(width=6*nucleus_radius)
if position_correction:
distance_deviation = nucleus.get_center() - orbits[-1].get_center()
orbits.shift(distance_deviation)
orbits.rotate(angle = PI/2, axis = [1,0,0]) # sp2 杂化默认视角在 p 轨道上方,旋转 PI/2 从侧面观察
nucleus.rotate(angle = PI/2, axis = [1,0,0]) # 不能使用 self.rotate(),因为 self.add() 在下方才定义
self.nucleus = nucleus
self.orbits = orbits
if show_nucleus:
self.add(nucleus, orbits)
else:
self.add(orbits)
def specific_harmonic_func(self, u, v, num) -> list: # 这个函数根据是否杂化而变化
pass
class AutographIntroduction(Group):
def __init__(
self,
image: str,
name: str,
date_of_birth_and_death: str,
**kwargs
):
super().__init__(**kwargs)
image = ImageMobject(image)
if image.width > image.height:
image.set(width = 5.5)
else:
image.set(height = 5.5)
image.shift(3.5*LEFT+0.8*UP)
name = Narrator(name).next_to(image,DOWN)
date_of_birth_and_death = Narrator(date_of_birth_and_death).scale(0.8).next_to(name,DOWN)
self.add(image, name, date_of_birth_and_death)
class ColorBar(VGroup):
def __init__(
self,
start_color: color = BLUE,
end_color: color = RED,
**kwargs
):
super().__init__(**kwargs)
width = 7
height = 0.5
bar = RoundedRectangle(width=width, height=height, fill_opacity=1, corner_radius=0.2).set_color_by_gradient([end_color, start_color])
bar.set(stroke_color=WHITE)
pointer = Triangle(radius=0.2, fill_opacity=1).rotate(PI/3)
self.start_color = start_color
self.end_color = end_color
self.width = width
self.height = height
self.bar = bar
self.pointer = pointer
self.add(bar, pointer)
self.arrange(direction=np.array([0,1,0]),buff=0.1) # 如果使用 [0,1,0] 会报错
def begin_color_update(self):
def update_color(pointer):
position_deviation = (pointer.get_x() - self.bar.get_x() + self.width/2)/self.width # 范围为 0 到 1,但是会有一点点的偏差,可能超过 1
if position_deviation > 1:
position_deviation = 1
pointer.set_color(interpolate_color(self.start_color, self.end_color, position_deviation))
self.pointer.add_updater(update_color)
def end_color_update(self):
self.pointer.clear_updater()
def pointer_to_rightmost_end(self): # 效果并不理想
distance = self.bar.get_x() + self.width/2 - self.pointer.get_x()
return ApplyMethod(self.pointer.shift, distance * RIGHT)
def pointer_to_leftmost_end(self): # 效果并不理想
distance = self.bar.get_x() - self.width/2 - self.pointer.get_x()
return ApplyMethod(self.pointer.shift, distance * RIGHT)
class DotGroup(VGroup):
def __init__(
self,
num: int = 10,
internal_radius: float = 0,
external_radius: float = 2.5,
scale_min: float = 0.6,
scale_max: float = 1.4,
**kwargs
):
super().__init__(**kwargs)
for i in range(num):
radius = internal_radius + (external_radius - internal_radius)*np.random.rand()
scale = scale_min + (scale_max - scale_min)*np.random.rand()
theta = np.random.rand()*2*PI
self.add(Dot().copy().shift(radius*np.cos(theta)*RIGHT+radius*np.sin(theta)*UP).scale(scale))
class HybridAtom(Atom):
def __init__(
self,
hybrid_type: int,
**kwargs
):
self.hybrid_type = hybrid_type
quantum_num_li = self.hybrid_coefficient(hybrid_type)
if hybrid_type == 2:
position_correction = True
else:
position_correction = False
super().__init__(quantum_num_li, position_correction=position_correction, **kwargs)
def hybrid_coefficient(self, hybrid_type):
if hybrid_type == 1:
coe = [[[1/np.sqrt(2), 0, 0], [1/np.sqrt(2), 1, 1]], [[1/np.sqrt(2), 0, 0], [-1/np.sqrt(2), 1, 1]], [
[1, 1, -1]], [[1, 1, 0]]] # this represents f1 = s + px, f2 = x - px, f3 = p, f4 = p
elif hybrid_type == 2:
coe = [[[np.sqrt(1/3), 0, 0], [np.sqrt(2/3), 1, 1]], [[np.sqrt(1/3), 0, 0], [-np.sqrt(1/6), 1, 1], [
np.sqrt(1/2), 1, -1]], [[np.sqrt(1/3), 0, 0], [-np.sqrt(1/6), 1, 1], [-np.sqrt(1/2), 1, -1]], [[1, 1, 0]]]
elif hybrid_type == 3:
coe = [[[1, 0, 0], [1, 1, 1], [1, 1, -1], [1, 1, 0]], [[1, 0, 0], [1, 1, 1], [-1, 1, -1], [-1, 1, 0]],
[[1, 0, 0], [-1, 1, 1], [1, 1, -1], [-1, 1, 0]], [[1, 0, 0], [-1, 1, 1], [-1, 1, -1], [1, 1, 0]]]
return coe
def specific_harmonic_func(self, u, v, num) -> list:
r = 0
for i in num:
r += i[0]*harmonic_func(u, v, i[1], i[2])
r = r ** 2
return [r*np.sin(u)*np.cos(v), r*np.sin(u)*np.sin(v), r*np.cos(u)]
def correct_position(self, direction): # 将杂化的轨道调整到 direction 的方向
# 这个函数的作用效果取决于是否使用了 set_camera_orientation(),例如,我们现在想要让杂化轨道朝右,如果没有使用此方法,direction 为 [1,0,0],如果使用了此方法,direction 为 [0,1,0],这是因为 set_camera_orientation() 会使x, y 轴互换
if self.hybrid_type == 1:
orbits_direction = [1,0,0]
elif self.hybrid_type == 2:
orbits_direction = [1,0,0]
elif self.hybrid_type == 3:
orbits_direction = [1,1,1]
angle = angle_between_vectors(orbits_direction, direction)
axis = get_unit_normal(orbits_direction, direction)
# 旋转之后仍然需要调整角度来和坐标轴平行
# sp3: 15*DEGREES
self.rotate(angle = angle, axis = axis)
return self
class LinesBetweenCenterAndVertexesOfPolyhedron(VGroup):
def __init__(
self,
mobject,
**kwargs
):
# 只适用于多面体
super().__init__(**kwargs)
vertexes = mobject.vertex_coords
for vertex in vertexes:
line = DashedLine(mobject.get_center(), vertex, shade_in_3d = True)
self.add(line)
class MusicRecommender(Group):
def __init__(
self,
recommender: str,
music: str,
**kwargs
):
super().__init__(**kwargs)
image = ImageMobject(f"D:\\manimIMG\\{recommender}.png").set(width=1)
music = Text(music, font='STFangSong').scale(0.7).next_to(img,10*LEFT)
name = ImageMobject(f"D:\\manimIMG\\{recommender}name.png").match_height(music).next_to(img,2*RIGHT)
self.add(image, music, name)
class Narrator(Text):
def __init__(
self,
text: str,
font_type: int = 1,
**kwargs
):
if font_type == 1:
font = "STZhongsong"
elif font_type == 2:
font = "STFangsong"
super().__init__(text, font=font, **kwargs)
self.to_edge(DOWN).scale(0.6)
class TextFlower(VGroup):
def __init__(
self,
text: str,
color_type: int = 1,
use_latex: bool = False,
font: str = 'STZhongsong',
**kwargs
):
super().__init__(**kwargs)
if color_type == 1:
color = ['#f3e9e0', '#fea8a9']
elif color_type == 2:
color = ['#f3e9e0', '#b1d85c']
elif color_type == 3:
color = ['#f3e9e0', '#6A5ACD']
if use_latex:
text = MathTex(text, stroke_width=2).set_color(color = color)
else:
text = Text(text, font = font, stroke_width=2).set_color(color = color)
flower = SVGMobject(r"D:\manimSVG\figures\icon.svg")[8].rotate(PI/2).set_color(color = color[::-1]).scale(1.5)
text.next_to(flower, RIGHT)
self.add(flower, text)
self.set(height = 0.6)
self.move_to(ORIGIN)
class UnhybridAtom(Atom):
def __init__(
self,
*args, # this list contains l, m
**kwargs
):
super().__init__([[*args]], **kwargs)
def specific_harmonic_func(self, u, v, num) -> list:
r = harmonic_func(u, v, *num)
r = r ** 2
return [r*np.sin(u)*np.cos(v), r*np.sin(u)*np.sin(v), r*np.cos(u)]
class xa(Atom): # Atom
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class xai(AutographIntroduction): # AutographIntroduction
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class xcb(ColorBar): # ColorBar
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class xdg(DotGroup): # DotGroup
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class xha(HybridAtom): # HybridAtom
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class xlb(LinesBetweenCenterAndVertexesOfPolyhedron): # LinesBetweenCenterAndVertexesOfPolyhedron
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class xmr(MusicRecommender): # MusicRecommender
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class xn(Narrator): # Narrator
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class xtf(TextFlower): # TextFlower
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class xua(UnhybridAtom): # UnhybridAtom
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if type('animation') == str:
class CorrespondingPartsTransform(AnimationGroup):
def __init__(
self,
names,
*args,
**kwargs
):
# names is a list of mobjects
# args represent lists, such as [0,1,[0,0,1,1,2,2]], the 1st and 2nd numbers represent the index of mobject in the names list
def pretrans0(name, i): # get the transforming part
if type(i) == int:
back = name[i]
elif type(i) == str:
m = i.find('.')
n1 = int(i[0:m])
n2 = int(i[m+1:])
back = name[n1:n2]
return back
def pretrans(name, i):
if type(i) == int:
back = name[i]
elif type(i) == str:
m = i.find('.')
n1 = int(i[0:m])
n2 = int(i[m+1:])
back = name[n1:n2]
elif type(i) == list:
back = VGroup()
for j in i:
item = pretrans0(name, j)
back.add(item)
return back
ag = []
itemli = []
for l in names:
itemli.append(list(range(len(l))))
# delete the tranformed part
def countli0(name, i):
if type(i) == int:
name.remove(i)
elif type(i) == str:
m = i.find('.')
n1 = int(i[0:m])
n2 = int(i[m+1:])-1
p1 = name.index(n1)
p2 = name.index(n2)
del name[p1:p2+1]
def countli(name, i):
if type(i) == int:
name.remove(i)
elif type(i) == str:
m = i.find('.')
n1 = int(i[0:m])
n2 = int(i[m+1:])-1
p1 = name.index(n1)
p2 = name.index(n2)
del name[p1:p2+1]
elif type(i) == list:
for j in i:
item = countli0(name, j)
set1 = set()
set2 = set()
# two groups of transforming
for m in range(len(args)):
n = 0
for i in args[m][2]:
n += 1
if n % 2 != 0:
u = pretrans(names[args[m][0]], i)
countli(itemli[args[m][0]], i)
set1.add(args[m][0])
else:
v = pretrans(names[args[m][1]], i)
item = Transform(u,v) # 如果使用改进的 at(),会显示未定义
ag.append(item)
countli(itemli[args[m][1]], i)
set2.add(args[m][1])
for i in set1:
for ii in itemli[i]:
names[i][ii].save_state()
ag.append(ShrinkToCenter(names[i][ii]))
for j in set2:
for jj in itemli[j]:
ag.append(GrowFromCenter(names[j][jj]))
super().__init__(*ag, **kwargs)
class CorrespondingPartsTransformOrigin(AnimationGroup):
def __init__(
self,
original_mobject: mobject,
target_mobject: mobject,
corresponding_parts_list: list,
**kwargs
):
# a and b represent mobjects, c represents the corresponding transform parts
def pretrans0(name, i):
if type(i) == int:
back = name[i]
elif type(i) == str:
m = i.find('.')
n1 = int(i[0:m])
n2 = int(i[m+1:])
back = name[n1:n2]
return back
def pretrans(name, i):
if type(i) == int:
back = name[i]
elif type(i) == str:
m = i.find('.')
n1 = int(i[0:m])
n2 = int(i[m+1:])
back = name[n1:n2]
elif type(i) == list:
back = VGroup()
for j in i:
item = pretrans0(name, j)
back.add(item)
return back
n = 0
ag = []
for i in corresponding_parts_list:
n += 1
if n % 2 != 0:
u = pretrans(original_mobject, i)
else:
v = pretrans(target_mobject, i)
item = Transform(u,v)
ag.append(item)
super().__init__(*ag, **kwargs)
class DotFadeIn(AnimationGroup):
def __init__(
self,
*mobjects,
**kwargs
):
ag = []
for i in mobjects:
dots = DotGroup(len(i)).set_color(WHITE).set_opacity(0)
ag.append(Transform(dots,i))
super().__init__(*ag, **kwargs)
class DotFadeOut(AnimationGroup):
def __init__(
self,
*mobjects,
**kwargs
):
ag = []
for i in mobjects:
dots = DotGroup(len(i)).set_color(WHITE).set_opacity(0)
ag.append(Transform(i,dots))
super().__init__(*ag, **kwargs)
class FadeInDownward(AnimationGroup):
def __init__(
self,
*mobjects,
**kwargs
):
ag = []
for mobject in mobjects:
ag.append(FadeIn(mobject, shift=DOWN))
super().__init__(*ag, **kwargs)
class FadeOutDownward(AnimationGroup):
def __init__(
self,
*mobjects,
**kwargs
):
ag = []
for mobject in mobjects:
ag.append(FadeOut(mobject, shift=DOWN))
super().__init__(*ag, **kwargs)
class FisheyeGrow(AnimationGroup):
def __init__(
self,
*mobjects,
**kwargs
):
# 注意如果对 Text 使用此动画,要使用索引才能生效:Text()[0]
ag = []
for a in mobjects:
a0 = a.copy()
myl = 5 # myl represents the fish eye's border, default: 5
li = a.points
def getlen(k):
return np.sqrt(k[0]**2 + k[1]**2 + k[2]**2)
for i in li:
times = 0.04*(myl - getlen(i))**3
i *= times # the further, times is smaller
ag.append(Transform(a0,a))
super().__init__(*ag, **kwargs)
class FisheyeShrink(AnimationGroup):
def __init__(
self,
*mobjects,
**kwargs
):
# 注意如果对 Text 使用此动画,要使用索引才能生效:Text()[0]
ag = []
for a in mobjects:
a0 = a.copy()
li = a.points
def getlen(k):
return np.sqrt(k[0]**2 + k[1]**2 + k[2]**2)
for i in li:
times = 0.02*getlen(i)**3 + 0.5 # 0.5 represent the minimum, because when the point is near center, genlen() return a number near 0
i *= times # the further, times is larger
ag.append(Transform(a0,a))
super().__init__(*ag, **kwargs)
class RotateAboutOwnAxis(Rotate):
def __init__(
self,
mobject,
*args,
**kwargs
):
super().__init__(mobject, axis = [0,np.sqrt(3),1], *args, **kwargs)
class SubTextFlowersCatalogue(AnimationGroup):
def __init__(
self,
**kwargs
):
super().__init__(**kwargs)
def begin_animation(self, text):
text_flower = TextFlower(text, color_type=2, font="Cambria Math").to_corner(UL).shift(0.8*DOWN)
animation = DrawBorderThenFill(text_flower)
self.text_flower = text_flower
super().__init__(animation)
return self
def alter(self, text):
text_flower_after = TextFlower(text, color_type=2, font="Cambria Math").to_corner(UL).shift(0.8*DOWN)
text_flower = self.text_flower
animation1 = yfod(text_flower)
animation2 = yfid(text_flower_after)
self.text_flower = text_flower_after
super().__init__(animation1, animation2)
return self
def end_animation(self):
text_flower = self.text_flower
animation = FadeOut(text_flower)
super().__init__(animation)
return self
class TextFlowersCatalogue(Succession):
def __init__(
self,
texts: list, # a list of texts
grid: list,
buff: float = 2, # the spacing of each item
**kwargs
):
text_flowers = VGroup(*map(TextFlower, texts)).arrange_in_grid(*grid,buff=buff)
self.text_flowers = text_flowers
super().__init__(**kwargs)
def begin_animation(self): # 注意不能使用 begin 命名
text_flowers = self.text_flowers
flower_num = len(text_flowers)
flower = TextFlower("").shift(0.7*LEFT)
flowers = VGroup(*[flower.copy() for i in range(flower_num)]) # 注意不能直接写成 VGroup(... for i in ...),必须使用 *
animation1 = DrawBorderThenFill(flowers)
animation2 = AnimationGroup(*[Rotate(flowers[i+1], angle=2*PI/flower_num*(i+1), about_point=ORIGIN) for i in range(flower_num-1)], lag_ratio=0.3)
animation3 = Transform(flowers, text_flowers) # 我用 ReplacementTransform 不能实现预期的效果
self.text_flowers = flowers # 注意,这里改变成了 transform 的对象 flowers
super().__init__(animation1, animation2, animation3)
return self
def forward(self, index):
text_flowers = self.text_flowers
text_flowers_after = text_flowers[index-1].copy().to_corner(UL)
shift_distance = text_flowers_after.get_center() - text_flowers[index-1].get_center()
animation1 = ApplyMethod(text_flowers[index-1].shift, shift_distance)
animation2 = FadeOut(text_flowers[0:index-1])
animation3 = FadeOut(text_flowers[index:])
self.shift_distance = shift_distance
self.index = index
return AnimationGroup(animation1, animation2, animation3)
def remaining_text_flower(self, index):
text_flowers = self.text_flowers
text_flowers_after = text_flowers[index-1].copy().to_corner(UL)
shift_distance = text_flowers_after.get_center() - text_flowers[index-1].get_center()
self.shift_distance = shift_distance
self.index = index
return text_flowers[index-1].to_corner(UL)
def backward(self):
text_flowers = self.text_flowers
index = self.index
shift_distance = self.shift_distance
animation1 = ApplyMethod(text_flowers[index-1].shift, -shift_distance) # 不再使用 Restore()
animation2 = FadeIn(text_flowers[0:index-1])
animation3 = FadeIn(text_flowers[index:])
return AnimationGroup(animation1, animation2, animation3)
def end_animation(self):
text_flowers = self.text_flowers
flower_num = len(text_flowers)
flower = TextFlower("").shift(0.7*LEFT)
flowers = VGroup(*[flower.copy().rotate(angle=2*PI/flower_num*i, about_point=ORIGIN) for i in range(flower_num)])
animation1 = Transform(text_flowers, flowers)
animation2 = AnimationGroup(*[DrawBorderThenFill(text_flowers[i], reverse_rate_function = True) for i in range(flower_num)], lag_ratio=0.3)
super().__init__(animation1, animation2)
return self
class Vibration(AnimationGroup):
def __init__(
self,
*mobjects,
t: int = 2,
**kwargs
):
ag = []
for mobject in mobjects:
li = []
center = mobject.get_center()
vibration_times = int(t*5) # 否则为 float 类型
for i in range(vibration_times):
x = np.random.rand()*0.3
y = np.random.rand()*2*PI
li.append(ApplyMethod(mobject.move_to, center+x*np.cos(y)*RIGHT+x*np.sin(y)*UP, run_time=0.2))
ag.append(Succession(*li))
super().__init__(*ag, **kwargs)
class ycpt(CorrespondingPartsTransform): # CorrespondingPartsTransform
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class ycpto(CorrespondingPartsTransformOrigin): # CorrespondingPartsTransformOrigin
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class ydfi(DotFadeIn): # DotFadeIn
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class ydfo(DotFadeOut): # DotFadeOut
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class yfid(FadeInDownward): # FadeInDownward
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class yfod(FadeOutDownward): # FadeOutDownward
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class yfg(FisheyeGrow): # FisheyeGrow
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class yfs(FisheyeShrink): # FisheyeShrink
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class yrab(RotateAboutOwnAxis): # RotateAboutOwnAxis
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class ystf(SubTextFlowersCatalogue): # SubTextFlowersCatalogue
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class ytfc(TextFlowersCatalogue): # TextFlowersCatalogue
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class yv(Vibration): # Vibration
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if type('protagonist') == str:
class Orbital(VGroup):
def __init__(
self,
l: int,
m: int,
**kwargs
):
super().__init__(**kwargs)
self.l = l
self.m = m
phi = self.wave_func_phi(m)
theta = self.wave_func_theta(l, m)
p = sym.symbols('p')
t = sym.symbols('t')
def harmonic_func(u, v):
r = phi.subs(p, v)*theta.subs(t, u) # u v 的顺序不要搞错
r = r ** 2
return [r*np.sin(u)*np.cos(v), r*np.sin(u)*np.sin(v), r*np.cos(u)]
axes = ThreeDAxes(x_range=(-5, 5, 1), y_range=(-5, 5, 1), z_range=(-5, 5, 1),
x_length=5, y_length=5, z_length=5)
nucleus = Sphere(fill_opacity=0.7, stroke_width=0, radius=0.5).set_color("#9AFF9A")
orbit = Surface(
lambda u, v: axes.c2p(*harmonic_func(u, v)),
resolution=(20, 20),
v_range=[0, 2*PI],
u_range=[0, PI],
checkerboard_colors=[BLUE],
fill_opacity=0.8,
stroke_width=0,
)
nucleus_radius = nucleus.width
if orbit.width >= orbit.height:
if orbit.width >= orbit.depth:
orbit.set(width=6*nucleus_radius)
else:
orbit.set(depth=6*nucleus_radius)
else:
if orbit.height >= orbit.depth:
orbit.set(height=6*nucleus_radius)
else:
orbit.set(depth=6*nucleus_radius)
self.orbit = orbit
self.l_and_m = f'l = {str(l)}, m = {str(m)}'
self.add(orbit)
def wave_func_phi(self, m):
p = sym.symbols('p')
if m >= 0:
result = sym.cos(m*p) # 省略了系数
elif m < 0:
result = sym.sin(np.abs(m)*p) # 省略了系数
return result
def wave_func_theta(self, l, m):
m = np.abs(m)
x = sym.symbols('x')
t = sym.symbols('t')
def associated_legendre_polynomial(l, m):
def legendre_polynomial(n):
if n == 0:
return 1
elif n == 1:
return x
elif n >= 2:
result = (2*n-1)/n*x*legendre_polynomial(n-1) - (n-1)/n*legendre_polynomial(n-2)
return sym.simplify(result)
result = (1-x**2)**(m/2)*sym.diff(legendre_polynomial(l), x, m)
return sym.simplify(result.subs(x, sym.cos(t)))
result = associated_legendre_polynomial(l, m) # 省略了系数
return result
def wave_func_radius(self, n, l):
x = sym.symbols('x')
def associated_Laguerre_polynomial(n, l):
result = x**(-l)*sym.exp(x)*sym.diff(sym.exp(-x)*x**(n+l), x, n) # 这里的 x 实际上也要乘以一个系数
return sym.simplify(result)
result = sym.exp(-x/2)*x**l*associated_Laguerre_polynomial(n, l) # 省略了系数
return result
def harmonic_func(self, u, v, l, m):
phi = self.wave_func_phi(m)
theta = self.wave_func_theta(l, m)
p = sym.symbols('p')
t = sym.symbols('t')
func = phi.subs(p, v)*theta.subs(t, u) # u v 的顺序不要搞错
return func
def axes(self):
axes = SVGMobject(r"D:\manimSVG\axes.svg").set_height(5.5)
axes_distance_deviation = ORIGIN - axes[-1].get_center()
axes.shift(axes_distance_deviation)
return axes
class HybridOrbital(VGroup):
def __init__(
self,
hybrid_num: int,
**kwargs
):
super().__init__(**kwargs)
self.num = hybrid_num
property = self.hybrid_orbit_property(hybrid_num)
axes = ThreeDAxes(x_range=(-5, 5, 1), y_range=(-5, 5, 1), z_range=(-5, 5, 1),
x_length=5, y_length=5, z_length=5)
orbits = VGroup()
labels = VGroup()
for coe in property[4:]:
def hybrid_orbit(u, v):
r = 0
n = 0
for origin_orbit in property[3]:
r += coe[n]*self.harmonic_func(u, v, *origin_orbit)
n += 1
r = r ** 2
return [r*np.sin(u)*np.cos(v), r*np.sin(u)*np.sin(v), r*np.cos(u)]
orbit = Surface(
lambda u, v: axes.c2p(*hybrid_orbit(u, v)),
resolution=(40, 40),
v_range=[0, 2*PI],
u_range=[0, PI],
checkerboard_colors=[BLUE],
fill_opacity=0.8,
stroke_width=0,
)
orbits.add(orbit)
self.scale_to_fit(orbits)
geometry = self.geometry()
self.add(orbits, geometry)
self.arrange(buff=2)
label1 = TextFlower(property[0], color_type=1, use_latex=True).to_corner(UL)
label2 = TextFlower(property[1], color_type=2, use_latex=True).next_to(label1, RIGHT)
label3 = TextFlower(property[2], color_type=3).next_to(label2, RIGHT)
labels.add(label1, label2, label3)
self.add(labels)
def scale_to_fit(self, mob):
if mob.width >= mob.height:
if mob.width >= mob.depth:
mob.set(width=4.5)
else:
mob.set(depth=4.5)
else:
if mob.height >= mob.depth:
mob.set(height=4.5)
else:
mob.set(depth=4.5)
def hybrid_orbit_property(self, num):
if num == 2:
property = ['AB_{2}','sp','直线形',[[0,0],[1,1]],[np.sqrt(1/2), np.sqrt(1/2)],[np.sqrt(1/2), -np.sqrt(1/2)]]
elif num == 3:
property = ['AB_{3}','sp^{2}','平面正三角形',[[0,0],[1,1],[1,-1]],[np.sqrt(1/3),np.sqrt(2/3),0],[np.sqrt(1/3),-np.sqrt(1/6),np.sqrt(1/2)],[np.sqrt(1/3),-np.sqrt(1/6),-np.sqrt(1/2)]]
elif num == 4:
property = ['AB_{4}','sp^{3}','正四面体',[[0,0],[1,0],[1,1],[1,-1]],[1/2,1/2,1/2,1/2],[1/2,-1/2,-1/2,1/2],[1/2,1/2,-1/2,-1/2],[1/2,-1/2,1/2,-1/2]]
elif num == 5:
property = ['AB_{4}','dsp^{2}','平面正方形',[[0,0],[2,2],[1,1],[1,-1]],[1/2,1/2,np.sqrt(1/2),0],[1/2,-1/2,0,np.sqrt(1/2)],[1/2,1/2,-np.sqrt(1/2),0],[1/2,-1/2,0,-np.sqrt(1/2)]]
elif num == 6:
property = ['AB_{5}','dsp^{3}/d^{3}sp','三角双锥',[[0,0],[2,0],[1,1],[1,-1],[1,0]],[np.sqrt(1/2),0,0,0,np.sqrt(1/2)],[np.sqrt(1/2),0,0,0,-np.sqrt(1/2)],[0,np.sqrt(1/3),np.sqrt(2/3),0,0],[0,np.sqrt(1/3),-np.sqrt(1/6),np.sqrt(1/2),0],[0,np.sqrt(1/3),-np.sqrt(1/6),-np.sqrt(1/2),0]]
elif num == 7:
property = ['AB_{6}','d^{4}sp','正三棱柱',[[0,0],[1,1],[1,-1],[1,0],[2,1],[2,-1]],[np.sqrt(1/6),np.sqrt(1/3),0,-np.sqrt(1/6),-np.sqrt(1/3),0],[np.sqrt(1/6),np.sqrt(1/3),0,np.sqrt(1/6),np.sqrt(1/3),0],[np.sqrt(1/6),-np.sqrt(1/12),1/2,-np.sqrt(1/6),np.sqrt(1/12),-1/2],[np.sqrt(1/6),-np.sqrt(1/12),1/2,np.sqrt(1/6),-np.sqrt(1/12),1/2],[np.sqrt(1/6),-np.sqrt(1/12),-1/2,np.sqrt(1/6),-np.sqrt(1/12),-1/2],[np.sqrt(1/6),-np.sqrt(1/12),-1/2,-np.sqrt(1/6),np.sqrt(1/12),1/2]]
elif num == 8:
property = ['AB_{6}','sp^{3}d^{2}/d^{2}sp^{3}','正八面体',[[0,0],[2,0],[2,2],[1,1],[1,-1],[1,0]],[np.sqrt(1/6),-np.sqrt(1/12),1/2,np.sqrt(1/2),0,0],[np.sqrt(1/6),-np.sqrt(1/12),1/2,-np.sqrt(1/2),0,0],[np.sqrt(1/6),-np.sqrt(1/12),-1/2,0,np.sqrt(1/2),0],[np.sqrt(1/6),-np.sqrt(1/12),-1/2,0,-np.sqrt(1/2),0],[np.sqrt(1/6),np.sqrt(1/3),0,0,0,np.sqrt(1/2)],[np.sqrt(1/6),np.sqrt(1/3),0,0,0,-np.sqrt(1/2)]]
elif num == 9:
sin1=np.sin(0.4*PI)
sin2=np.sin(0.8*PI)
cos1=np.cos(0.4*PI)
cos2=np.cos(0.8*PI)
s1 = sin1/np.sqrt(2*(sin1**2+sin2**2))
s2 = sin2/np.sqrt(2*(sin1**2+sin2**2))
c1 = cos1/np.sqrt(1+2*(cos1**2+cos2**2))
c2 = cos2/np.sqrt(1+2*(cos1**2+cos2**2))
constant = 1/np.sqrt(1+2*(cos1**2+cos2**2))
property = ['AB_{7}','d^{3}sp^{3}','五角双锥',[[2,0],[0,0],[1,1],[1,-1],[2,2],[2,-2],[1,0]],[np.sqrt(1/7),np.sqrt(1/2),0,0,0,0,np.sqrt(1/2)],[np.sqrt(1/7),np.sqrt(1/2),0,0,0,0,-np.sqrt(1/2)],[np.sqrt(1/7),0,0,constant,constant,0,0],[np.sqrt(1/7),0,s2,c2,c1,s1,0],[np.sqrt(1/7),0,-s1,c1,c2,s2,0],[np.sqrt(1/7),0,s1,c1,c2,-s2,0],[np.sqrt(1/7),0,-s2,c2,c1,-s1,0]]
elif num == 10:
c = np.sqrt(1/8)
property = ['AB_{8}','d^{3}fsp^{3}','立方体',[[0,0],[2,1],[2,-1],[2,-2],[3,2],[1,1],[1,-1],[1,0]],[c,c,c,c,c,c,c,c],[c,-c,c,-c,-c,-c,c,c],[c,-c,-c,c,c,-c,-c,c],[c,c,-c,-c,-c,c,-c,c],[c,-c,-c,c,-c,c,c,-c],[c,c,-c,-c,c,-c,c,-c],[c,c,c,c,-c,-c,-c,-c],[c,-c,c,-c,c,c,-c,-c]]
elif num == 11:
c = np.sqrt(1/8)
property = ['AB_{8}','d^{4}sp^{3}','正四角反棱柱',[[0,0],[1,0],[1,1],[1,-1],[2,2],[2,-2],[2,1],[2,-1]],[c,-c,c,-c,0,-1/2,-c,c],[c,-c,c,c,0,1/2,-c,-c],[c,-c,-c,c,0,-1/2,c,-c],[c,-c,-c,-c,0,1/2,c,c],[c,c,1/2,0,1/2,0,1/2,0],[c,c,0,1/2,-1/2,0,0,1/2],[c,c,-1/2,0,1/2,0,-1/2,0],[c,c,0,-1/2,-1/2,0,0,-1/2]]
elif num == 12:
property = ['AB_{8}','d^{4}sp^{3}','十二面体',[[0,0],[2,0],[2,2],[1,0],[1,1],[1,-1],[2,1],[2,-1]],[1/2,0,1/2,0,np.sqrt(1/2),0,0,0],[1/2,0,-1/2,0,0,np.sqrt(1/2),0,0],[1/2,0,1/2,0,-np.sqrt(1/2),0,0,0],[1/2,0,-1/2,0,0,-np.sqrt(1/2),0,0],[0,1/2,0,1/2,0,0,np.sqrt(1/2),0],[0,1/2,0,1/2,0,0,-np.sqrt(1/2),0],[0,1/2,0,-1/2,0,0,0,np.sqrt(1/2)],[0,1/2,0,-1/2,0,0,0,-np.sqrt(1/2)]]
return property
def harmonic_func(self, u, v, l, m):
if l == 0 & m == 0:
r = np.sqrt(1/(4*PI))
elif l == 1:
if m == 0:
r = np.sqrt(3/(4*PI))*np.cos(u)
elif m == 1:
r = np.sqrt(3/(4*PI))*np.sin(u)*np.cos(v)
elif m == -1:
r = np.sqrt(3/(4*PI))*np.sin(u)*np.sin(v)
elif l == 2:
if m == 0:
r = np.sqrt(5/(16*PI))*(3*np.cos(u)**2 - 1)
if m == 1:
r = np.sqrt(15/(16*PI))*np.sin(2*u)*np.cos(v)
if m == -1:
r = np.sqrt(15/(16*PI))*np.sin(2*u)*np.sin(v)
if m == 2:
r = np.sqrt(15/(16*PI))*np.sin(u)**2*np.cos(2*v)
if m == -2:
r = np.sqrt(15/(16*PI))*np.sin(u)**2*np.sin(2*v)
elif l == 3:
if m == 0:
r = 1/4*np.sqrt(7/PI)*(5*np.cos(u)**3 - 3*np.cos(u))
if m == 1:
r = 1/8*np.sqrt(42/PI)*np.sin(u)*(5*np.cos(u)**2 - 1)*np.cos(v)
if m == -1:
r = 1/8*np.sqrt(42/PI)*np.sin(u)*(5*np.cos(u)**2 - 1)*np.sin(v)
if m == 2:
r = 1/4*np.sqrt(105/PI)*np.sin(u)**2*np.cos(u)*np.sin(2*v)
if m == -2:
r = 1/4*np.sqrt(105/PI)*np.sin(u)**2*np.cos(u)*np.cos(2*v)
if m == 3:
r = 1/8*np.sqrt(70/PI)*np.sin(u)**3*np.cos(3*v)
if m == -3:
r = 1/8*np.sqrt(70/PI)*np.sin(u)**3*np.sin(3*v)
else:
raise Exception('l or m is too large')
return r
def geometry(self):
num = self.num
if num == 2:
vertex_coords = [[1,0,0],[-1,0,0]]
faces_list = [[0,1]]
elif num == 3:
vertex_coords = [[2,0,0],[-1,np.sqrt(3),0],[-1,-np.sqrt(3),0]]
faces_list = [[0,1,2]]
elif num == 4:
vertex_coords = [[1,1,1],[-1,-1,1],[1,-1,-1],[-1,1,-1]]
faces_list = [[0,1,2],[0,1,3],[0,2,3],[1,2,3]]
elif num == 5:
vertex_coords = [[1,0,0],[0,1,0],[-1,0,0],[0,-1,0]]
faces_list = [[0,1,2,3]]
elif num == 6:
vertex_coords = [[-2,0,0],[1,np.sqrt(3),0],[1,-np.sqrt(3),0],[0,0,2],[0,0,-2]]
faces_list = [[0,1,3],[1,2,3],[0,2,3],[0,1,4],[1,2,4],[0,2,4]]
elif num == 7:
vertex_coords = [[-1,np.sqrt(3),np.sqrt(3)],[2,0,np.sqrt(3)],[-1,-np.sqrt(3),np.sqrt(3)],[-1,np.sqrt(3),-np.sqrt(3)],[2,0,-np.sqrt(3)],[-1,-np.sqrt(3),-np.sqrt(3)]]
faces_list = [[0,1,2],[0,2,5,3],[2,1,4,5],[0,1,4,3],[3,4,5]]
elif num == 8:
vertex_coords = [[0,-1,0],[-1,0,0],[0,1,0],[1,0,0],[0,0,1],[0,0,-1]]
faces_list = [[0,1,4],[1,2,4],[2,3,4],[0,3,4],[0,1,5],[1,2,5],[2,3,5],[0,3,5]]
elif num == 9:
vertex_coords = [[0,-1,0],[np.cos(0.1*PI),-np.sin(0.1*PI),0],[np.cos(0.3*PI),np.sin(0.3*PI),0],[-np.cos(0.3*PI),np.sin(0.3*PI),0],[-np.cos(0.1*PI),-np.sin(0.1*PI),0],[0,0,1],[0,0,-1]]
faces_list = [[5,4,0],[5,0,1],[5,1,2],[5,2,3],[5,3,4],[5,4,0],[6,4,0],[6,0,1],[6,1,2],[6,2,3],[6,3,4],[6,4,0]]
elif num == 10:
vertex_coords = [[1,1,1],[1,-1,1],[-1,-1,1],[-1,1,1],[1,1,-1],[1,-1,-1],[-1,-1,-1],[-1,1,-1]]
faces_list = [[0,1,2,3],[3,0,4,7],[0,1,5,4],[2,1,5,6],[2,3,7,6],[4,5,6,7]]
elif num == 11:
vertex_coords = [[0,np.sqrt(2),1],[np.sqrt(2),0,1],[0,-np.sqrt(2),1],[-np.sqrt(2),0,1],[1,1,-1],[1,-1,-1],[-1,-1,-1],[-1,1,-1]]
faces_list = [[0,1,2,3],[3,0,7],[0,1,4],[1,4,5],[1,2,5],[2,5,6],[2,3,6],[3,6,7],[4,5,6,7]]
elif num == 12:
vertex_coords = [[0,2,0],[2,0,0],[0,-2,0],[-2,0,0],[-1,0,np.sqrt(3)],[1,0,np.sqrt(3)],[0,1,-np.sqrt(3)],[0,-1,-np.sqrt(3)]]
faces_list = [[0,3,4],[0,1,5],[0,4,5],[2,3,4],[1,2,5],[2,4,5],[0,3,6],[0,1,6],[2,3,7],[1,2,7],[3,6,7],[1,6,7]]
geo = Polyhedron(vertex_coords, faces_list)
self.scale_to_fit(geo)
for dot in geo.graph:
dot.set(width=0.3)
return geo
class k1(Scene):
def construct(self):
if type('abbreviation') == str:
# 必须写在 construct 函数里面
if type('constant') == str:
r = RIGHT;l = LEFT;d = DOWN;u = UP;o = ORIGIN
# color
cr = RED;cb = BLUE;cg = GREEN;cgo = GOLD;cp = PINK;ct = TEAL;cw = WHITE
if type('manim CE') == str:
def sa(*args, **kwargs): # self.add()
return self.add(*args, **kwargs)
def saw(*args, **kwargs): # self.add() and self.wait()
self.add(*args, **kwargs);self.wait()
def sc(*args, **kwargs): # self.clear()
return self.clear(*args, **kwargs)
def sp(*args, **kwargs): # self.play()
return self.play(*args, **kwargs)
def sr(*args, **kwargs): # self.remove()
return self.remove(*args, **kwargs)
def sw(*args, **kwargs): # self.wait()
return self.wait(*args, **kwargs)
def ac(a, b): # Animation coloring
return ApplyMethod(a.set_color, b)
def acg(*args, **kwargs): # Animation coloring group
li = []
for i in range(len(args)):
if i % 2 == 0:
li.append(ApplyMethod(args[i].set_color, args[i+1], **kwargs))
return AnimationGroup(*li)
def aci(a, *args, **kwargs): # Animation Circumscribe()
return Circumscribe(a, *args, **kwargs)
def acct(a, b, *args, **kwargs): # Animation CounterclockwiseTransform()
return CounterclockwiseTransform(a, b, *args, **kwargs)
def act(a, b, *args, **kwargs): # Animation ClockwiseTransform()
return ClockwiseTransform(a, b, *args, **kwargs)
def adb(*args, **kwargs): # Animation DrawBorderThenFill()
li = []
for i in args:
li.append(DrawBorderThenFill(i, **kwargs))
return AnimationGroup(*li)
def adbo(a, *args, **kwargs): # Animation DrawBorderThenFill() origin
return DrawBorderThenFill(a, *args, **kwargs)
def adbr(a, *args, **kwargs): # Animation DrawBorderThenFill() reverse
return DrawBorderThenFill(a, *args, **kwargs, reverse_rate_function = True)
def afi(a, *args, **kwargs): # Animation FadeIn()
return FadeIn(a, *args, **kwargs)
def afii(a, *args, **kwargs): # Animation FadeIn()
return FadeIn(a, *args, shift=DOWN, **kwargs)
def afo(a, *args, **kwargs): # Animation FadeOut()
return FadeOut(a, *args, **kwargs)
def afoo(a, *args, **kwargs): # Animation FadeOut()
return FadeOut(a, *args, shift=DOWN, **kwargs)
def agf(*args, **kwargs): # Animation GrowFromCenter()
li = []
for i in args:
li.append(GrowFromCenter(i, **kwargs))
return AnimationGroup(*li)
def agfe(a, b, *args, **kwargs): # Animation GrowFromEdge()
return GrowFromEdge(a, b, *args, **kwargs)
def agfo(a, *args, **kwargs): # Animation GrowFromCenter() origin
return GrowFromCenter(a, *args, **kwargs)
def agfp(a, b, *args, **kwargs): # Animation GrowFromPoint()
return GrowFromPoint(a, b, *args, **kwargs)
def ai(a, *args, **kwargs): # Animation Indicate
return Indicate(a, *args, **kwargs)
def als(a, *args, **kwargs): # Animation LaggedStart()
return LaggedStart(a, *args, **kwargs)
def ama(a, b, *args, **kwargs): # Animation MoveAlongPath()
return MoveAlongPath(a, b, *args, **kwargs)
def amt(*args, **kwargs): # Animation move to
li = []
i = 1
for a in args:
if i % 2 != 0:
li.append(ApplyMethod(a.move_to, args[i], **kwargs))
i += 1
return AnimationGroup(*li)
def are(a, *args, **kwargs): # Animation Restore()
return Restore(a, *args, **kwargs)
def aro(a, *args, **kwargs): # Animation Rotate()
return Rotate(a, *args, **kwargs)
def art(a, b, *args, **kwargs): # Animation ReplacementTransform()
return ReplacementTransform(a, b, *args, **kwargs)
def asc(a, b): # Animation scale
return ApplyMethod(a.scale, b)
def ascg(*args, **kwargs): # Animation scale group
li = []
i = 1
for a in args:
if i % 2 != 0:
li.append(ApplyMethod(a.scale, args[i], **kwargs))
i += 1
return AnimationGroup(*li)
def ash(a, b): # Animation Shift
return ApplyMethod(a.shift, b)
def ashg(*args, **kwargs): # Animation Shift group
li = []
i = 1
for a in args:
if i % 2 != 0:
li.append(ApplyMethod(a.shift, args[i], **kwargs))
i += 1
return AnimationGroup(*li)
def asho(a, b, *args, **kwargs): # Animation Shift origin
return a.animate.shift(b, *args, **kwargs)
def asi(a, *args, **kwargs): # Animation SpinInFromNothing
return SpinInFromNothing(a, angle=2 * PI, *args, **kwargs)
def asir(a, *args, **kwargs): # Animation SpinInFromNothing reverse
return SpinInFromNothing(a, angle=2 * PI, *args, **kwargs, reverse_rate_function = True)
def aso(*args, **kwargs): # Animation set opacity
li = []
for i in range(len(args)):
if i % 2 == 0:
li.append(ApplyMethod(args[i].set_opacity, args[i+1], **kwargs))
return AnimationGroup(*li)
def ast(*args, **kwargs): # Animation ShrinkToCenter
def ast_single(a, *args, **kwargs):
dot = Dot(radius=0.01).set_opacity(0).move_to(a)
return at(a,dot)
li = []
for i in args:
li.append(ast_single(i))
return AnimationGroup(*li)
def asto(a, *args, **kwargs): # Animation ShrinkToCenter origin
# a will restore after shrink
a.save_state()
li = []
li.append(ShrinkToCenter(a, *args, **kwargs))
li.append(Restore(a, run_time=0))
return Succession(*li)
def asu(*args): # Animation Succession
return Succession(*args)
def at(a, b, *args, **kwargs): # Animation Transform() (improved)
# improve: a will not change after transform
a.save_state()
return Succession(ReplacementTransform(a, b, *args, **kwargs),Restore(a,run_time=0))
def atm(a, b, *args, **kwargs): # Animation TransformMatchingShapes()
return TransformMatchingShapes(a, b, *args, **kwargs)
def ato(a, b, *args, **kwargs): # Animation Transform() origin
return Transform(a, b, *args, **kwargs)
def auw(a, *args, **kwargs): # Animation Unwrite()
return Unwrite(a, *args, **kwargs)
def aw(*args, **kwargs): # Animation Wait()
return Wait(*args, **kwargs)
def awi(a, *args, **kwargs): # Animation Wiggle()
return Wiggle(a, *args, **kwargs)
def awr(a, *args, **kwargs): # Animation Write()
return Write(a, *args, **kwargs)
def mab(a, b, *args, **kwargs): # mobject ArcBetweenPoints
return ArcBetweenPoints(a.get_center(), b.get_center(), angle = 120*DEGREES, *args, **kwargs)
def mbbm(a,b,buff,rise=0,single=0,dire=DOWN): # mobject BraceBetweenMobject
x = a.get_center() - b.get_center()
y = x/np.sqrt(x[0]**2+x[1]**2+x[2]**2)
if single == 0:
return BraceBetweenPoints(a.get_center() + buff + rise/2*y, b.get_center() + buff - rise/2*y)
else:
return BraceBetweenPoints(a.get_center() + buff - rise/2*dire, b.get_center() + buff + rise/2*(dire))
def mdl(a, b, *args, **kwargs): # mobject DashedLine
return DashedLine(a, b, *args, **kwargs)
def mmt(text, *args, **kwargs): # mobject MathTex
return MathTex(text, *args, **kwargs)
def msm(name, *args, **kwargs): # mobject SVGMobject
sm = SVGMobject(f'D:\\manimSVG\\{name}.svg')
return sm
def msmc(name, *args, **kwargs): # mobject SVGMobject constant
sm = SVGMobject(f'D:\\manimSVG\\constants\\{name}.svg').scale(0.4).shift(5*r)
return sm
def mt(text, *args, **kwargs): # mobject Text
return Text(text, *args, **kwargs)
def mte(*args, **kwargs): # mobject Tetrahedron
return Tetrahedron(*args, **kwargs)
def mteu(edge_length = 1.2): # mobject Tetrahedron upright
vertex_coords = [
[-np.sqrt(3)*edge_length, 0, -1*edge_length],
[np.sqrt(3)*edge_length, 0, -1*edge_length],
[0, 0, 2*edge_length],
[0, 2*np.sqrt(2)*edge_length, 0]
]
faces_list = [
[0, 1, 2],
[3, 1, 2],
[0, 3, 2],
[0, 1, 3]
]
return Polyhedron(vertex_coords, faces_list)
def mvg(*args, **kwargs): # mobject VGroup
return VGroup(*args, **kwargs)
if type('organization') == str:
def obc(*args): # organization beforehand coloring
i = 1
for a in args:
if i % 2 != 0:
a.set_color(args[i])
i += 1
def obmt(*args): # organization beforehand move to
i = 1
for a in args:
if i % 2 != 0:
a.move_to(args[i])
i += 1
def obnt(*args): # organization beforehand next to
i = 0
for a in args:
if i % 3 == 0:
a.next_to(args[i+1], args[i+2])
i += 1
def obntr(*args): # organization beforehand next to regardless of thickness
i = 0
for a in args:
if i % 3 == 0:
a.move_to(args[i+1]).shift(args[i+2])
i += 1
def obo(*args): # organization beforehand (set) opacity
i = 1
for a in args:
if i % 2 != 0:
a.set_opacity(args[i])
i += 1
def obsc(*args): # organization beforehand scale
i = 1
for a in args:
if i % 2 != 0:
if type(a) == list:
for j in a:
j.scale(args[i])
else:
a.scale(args[i])
i += 1
def obsh(*args): # organization beforehand shift
i = 1
for a in args:
if i % 2 != 0:
a.shift(args[i])
i += 1
def oc(*args): # organization check
self.camera.background_color = WHITE
full = FullScreenRectangle()
for svg in args:
if type(svg) == list:
self.clear()
def checkedge(a):
for i in a:
if i.width/i.height > 1920/1080:
i.set(width = full.width/2.5)
else:
i.set(height = full.height/2.5)
checkedge(svg)
if len(svg) == 2:
svg[0].to_corner(UL)
svg[1].to_corner(DR)
elif len(svg) == 3:
svg[0].to_corner(UL)
svg[1].to_corner(UR)
svg[2].to_corner(DL)
elif len(svg) == 4:
svg[0].to_corner(UL)
svg[1].to_corner(UR)
svg[2].to_corner(DL)
svg[3].to_corner(DR)
for sss in svg:
self.add(sss.set(color = RED))
for sss in svg:
n = 0
for i in sss:
num = Integer(number=n, stroke_width=2).set_color(BLACK).move_to(i)
n += 1
self.add(num)
self.wait()
else:
self.clear()
self.add(svg.set(color = RED))
n = 0
for i in svg:
num = Integer(number=n, stroke_width=2).set_color(BLACK).move_to(i)
n += 1
self.add(num)
self.wait()
self.clear()
def ocoi(*args): # organization color and opacity init
def xc2h(c):
return rgb_to_hex(color_to_rgb(c))
for a in args:
# 先缩放再设置透明度(否则会消失不见)
a.set_opacity(1)
for i in a:
if xc2h(i.color) == '#ff0000':
i.set_color([RED_C, RED_E]).set_sheen(0.3).set_opacity(0.8)
elif xc2h(i.color) == '#ffff00':
i.set_color([GOLD_C, GOLD_E]).set_sheen(0.3).set_opacity(0.8)
elif xc2h(i.color) == '#00ff00':
i.set_color([GREEN_C, GREEN_E]).set_sheen(0.3).set_opacity(0.8)
elif xc2h(i.color) == '#00ffff':
i.set_color([TEAL_C, TEAL_E]).set_sheen(0.3).set_opacity(0.8)
elif xc2h(i.color) == '#0000ff':
i.set_color([BLUE_C, BLUE_E]).set_sheen(0.3).set_opacity(0.8)
elif xc2h(i.color) == '#ff00ff':
i.set_color([PINK_C, PINK_E]).set_sheen(0.3).set_opacity(0.8)
def oen(a): # organization execute narrator
for i in a:
globals()[f't{str(i)}']=xn(a[i])
def oeo(name,a): # organization execute object
# attention: when you try to use object defined before in oeo(), you should run this function before you defined
for i in a:
if type(a[i]) == str:
globals()[name + str(i)]=xn(a[i], font_type=2)
else:
globals()[name + str(i)]=a[i]
def oes(name, num): # organization execute SVGMobject
for i in range(num):
globals()[name + str(i+1)]=msm(name + str(i+1))
def olal(t,*argss): # organization lagged animations list
args = []
for i in argss:
args.append(i)
args.reverse() # 不是args = args.reverse()
n = 0
for a in args:
if n == 1:
ag = AnimationGroup(args[1],args[0],lag_ratio=t)
elif n > 1:
ag = AnimationGroup(a,ag,lag_ratio=t)
n += 1
return ag
def oms(a, n1, b, n2): # organization move and scale (according to components)
scale = b[n2].width/a[n1].width
a.scale(scale)
vect = b[n2].get_center() - a[n1].get_center()
a.shift(vect)
def onp(*args): # organization normalize perspective
for i in args:
i.rotate(angle = -PI/2, axis=[1,0,0]) # 让 z 轴向上
i.rotate(angle = PI/6, axis=[1,0,0])
def ont(a,n,b,m,buff): # organization next to (according to components)
a1 = a[n].copy()
a[n].next_to(b[m],buff)
vect = a[n].get_center() - a1.get_center()
aleft = mvg(a[0:n],a[n+1:])
aleft.shift(vect)
def oos(num, *args): # organization opacity scale (according to components)
# num represents the magnification of the 1st mobject, args represent the mobjects
def premovescale(a, b):
for i in a:
# 设置 get_fill_opacity() == 0.5 会失效
if i.get_fill_opacity() < 1:
x = i
for j in b:
if j.get_fill_opacity() < 1:
y = j
scale = y.height/x.height
a.scale(scale)
# 先缩放再移动
vector = y.get_center() - x.get_center()
a.shift(vector)
args[0].scale(num)
for n in range(len(args) - 1):
premovescale(args[n+1], args[n])
ocoi(*args)
def opic(r, *args): # organization place in circle
n = 0
vg = VGroup()
for i in args:
i.move_to(ORIGIN)
i.shift(r*np.cos(n*2*PI/len(args) + PI/2)*RIGHT + r*np.sin(n*2*PI/len(args) + PI/2)*UP)
vg.add(i)
n += 1
return vg
def opifb(*args): # organization place in flex box
# about the screen:
# 横向为 x 方向,纵向为 y 方向,垂直屏幕方向为 z 方向(不使用 set_camera_orientation())
# x 方向为 width,y 方向为 height,z 方向为 depth
# 总宽为 13.2,即 6.6 * 2
# 总高为 5.6,即 2.8 * 2(除去底部旁白和顶部目录)
full_width = 13.2
full_height = 5.6
buff = 1.2 # 物体之间的间距
def scale_according_to_aspect_radio(mobject, width, height):
aspect_radio = width/height
mob_aspect_radio = mobject.width/mobject.height
if mob_aspect_radio > aspect_radio:
mobject.scale_to_fit_width(width)
else:
mobject.scale_to_fit_height(height)
row_num = len(args)
each_mob_height = (full_height - (row_num - 1)*buff)/row_num
row_index = 0
for arg in args:
column_num = len(arg)
each_mob_width = (full_width - (column_num - 1)*buff)/column_num
column_index = 0
for mob in arg:
scale_according_to_aspect_radio(mob, each_mob_width, each_mob_height)
mob.move_to(ORIGIN)
mob.shift(full_width/2*LEFT + full_height/2*UP + (row_index + 1/2)*each_mob_height*DOWN + row_index*buff*DOWN + (column_index + 1/2)*each_mob_width*RIGHT + column_index*buff*RIGHT)
column_index += 1
row_index += 1
def osp(*args): # organization successive play (deprecated)
for a in args:
if type(a) == list:
self.wait(0.5)
self.play(*a)
self.wait(0.5)
elif type(a) == tuple:
self.clear()
self.add(*a)
elif a.__class__.__name__ == 'Wait':
self.play(a)
else:
self.wait(0.5)
self.play(a)
self.wait(0.5)