from manim import *
frame_width = config["frame_width"]
frame_height = config["frame_height"]
def narrator(a):
for i in a:
globals()[f't{str(i)}']=Text(a[i], font = "STZhongsong").to_edge(DOWN).scale(0.6)
class POrbital(SVGMobject):
def __init__(self):
file_path = r"D:\manimSVG\figures\p_orbital.svg"
super().__init__(file_path)
self.set_opacity(0.4)
self.set_color(color=[BLUE_A, BLUE_D])
class SP2Orbital(VGroup):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
file_path = r"D:\manimSVG\figures\single_p_orbital.svg"
single_p_orbital = SVGMobject(file_path)
single_p_orbital.set_opacity(0.4).set_color(color=[BLUE_A, BLUE_D]).shift(1.2*UP)
p_orbitals = VGroup()
for i in range(0, 3):
p_orbitals.add(single_p_orbital.copy().rotate(2*PI/3*i, about_point=ORIGIN))
dot = Dot().set_opacity(0)
self.add(dot, p_orbitals)
self.dot = dot
self.p_orbitals = p_orbitals
self.scale(0.5)
def move_accurately_to(self, point_or_mobject):
if isinstance(point_or_mobject, Mobject):
distance = point_or_mobject.get_center() - self.dot.get_center()
else:
distance = point_or_mobject - self.dot.get_center()
self.shift(distance)
return self
def rotate_about_center(self, *args, **kwargs):
self.rotate(about_point=self.dot.get_center(), *args, **kwargs)
return self
class ResonanceMolecule(VMobject):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def mob_molecule(self, str, scale_factor=1):
molecule = SVGMobject(str).scale(scale_factor)
self.scale_factor = scale_factor
return molecule
def mob_p_orbitals(self, indexes, colors):
molecule = self.molecule.copy()
molecule.rotate(PI/4, axis=[1,0,0])
if type(indexes[0]) == int:
p_orbitals = VGroup(*(POrbital().move_to(molecule[index]) for index in indexes))
else:
p_orbitals = VGroup(*(POrbital().move_to(index) for index in indexes))
for i in range(len(colors)):
if colors[i] == BLUE:
p_orbitals[i].set_color(color=[BLUE_A, BLUE_D])
elif colors[i] == RED:
p_orbitals[i].set_color(color=[RED_A, RED_D])
elif colors[i] == PURPLE:
p_orbitals[i].set_color(color=[PURPLE_A, PURPLE_D])
return p_orbitals
def mob_sp2_orbitals(self, indexes, angles, scale_factor=1):
molecule = self.molecule
if type(indexes[0]) == int:
sp2_orbitals = VGroup(*(SP2Orbital().scale(scale_factor).move_accurately_to(molecule[indexes[i]]).rotate_about_center(angles[i]).scale(scale_factor) for i in range(len(indexes))))
else:
sp2_orbitals = VGroup(*(SP2Orbital().scale(scale_factor).move_accurately_to(indexes[i]).rotate_about_center(angles[i]) for i in range(len(indexes))))
return sp2_orbitals
def anim_show_p_orbitals(self, return_mob=False):
molecule = self.molecule
p_orbitals = self.p_orbitals
anim = Succession(Rotate(molecule, PI/4, axis=[1,0,0]), LaggedStart(*(FadeIn(orbital, shift=DOWN) for orbital in p_orbitals), run_time=len(p_orbitals)/2))
if return_mob:
molecule.rotate(PI/4, axis=[1,0,0])
return VGroup(molecule, p_orbitals)
else:
return anim
def anim_show_sp2_orbitals(self):
sp2_orbitals = self.sp2_orbitals
anim = LaggedStart(*(GrowFromCenter(sp2_orbital) for sp2_orbital in sp2_orbitals), run_time=len(sp2_orbitals)/1.5)
return anim
def anim_show_lines_between_p_orbitals(self, indexes_list = None, return_mob=False):
p_orbitals = self.p_orbitals
lines = VGroup()
if indexes_list is None:
for i in range(len(p_orbitals) - 1):
line1 = DashedLine(p_orbitals[i][2].get_center(), p_orbitals[i+1][2].get_center())
line2 = DashedLine(p_orbitals[i][5].get_center(), p_orbitals[i+1][5].get_center())
lines.add(line1, line2)
else:
for index in indexes_list:
line1 = DashedLine(p_orbitals[index[0]][2].get_center(), p_orbitals[index[1]][2].get_center())
line2 = DashedLine(p_orbitals[index[0]][5].get_center(), p_orbitals[index[1]][5].get_center())
lines.add(line1, line2)
anim = LaggedStart(*(Write(line) for line in lines), run_time=len(p_orbitals)/2)
if return_mob:
return lines
else:
return anim
def anim_change_between_resonance_contributors(self, mol_trans_list):
original_mol = self.molecule
target_mol = SVGMobject(mol_trans_list[0]).scale(self.scale_factor)
anim = Succession(Rotate(original_mol, -PI/4, axis=[1,0,0]), CorrespondingPartsTransform([original_mol, target_mol], mol_trans_list[1], run_time=3))
return anim
def anim_show_resonance_hybrid(self, resonance_structures, resonance_hybrid, direction_buffs_scales = [RIGHT, 2, 1, 1, 1]):
resonance_structures = VGroup(*(SVGMobject(file_path).scale(direction_buffs_scales[3]) for file_path in resonance_structures)).set_color(color=[BLUE_A, BLUE_D]).arrange(direction_buffs_scales[0], buff=direction_buffs_scales[1])
resonance_hybrid = SVGMobject(resonance_hybrid).set_color(color=[GREEN_A, GREEN_D]).scale(direction_buffs_scales[4]).next_to(resonance_structures, direction=DOWN, buff=direction_buffs_scales[2])
VGroup(resonance_structures, resonance_hybrid).center()
anim = Succession(Write(resonance_structures), Write(resonance_hybrid))
return anim
class Benzene(ResonanceMolecule):
def __init__(self):
self.molecule = self.mob_molecule(r"D:\manimSVG\molecules\benzene.svg", scale_factor=2)
self.p_orbitals = self.mob_p_orbitals()
self.sp2_orbitals = self.mob_sp2_orbitals()
self.show_p_orbitals = self.anim_show_p_orbitals()
self.show_sp2_orbitals = self.anim_show_sp2_orbitals()
self.show_large_pi_orbital = self.anim_show_large_pi_orbital()
self.to_different_bond_length = self.anim_to_different_bond_length()
self.to_same_bond_length = self.anim_to_same_bond_length()
self.show_lines_between_p_orbitals = self.anim_show_lines_between_p_orbitals(indexes_list=[[0,1], [1,2], [2,3], [3,4], [4,5], [5,0]], return_mob=True)
self.change_between_resonance_contributors = self.anim_change_between_resonance_contributors([r"D:\manimSVG\molecules\benzene_resonance_1.svg", [0, 1, [6, 0, 8, 1, 0, 3, 2, 4, 3, 6, 5, 7, 1, 2, 7, 8, 4, 5]]])
self.show_resonance_hybrid = self.anim_show_resonance_hybrid([r"D:\manimSVG\molecules\benzene.svg", r"D:\manimSVG\molecules\benzene_resonance_1.svg"], r"D:\manimSVG\molecules\benzene_resonance_hybrid.svg")
def mob_p_orbitals(self):
molecule = self.molecule.copy()
molecule.rotate(PI/4, axis=[1,0,0])
points = [molecule.get_left(), molecule[2].get_left(), molecule[2].get_right(), molecule.get_right(), molecule[6].get_right(), molecule[6].get_left()]
p_orbitals = super().mob_p_orbitals(points, [BLUE for i in range(6)])
return p_orbitals
def mob_sp2_orbitals(self):
molecule = self.molecule
points = [molecule.get_left(), molecule[2].get_left(), molecule[2].get_right(), molecule.get_right(), molecule[6].get_right(), molecule[6].get_left()]
angles = [90*DEGREES, 150*DEGREES, 210*DEGREES, 270*DEGREES, 330*DEGREES, 390*DEGREES]
sp2_orbitals = super().mob_sp2_orbitals(points, angles, scale_factor=0.9)
return sp2_orbitals
def anim_show_large_pi_orbital(self):
large_pi_orbital1 = SVGMobject(r"D:\manimSVG\figures\large_pi_orbital.svg").scale(1.5)
large_pi_orbital1.set(fill_opacity=0, stroke_color=BLUE, stroke_opacity=0.6)
large_pi_orbital2 = large_pi_orbital1.copy()
large_pi_orbital1.shift(0.5*UP)
large_pi_orbital2.shift(0.5*DOWN)
anim = Succession(FadeIn(large_pi_orbital1, shift=DOWN), FadeIn(large_pi_orbital2, shift=UP))
return anim
def anim_to_different_bond_length(self):
mol = self.molecule
target = SVGMobject(r"D:\manimSVG\molecules\benzene_with_different_bond_length.svg").scale(self.scale_factor)
return Transform(mol, target)
def anim_to_same_bond_length(self):
mol = self.molecule
target = SVGMobject(r"D:\manimSVG\molecules\benzene.svg").scale(self.scale_factor)
return Transform(mol, target)
class Example1(ResonanceMolecule):
def __init__(self):
self.molecule = self.mob_molecule(r"D:\manimSVG\molecules\example_1.svg", scale_factor=2)
self.p_orbitals = self.mob_p_orbitals([10, 6, 8], [BLUE, BLUE, PURPLE])
self.sp2_orbitals = self.mob_sp2_orbitals([6, 10], [PI/2, PI/6], scale_factor=1.05)
self.show_p_orbitals = self.anim_show_p_orbitals()
self.show_sp2_orbitals = self.anim_show_sp2_orbitals()
self.show_lines_between_p_orbitals = self.anim_show_lines_between_p_orbitals()
self.change_between_resonance_contributors = self.anim_change_between_resonance_contributors([r"D:\manimSVG\molecules\example_1_resonance_1.svg", [0, 1, ['0.9', '0.9', 9, 10, 14, 9, 13, 12, 12, 14, 10, 11]]])
self.show_resonance_hybrid = self.anim_show_resonance_hybrid([r"D:\manimSVG\molecules\example_1.svg", r"D:\manimSVG\molecules\example_1_resonance_1.svg"], r"D:\manimSVG\molecules\example_1_resonance_hybrid.svg")
class Example2(ResonanceMolecule):
def __init__(self):
self.molecule = self.mob_molecule(r"D:\manimSVG\molecules\example_2.svg")
self.p_orbitals = self.mob_p_orbitals([3, 5, 9], [BLUE, BLUE, RED])
self.sp2_orbitals = self.mob_sp2_orbitals([3, 5], [-PI/2, PI/2], scale_factor=1.2)
self.show_p_orbitals = self.anim_show_p_orbitals()
self.show_sp2_orbitals = self.anim_show_sp2_orbitals()
self.show_lines_between_p_orbitals = self.anim_show_lines_between_p_orbitals()
self.change_between_resonance_contributors = self.anim_change_between_resonance_contributors([r"D:\manimSVG\molecules\example_2_resonance_1.svg", [0, 1, ['0.7', '0.7', 8, 14, 7, 12, 14, 13, '9.14', '7.12']]])
self.show_resonance_hybrid = self.anim_show_resonance_hybrid([r"D:\manimSVG\molecules\example_2.svg", r"D:\manimSVG\molecules\example_2_resonance_1.svg"], r"D:\manimSVG\molecules\example_2_resonance_hybrid.svg", direction_buffs_scales = [RIGHT, 1.5, 1, 0.5, 0.35])
class Example3(ResonanceMolecule):
pass
class Example4(ResonanceMolecule):
def __init__(self):
self.molecule = self.mob_molecule(r"D:\manimSVG\molecules\example_4.svg", scale_factor=2)
self.p_orbitals = self.mob_p_orbitals([1, 0, 4], [BLUE, BLUE, PURPLE])
self.sp2_orbitals = self.mob_sp2_orbitals([1, 0], [PI, 0], scale_factor=1.05)
self.show_p_orbitals = self.anim_show_p_orbitals()
self.show_sp2_orbitals = self.anim_show_sp2_orbitals()
self.show_lines_between_p_orbitals = self.anim_show_lines_between_p_orbitals()
self.change_between_resonance_contributors = self.anim_change_between_resonance_contributors([r"D:\manimSVG\molecules\example_4_resonance_1.svg", [0, 1, [11, 9, 1, 1, 12, 10, 2, 11, 3, 13, 0, 0, 9, 8, 8, 7, 7, 6, 10, 5, '4.7', '2.5']]])
self.show_resonance_hybrid = self.anim_show_resonance_hybrid([r"D:\manimSVG\molecules\example_4.svg", r"D:\manimSVG\molecules\example_4_resonance_1.svg"], r"D:\manimSVG\molecules\example_4_resonance_hybrid.svg", direction_buffs_scales = [RIGHT, 2, 1, 1.1, 1.1])
class MyScene(Scene):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.current_narrator = None
self.fixed_mobjects = []
def play_with_narrator(self, text, *anims, start=False, end=False, start_end=False):
def text_duration(text, *anims):
time = len(text)/10
if anims != ():
time += 1 # 基础时间为 1s
anim_run_time_list = []
for anim in anims:
anim_run_time_list.append(anim.get_run_time())
anim_run_time = max(anim_run_time_list)
if anim_run_time >= time:
time = 1
else:
time -= anim_run_time
return time
if start:
self.play(FadeIn(text), *anims)
self.wait(text_duration(text, *anims), frozen_frame =False) # 使用 frozen_frame 可以让 updater 动画正常运行
self.current_narrator = text
self.add_fixed_mobjects(text)
elif end:
origin_text = self.current_narrator
self.play(Transform(origin_text, text), *anims)
self.wait(text_duration(text, *anims), frozen_frame =False)
self.play(FadeOut(origin_text))
self.current_narrator = None
self.remove_fixed_mobject(origin_text)
elif start_end:
self.play(FadeIn(text), *anims)
self.wait(text_duration(text, *anims), frozen_frame =False)
self.play(FadeOut(text))
elif not start and not end:
origin_text = self.current_narrator
self.play(Transform(origin_text, text), *anims)
self.wait(text_duration(text, *anims), frozen_frame =False)
if self.current_narrator is not None:
self.current_narrator.set_z_index(10)
def clear_all(self, except_fixed_mobjects = False):
if except_fixed_mobjects:
anim = [FadeOut(*self.mobjects), *(mob.animate.scale(1) for mob in self.fixed_mobjects)]
else:
anim = [FadeOut(*self.mobjects)]
self.fixed_mobjects = []
self.play(*anim)
self.clear() # 这一步不可忽略,否则会出现动画完成后物件突然消失的 bug
self.add(*self.fixed_mobjects)
def add_fixed_mobjects(self, *mobs):
self.fixed_mobjects.extend(mobs)
def remove_fixed_mobject(self, *mobs):
for mob in mobs:
self.fixed_mobjects.remove(mob)
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)
# item = ReplacementTransform(u,v)
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)