Better PiCreature.look methods

This commit is contained in:
Grant Sanderson
2016-09-07 22:03:56 -07:00
parent 337991e8d1
commit a58afe13e8

View File

@ -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