mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 21:44:19 +08:00
Better PiCreature.look methods
This commit is contained in:
@ -2,7 +2,7 @@ from helpers import *
|
|||||||
|
|
||||||
from mobject import Mobject
|
from mobject import Mobject
|
||||||
from mobject.svg_mobject import SVGMobject
|
from mobject.svg_mobject import SVGMobject
|
||||||
from mobject.vectorized_mobject import VMobject
|
from mobject.vectorized_mobject import VMobject, VGroup
|
||||||
from mobject.tex_mobject import TextMobject, TexMobject
|
from mobject.tex_mobject import TextMobject, TexMobject
|
||||||
|
|
||||||
from animation import Animation
|
from animation import Animation
|
||||||
@ -32,6 +32,7 @@ class PiCreature(SVGMobject):
|
|||||||
"corner_scale_factor" : 0.75,
|
"corner_scale_factor" : 0.75,
|
||||||
"flip_at_start" : False,
|
"flip_at_start" : False,
|
||||||
"is_looking_direction_purposeful" : False,
|
"is_looking_direction_purposeful" : False,
|
||||||
|
"start_corner" : None,
|
||||||
}
|
}
|
||||||
def __init__(self, mode = "plain", **kwargs):
|
def __init__(self, mode = "plain", **kwargs):
|
||||||
self.parts_named = False
|
self.parts_named = False
|
||||||
@ -44,6 +45,8 @@ class PiCreature(SVGMobject):
|
|||||||
self.init_colors()
|
self.init_colors()
|
||||||
if self.flip_at_start:
|
if self.flip_at_start:
|
||||||
self.flip()
|
self.flip()
|
||||||
|
if self.start_corner is not None:
|
||||||
|
self.to_corner(self.start_corner)
|
||||||
|
|
||||||
def name_parts(self):
|
def name_parts(self):
|
||||||
self.mouth = self.submobjects[MOUTH_INDEX]
|
self.mouth = self.submobjects[MOUTH_INDEX]
|
||||||
@ -75,43 +78,44 @@ class PiCreature(SVGMobject):
|
|||||||
self.body.set_fill(color)
|
self.body.set_fill(color)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def move_to(self, destination):
|
|
||||||
self.shift(destination-self.get_bottom())
|
|
||||||
return self
|
|
||||||
|
|
||||||
def change_mode(self, mode):
|
def change_mode(self, mode):
|
||||||
curr_center = self.get_center()
|
curr_eye_center = self.eyes.get_center()
|
||||||
curr_height = self.get_height()
|
curr_height = self.get_height()
|
||||||
should_be_flipped = self.is_flipped()
|
should_be_flipped = self.is_flipped()
|
||||||
if self.is_looking_direction_purposeful:
|
should_look = hasattr(self, "purposeful_looking_direction")
|
||||||
looking_direction = self.get_looking_direction()
|
if should_look:
|
||||||
|
looking_direction = self.purposeful_looking_direction
|
||||||
self.__init__(mode)
|
self.__init__(mode)
|
||||||
self.scale_to_fit_height(curr_height)
|
self.scale_to_fit_height(curr_height)
|
||||||
self.shift(curr_center)
|
self.shift(curr_eye_center - self.eyes.get_center())
|
||||||
if should_be_flipped ^ self.is_flipped():
|
if should_be_flipped ^ self.is_flipped():
|
||||||
self.flip()
|
self.flip()
|
||||||
if self.is_looking_direction_purposeful:
|
if should_look:
|
||||||
self.look(looking_direction)
|
self.look(looking_direction)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def look(self, direction):
|
def look(self, direction):
|
||||||
self.is_looking_direction_purposeful = True
|
direction = direction/np.linalg.norm(direction)
|
||||||
x, y = direction[:2]
|
self.purposeful_looking_direction = direction
|
||||||
for pupil, eye in zip(self.pupils.split(), self.eyes.split()):
|
for pupil, eye in zip(self.pupils.split(), self.eyes.split()):
|
||||||
pupil.move_to(eye, aligned_edge = direction)
|
pupil_radius = pupil.get_width()/2.
|
||||||
#Some hacky nudging is required here
|
eye_radius = eye.get_width()/2.
|
||||||
if y > 0 and x != 0: # Look up and to a side
|
pupil.move_to(eye)
|
||||||
nudge_size = pupil.get_height()/4.
|
if direction[1] < 0:
|
||||||
if x > 0:
|
pupil.shift(pupil_radius*DOWN/3)
|
||||||
nudge = nudge_size*(DOWN+LEFT)
|
pupil.shift(direction*(eye_radius-pupil_radius))
|
||||||
else:
|
bottom_diff = eye.get_bottom()[1] - pupil.get_bottom()[1]
|
||||||
nudge = nudge_size*(DOWN+RIGHT)
|
if bottom_diff > 0:
|
||||||
pupil.shift(nudge)
|
pupil.shift(bottom_diff*UP)
|
||||||
elif y < 0:
|
|
||||||
nudge_size = pupil.get_height()/8.
|
|
||||||
pupil.shift(nudge_size*UP)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def look_at(self, point_or_mobject):
|
||||||
|
if isinstance(point_or_mobject, Mobject):
|
||||||
|
point = point_or_mobject.get_center()
|
||||||
|
else:
|
||||||
|
point = point_or_mobject
|
||||||
|
self.look(point - self.eyes.get_center())
|
||||||
|
|
||||||
|
|
||||||
def get_looking_direction(self):
|
def get_looking_direction(self):
|
||||||
return np.sign(np.round(
|
return np.sign(np.round(
|
||||||
@ -208,11 +212,12 @@ class Bubble(SVGMobject):
|
|||||||
"content_scale_factor" : 0.75,
|
"content_scale_factor" : 0.75,
|
||||||
"height" : 5,
|
"height" : 5,
|
||||||
"width" : 8,
|
"width" : 8,
|
||||||
|
"bubble_center_adjustment_factor" : 1./8,
|
||||||
"file_name" : None,
|
"file_name" : None,
|
||||||
"propogate_style_to_family" : True,
|
"propogate_style_to_family" : True,
|
||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
digest_config(self, kwargs, locals())
|
digest_config(self, kwargs)
|
||||||
if self.file_name is None:
|
if self.file_name is None:
|
||||||
raise Exception("Must invoke Bubble subclass")
|
raise Exception("Must invoke Bubble subclass")
|
||||||
svg_file = os.path.join(
|
svg_file = os.path.join(
|
||||||
@ -232,7 +237,8 @@ class Bubble(SVGMobject):
|
|||||||
return self.get_corner(DOWN+self.direction)-0.6*self.direction
|
return self.get_corner(DOWN+self.direction)-0.6*self.direction
|
||||||
|
|
||||||
def get_bubble_center(self):
|
def get_bubble_center(self):
|
||||||
return self.get_center() + self.get_height()*UP/8.0
|
factor = self.bubble_center_adjustment_factor
|
||||||
|
return self.get_center() + factor*self.get_height()*UP
|
||||||
|
|
||||||
def move_tip_to(self, point):
|
def move_tip_to(self, point):
|
||||||
self.shift(point - self.get_tip())
|
self.shift(point - self.get_tip())
|
||||||
@ -324,14 +330,16 @@ class TeacherStudentsScene(Scene):
|
|||||||
self.teacher = Mortimer()
|
self.teacher = Mortimer()
|
||||||
self.teacher.to_corner(DOWN + RIGHT)
|
self.teacher.to_corner(DOWN + RIGHT)
|
||||||
self.teacher.look(DOWN+LEFT)
|
self.teacher.look(DOWN+LEFT)
|
||||||
self.students = VMobject(*[
|
self.students = VGroup(*[
|
||||||
Randolph(color = c)
|
Randolph(color = c)
|
||||||
for c in BLUE_D, BLUE_C, BLUE_E
|
for c in BLUE_D, BLUE_C, BLUE_E
|
||||||
])
|
])
|
||||||
self.students.arrange_submobjects(RIGHT)
|
self.students.arrange_submobjects(RIGHT)
|
||||||
self.students.scale(0.8)
|
self.students.scale(0.8)
|
||||||
self.students.to_corner(DOWN+LEFT)
|
self.students.to_corner(DOWN+LEFT)
|
||||||
self.students = self.students.split()
|
self.teacher.look_at(self.students[-1].eyes)
|
||||||
|
for student in self.students:
|
||||||
|
student.look_at(self.teacher.eyes)
|
||||||
|
|
||||||
for pi_creature in self.get_everyone():
|
for pi_creature in self.get_everyone():
|
||||||
pi_creature.bubble = None
|
pi_creature.bubble = None
|
||||||
@ -344,7 +352,7 @@ class TeacherStudentsScene(Scene):
|
|||||||
return self.students
|
return self.students
|
||||||
|
|
||||||
def get_everyone(self):
|
def get_everyone(self):
|
||||||
return [self.get_teacher()] + self.get_students()
|
return [self.get_teacher()] + list(self.get_students())
|
||||||
|
|
||||||
def get_bubble_intro_animation(self, content, bubble_type,
|
def get_bubble_intro_animation(self, content, bubble_type,
|
||||||
pi_creature,
|
pi_creature,
|
||||||
@ -427,10 +435,15 @@ class TeacherStudentsScene(Scene):
|
|||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
def change_student_modes(self, *modes):
|
def change_student_modes(self, *modes):
|
||||||
self.play(*[
|
pairs = zip(self.get_students(), modes)
|
||||||
ApplyMethod(pi.change_mode, mode)
|
start = VGroup(*[s for s, m in pairs])
|
||||||
for pi, mode in zip(self.get_students(), modes)
|
target = VGroup(*[s.copy().change_mode(m) for s, m in pairs])
|
||||||
])
|
self.play(Transform(
|
||||||
|
start, target,
|
||||||
|
submobject_mode = "lagged_start",
|
||||||
|
run_time = 2
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
def zoom_in_on_thought_bubble(self, radius = SPACE_HEIGHT+SPACE_WIDTH):
|
def zoom_in_on_thought_bubble(self, radius = SPACE_HEIGHT+SPACE_WIDTH):
|
||||||
bubble = None
|
bubble = None
|
||||||
|
Reference in New Issue
Block a user