def handle_mathy(self, creatures): self.fade_all_but(creatures, 2) physy, compy, mathy = creatures v_color = YELLOW w_color = BLUE sum_color = GREEN v_arrow = Vector([1, 1]) w_arrow = Vector([2, 1]) w_arrow.shift(v_arrow.get_end()) sum_arrow = Vector(w_arrow.get_end()) arrows = VMobject(v_arrow, w_arrow, sum_arrow) arrows.scale(0.7) arrows.to_edge(LEFT, buff = 2) v_array = matrix_to_mobject([3, -5]) w_array = matrix_to_mobject([2, 1]) sum_array = matrix_to_mobject(["3+2", "-5+1"]) arrays = VMobject( v_array, TexMobject("+"), w_array, TexMobject("="), sum_array ) arrays.arrange_submobjects(RIGHT) arrays.scale(0.75) arrays.to_edge(RIGHT).shift(UP) v_sym = TexMobject("\\vec{\\textbf{v}}") w_sym = TexMobject("\\vec{\\textbf{w}}") syms = VMobject(v_sym, TexMobject("+"), w_sym) syms.arrange_submobjects(RIGHT) syms.center().shift(2*UP) statement = TextMobject("We'll ignore him \\\\ for now") statement.highlight(PINK) statement.scale_to_fit_width(arrays.get_width()) statement.next_to(arrays, DOWN, buff = 1.5) circle = Circle() circle.shift(syms.get_bottom()) VMobject(v_arrow, v_array, v_sym).highlight(v_color) VMobject(w_arrow, w_array, w_sym).highlight(w_color) VMobject(sum_arrow, sum_array).highlight(sum_color) self.play( Write(syms), Write(arrays), ShowCreation(arrows, submobject_mode = "one_at_a_time"), ApplyMethod(mathy.change_mode, "pondering"), run_time = 2 ) self.play(Blink(mathy)) self.add_scaling(arrows, syms, arrays) self.play(Write(statement)) self.play(ApplyMethod(mathy.change_mode, "sad")) self.wait() self.play( ShowCreation(circle), ApplyMethod(mathy.change_mode, "plain") ) self.wait()
def handle_mathy(self, creatures): self.fade_all_but(creatures, 2) physy, compy, mathy = creatures v_color = YELLOW w_color = BLUE sum_color = GREEN v_arrow = Vector([1, 1]) w_arrow = Vector([2, 1]) w_arrow.shift(v_arrow.get_end()) sum_arrow = Vector(w_arrow.get_end()) arrows = VMobject(v_arrow, w_arrow, sum_arrow) arrows.scale(0.7) arrows.to_edge(LEFT, buff = 2) v_array = matrix_to_mobject([3, -5]) w_array = matrix_to_mobject([2, 1]) sum_array = matrix_to_mobject(["3+2", "-5+1"]) arrays = VMobject( v_array, TexMobject("+"), w_array, TexMobject("="), sum_array ) arrays.arrange_submobjects(RIGHT) arrays.scale(0.75) arrays.to_edge(RIGHT).shift(UP) v_sym = TexMobject("\\vec{\\textbf{v}}") w_sym = TexMobject("\\vec{\\textbf{w}}") syms = VMobject(v_sym, TexMobject("+"), w_sym) syms.arrange_submobjects(RIGHT) syms.center().shift(2*UP) statement = TextMobject("We'll ignore him \\\\ for now") statement.highlight(PINK) statement.scale_to_fit_width(arrays.get_width()) statement.next_to(arrays, DOWN, buff = 1.5) circle = Circle() circle.shift(syms.get_bottom()) VMobject(v_arrow, v_array, v_sym).highlight(v_color) VMobject(w_arrow, w_array, w_sym).highlight(w_color) VMobject(sum_arrow, sum_array).highlight(sum_color) self.play( Write(syms), Write(arrays), ShowCreation(arrows, submobject_mode = "one_at_a_time"), ApplyMethod(mathy.change_mode, "pondering"), run_time = 2 ) self.play(Blink(mathy)) self.add_scaling(arrows, syms, arrays) self.play(Write(statement)) self.play(ApplyMethod(mathy.change_mode, "sad")) self.dither() self.play( ShowCreation(circle), ApplyMethod(mathy.change_mode, "plain") ) self.dither()
class PiCreature(SVGMobject): CONFIG = { "color": BLUE_E, "stroke_width": 0, "fill_opacity": 1.0, "initial_scale_factor": 0.01, "corner_scale_factor": 0.75, "flip_at_start": False, "is_looking_direction_purposeful": False, "start_corner": None, } def __init__(self, mode="plain", **kwargs): self.parts_named = False svg_file = os.path.join(PI_CREATURE_DIR, "PiCreatures_%s.svg" % mode) digest_config(self, kwargs, locals()) SVGMobject.__init__(self, svg_file, **kwargs) self.init_colors() if self.flip_at_start: self.flip() if self.start_corner is not None: self.to_corner(self.start_corner) def name_parts(self): self.mouth = self.submobjects[MOUTH_INDEX] self.body = self.submobjects[BODY_INDEX] self.pupils = VMobject(*[ self.submobjects[LEFT_PUPIL_INDEX], self.submobjects[RIGHT_PUPIL_INDEX] ]) self.eyes = VMobject(*[ self.submobjects[LEFT_EYE_INDEX], self.submobjects[RIGHT_EYE_INDEX] ]) self.submobjects = [] self.add(self.body, self.mouth, self.eyes, self.pupils) self.parts_named = True def init_colors(self): self.set_stroke(color=BLACK, width=self.stroke_width) if not self.parts_named: self.name_parts() self.mouth.set_fill(BLACK, opacity=1) self.body.set_fill(self.color, opacity=1) self.pupils.set_fill(BLACK, opacity=1) self.eyes.set_fill(WHITE, opacity=1) return self def highlight(self, color): self.body.set_fill(color) return self def change_mode(self, mode): curr_eye_center = self.eyes.get_center() curr_height = self.get_height() should_be_flipped = self.is_flipped() should_look = hasattr(self, "purposeful_looking_direction") if should_look: looking_direction = self.purposeful_looking_direction self.__init__(mode) self.scale_to_fit_height(curr_height) self.shift(curr_eye_center - self.eyes.get_center()) if should_be_flipped ^ self.is_flipped(): self.flip() if should_look: self.look(looking_direction) return self def look(self, direction): direction = direction / np.linalg.norm(direction) self.purposeful_looking_direction = direction for pupil, eye in zip(self.pupils.split(), self.eyes.split()): pupil_radius = pupil.get_width() / 2. eye_radius = eye.get_width() / 2. pupil.move_to(eye) if direction[1] < 0: pupil.shift(pupil_radius * DOWN / 3) pupil.shift(direction * (eye_radius - pupil_radius)) bottom_diff = eye.get_bottom()[1] - pupil.get_bottom()[1] if bottom_diff > 0: pupil.shift(bottom_diff * UP) 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()) return self def get_looking_direction(self): return np.sign( np.round(self.pupils.get_center() - self.eyes.get_center(), decimals=2)) def is_flipped(self): return self.eyes.submobjects[0].get_center()[0] > \ self.eyes.submobjects[1].get_center()[0] def blink(self): eye_bottom_y = self.eyes.get_bottom()[1] for mob in self.eyes, self.pupils: mob.apply_function(lambda p: [p[0], eye_bottom_y, p[2]]) return self def to_corner(self, vect=None): if vect is not None: SVGMobject.to_corner(self, vect) else: self.scale(self.corner_scale_factor) self.to_corner(DOWN + LEFT) return self def get_bubble(self, bubble_type="thought", **kwargs): if bubble_type == "thought": bubble = ThoughtBubble(**kwargs) elif bubble_type == "speech": bubble = SpeechBubble(**kwargs) else: raise Exception("%s is an invalid bubble type" % bubble_type) bubble.pin_to(self) return bubble
class PiCreature(SVGMobject): CONFIG = { "color" : BLUE_E, "stroke_width" : 0, "fill_opacity" : 1.0, "initial_scale_factor" : 0.01, "corner_scale_factor" : 0.75, "flip_at_start" : False, "is_looking_direction_purposeful" : False, } def __init__(self, mode = "plain", **kwargs): self.parts_named = False svg_file = os.path.join( PI_CREATURE_DIR, "PiCreatures_%s.svg"%mode ) digest_config(self, kwargs, locals()) SVGMobject.__init__(self, svg_file, **kwargs) self.init_colors() if self.flip_at_start: self.flip() def name_parts(self): self.mouth = self.submobjects[MOUTH_INDEX] self.body = self.submobjects[BODY_INDEX] self.pupils = VMobject(*[ self.submobjects[LEFT_PUPIL_INDEX], self.submobjects[RIGHT_PUPIL_INDEX] ]) self.eyes = VMobject(*[ self.submobjects[LEFT_EYE_INDEX], self.submobjects[RIGHT_EYE_INDEX] ]) self.submobjects = [] self.add(self.body, self.mouth, self.eyes, self.pupils) self.parts_named = True def init_colors(self): self.set_stroke(color = BLACK, width = self.stroke_width) if not self.parts_named: self.name_parts() self.mouth.set_fill(BLACK, opacity = 1) self.body.set_fill(self.color, opacity = 1) self.pupils.set_fill(BLACK, opacity = 1) self.eyes.set_fill(WHITE, opacity = 1) return self def highlight(self, color): self.body.set_fill(color) return self def move_to(self, destination): self.shift(destination-self.get_bottom()) return self def change_mode(self, mode): curr_center = self.get_center() curr_height = self.get_height() should_be_flipped = self.is_flipped() if self.is_looking_direction_purposeful: looking_direction = self.get_looking_direction() self.__init__(mode) self.scale_to_fit_height(curr_height) self.shift(curr_center) if should_be_flipped ^ self.is_flipped(): self.flip() if self.is_looking_direction_purposeful: self.look(looking_direction) return self def look(self, direction): self.is_looking_direction_purposeful = True x, y = direction[:2] for pupil, eye in zip(self.pupils.split(), self.eyes.split()): pupil.move_to(eye, aligned_edge = direction) #Some hacky nudging is required here if y > 0 and x != 0: # Look up and to a side nudge_size = pupil.get_height()/4. if x > 0: nudge = nudge_size*(DOWN+LEFT) else: nudge = nudge_size*(DOWN+RIGHT) pupil.shift(nudge) elif y < 0: nudge_size = pupil.get_height()/8. pupil.shift(nudge_size*UP) return self def get_looking_direction(self): return np.sign(np.round( self.pupils.get_center() - self.eyes.get_center(), decimals = 2 )) def is_flipped(self): return self.eyes.submobjects[0].get_center()[0] > \ self.eyes.submobjects[1].get_center()[0] def blink(self): eye_bottom_y = self.eyes.get_bottom()[1] for mob in self.eyes, self.pupils: mob.apply_function( lambda p : [p[0], eye_bottom_y, p[2]] ) return self def to_corner(self, vect = None): if vect is not None: SVGMobject.to_corner(self, vect) else: self.scale(self.corner_scale_factor) self.to_corner(DOWN+LEFT) return self def get_bubble(self, bubble_type = "thought", **kwargs): if bubble_type == "thought": bubble = ThoughtBubble(**kwargs) elif bubble_type == "speech": bubble = SpeechBubble(**kwargs) else: raise Exception("%s is an invalid bubble type"%bubble_type) bubble.pin_to(self) return bubble