diff --git a/eoc/chapter1.py b/eoc/chapter1.py index ec5bdb19..c8c69f36 100644 --- a/eoc/chapter1.py +++ b/eoc/chapter1.py @@ -23,7 +23,7 @@ from camera import Camera from mobject.svg_mobject import * from mobject.tex_mobject import * -class CircleScene(Scene): +class CircleScene(PiCreatureScene): CONFIG = { "radius" : 1.5, "stroke_color" : WHITE, @@ -32,7 +32,9 @@ class CircleScene(Scene): "radial_line_color" : MAROON_B, "outer_ring_color" : GREEN_E, "dR" : 0.1, + "dR_color" : YELLOW, "unwrapped_tip" : ORIGIN, + "include_pi_creature" : False, } def setup(self): self.circle = Circle( @@ -48,14 +50,21 @@ class CircleScene(Scene): color = self.radial_line_color ) self.radius_brace = Brace(self.radius_line, buff = SMALL_BUFF) - self.radius_label = self.radius_brace.get_text("R", buff = SMALL_BUFF) + self.radius_label = self.radius_brace.get_text("$R$", buff = SMALL_BUFF) self.add( self.circle, self.radius_line, self.radius_brace, self.radius_label ) - def introduce_circle(self): + self.pi_creature = self.get_pi_creature() + if self.include_pi_creature: + self.add(self.pi_creature) + + def get_pi_creature(self): + return Mortimer().to_corner(DOWN+RIGHT) + + def introduce_circle(self, added_anims = []): self.remove(self.circle) self.play( ShowCreation(self.radius_line), @@ -70,6 +79,7 @@ class CircleScene(Scene): about_point = self.circle.get_center(), ), ShowCreation(self.circle), + *added_anims, run_time = 2 ) self.play( @@ -92,10 +102,11 @@ class CircleScene(Scene): nudge_line.get_center() + 0.5*RIGHT+DOWN, nudge_line.get_center(), color = YELLOW, - buff = 0.025, + buff = SMALL_BUFF, tip_length = 0.2, ) nudge_label = TexMobject("%.01f"%self.dR) + nudge_label.highlight(self.dR_color) nudge_label.scale(0.75) nudge_label.next_to(nudge_arrow.get_start(), DOWN) @@ -103,19 +114,18 @@ class CircleScene(Scene): outer_ring = self.get_outer_ring() - self.play(ShowCreation(nudge_line)) self.play( + FadeIn(outer_ring), + ShowCreation(nudge_line), ShowCreation(nudge_arrow), - Write(nudge_label) + Write(nudge_label), ) self.dither() - self.play( - FadeIn(outer_ring), - *map(Animation, radius_mobs) - ) + self.nudge_line = nudge_line + self.nudge_arrow = nudge_arrow + self.nudge_label = nudge_label return outer_ring - def get_ring(self, radius, dR, color = GREEN): ring = Circle(radius = radius + dR).center() inner_ring = Circle(radius = radius) @@ -134,10 +144,11 @@ class CircleScene(Scene): color = self.outer_ring_color ) - def unwrap_ring(self, ring): - self.unwrap_rings(ring) + def unwrap_ring(self, ring, **kwargs): + self.unwrap_rings(ring, **kwargs) - def unwrap_rings(self, *rings): + def unwrap_rings(self, *rings, **kwargs): + added_anims = kwargs.get("added_anims", []) rings = VGroup(*rings) unwrapped = VGroup(*[ self.get_unwrapped(ring) @@ -149,10 +160,10 @@ class CircleScene(Scene): run_time = 2, path_arc = np.pi/2 ) - self.play(Transform( - rings, unwrapped, - run_time = 3, - )) + self.play( + Transform(rings, unwrapped, run_time = 3), + *added_anims + ) def get_unwrapped(self, ring): R = ring.R @@ -174,6 +185,7 @@ class CircleScene(Scene): ) result.move_to(self.unwrapped_tip, aligned_edge = DOWN) result.shift(R_plus_dr*DOWN) + result.to_edge(LEFT) return result @@ -442,22 +454,606 @@ class IntroduceCircle(Scene): ) self.dither() -class IntroduceTinyChangeInArea(CircleScene): +class PragmatismToArt(Scene): def construct(self): - new_area_form, minus area_form = expression = TexMobject( + morty = Mortimer() + morty.to_corner(DOWN+RIGHT) + morty.shift(LEFT) + pragmatism = TextMobject("Pragmatism") + art = TextMobject("Art") + pragmatism.move_to(morty.get_corner(UP+LEFT), aligned_edge = DOWN) + art.move_to(morty.get_corner(UP+RIGHT), aligned_edge = DOWN) + art.shift(0.2*(LEFT+UP)) + + circle1 = Circle( + radius = 2, + fill_opacity = 1, + fill_color = BLUE_E, + stroke_width = 0, + ) + circle2 = Circle( + radius = 2, + stroke_color = YELLOW + ) + arrow = DoubleArrow(LEFT, RIGHT, color = WHITE) + circle_group = VGroup(circle1, arrow, circle2) + circle_group.arrange_submobjects() + circle_group.to_corner(UP+LEFT) + circle2.save_state() + circle2.move_to(circle1) + q_marks = TextMobject("???").next_to(arrow, UP) + + + self.play( + morty.change_mode, "raise_right_hand", + morty.look_at, pragmatism, + Write(pragmatism, run_time = 1), + ) + self.play(Blink(morty)) + self.play( + morty.change_mode, "raise_left_hand", + morty.look_at, art, + Transform( + VectorizedPoint(morty.get_corner(UP+RIGHT)), + art + ), + pragmatism.fade, 0.7, + pragmatism.rotate_in_place, np.pi/4, + pragmatism.shift, DOWN+LEFT + ) + self.play(Blink(morty)) + self.play( + GrowFromCenter(circle1), + morty.look_at, circle1 + ) + self.play(ShowCreation(circle2)) + self.play( + ShowCreation(arrow), + Write(q_marks), + circle2.restore + ) + self.play(Blink(morty)) + +class IntroduceTinyChangeInArea(CircleScene): + CONFIG = { + "include_pi_creature" : True, + } + def construct(self): + new_area_form, minus, area_form = expression = TexMobject( "\\pi (R + 0.1)^2", "-", "\\pi R^2" ) - expression.to_corner(UP+RIGHT, buff = 2*MED_BUFF) + VGroup(*new_area_form[4:7]).highlight(self.dR_color) + expression_brace = Brace(expression, UP) + change_in_area = expression_brace.get_text("Change in area") + change_in_area.highlight(self.outer_ring_color) + area_brace = Brace(area_form) + area_word = area_brace.get_text("Area") + area_word.highlight(BLUE) + new_area_brace = Brace(new_area_form) + new_area_word = new_area_brace.get_text("New area") + group = VGroup( + expression, expression_brace, change_in_area, + area_brace, area_word, new_area_brace, new_area_word + ) + group.to_edge(UP).shift(RIGHT) + group.save_state() + area_group = VGroup(area_form, area_brace, area_word) + area_group.save_state() + area_group.next_to(self.circle, RIGHT, buff = LARGE_BUFF) - self.introduce_circle() - self.dither() + self.introduce_circle( + added_anims = [self.pi_creature.change_mode, "speaking"] + ) + self.play(Write(area_group)) + self.change_mode("happy") outer_ring = self.increase_radius() self.dither() - # ring = self.get_outer_ring() - - - - + self.play( + area_group.restore, + GrowFromCenter(expression_brace), + Write(new_area_form), + Write(minus), + Write(change_in_area), + self.pi_creature.change_mode, "confused", + ) + self.play( + Write(new_area_word), + GrowFromCenter(new_area_brace) + ) + self.dither(2) + self.play( + group.fade, 0.7, + self.pi_creature.change_mode, "happy" + ) + self.dither() + self.play( + outer_ring.highlight, YELLOW, + Animation(self.nudge_arrow), + Animation(self.nudge_line), + rate_func = there_and_back + ) + self.show_unwrapping(outer_ring) + self.play(group.restore) + self.work_out_expression(group) + self.second_unwrapping(outer_ring) + insignificant = TextMobject("Insignificant") + insignificant.highlight(self.dR_color) + insignificant.move_to(self.error_words) + self.play(Transform(self.error_words, insignificant)) + self.dither() + + big_rect = Rectangle( + width = 2*SPACE_WIDTH, + height = 2*SPACE_HEIGHT, + fill_color = BLACK, + fill_opacity = 0.85, + stroke_width = 0, + ) + self.play( + FadeIn(big_rect), + area_form.highlight, BLUE, + self.two_pi_R.highlight, GREEN, + self.pi_creature.change_mode, "happy" + ) + + def show_unwrapping(self, outer_ring): + almost_rect = outer_ring.copy() + self.unwrap_ring( + almost_rect, + added_anims = [self.pi_creature.change_mode, "pondering"] + ) + + circum_brace = Brace(almost_rect, UP).scale_in_place(0.95) + dR_brace = TexMobject("\\}") + dR_brace.stretch(0.5, 1) + dR_brace.next_to(almost_rect, RIGHT) + two_pi_R = circum_brace.get_text("$2\\pi R$") + dR = TexMobject("$0.1$").scale(0.7).next_to(dR_brace, RIGHT) + dR.highlight(self.dR_color) + + two_pi_R.generate_target() + dR.generate_target() + lp, rp = TexMobject("()") + change_in_area = TextMobject( + "Change in area $\\approx$" + ) + final_area = VGroup( + change_in_area, + two_pi_R.target, lp, dR.target.scale(1./0.7), rp + ) + final_area.arrange_submobjects(RIGHT, buff = SMALL_BUFF) + final_area.next_to(almost_rect, DOWN, buff = 2*MED_BUFF) + final_area.highlight(GREEN_A) + final_area[3].highlight(self.dR_color) + change_in_area.shift(0.1*LEFT) + + self.play( + GrowFromCenter(circum_brace), + Write(two_pi_R) + ) + self.dither() + self.play( + GrowFromCenter(dR_brace), + Write(dR) + ) + self.dither() + self.play( + MoveToTarget(two_pi_R.copy()), + MoveToTarget(dR.copy()), + Write(change_in_area, run_time = 1), + Write(lp), + Write(rp), + ) + self.remove(*self.get_mobjects_from_last_animation()) + self.add(final_area) + self.play( + self.pi_creature.change_mode, "happy", + self.pi_creature.look_at, final_area + ) + self.dither() + group = VGroup( + almost_rect, final_area, two_pi_R, dR, + circum_brace, dR_brace + ) + self.play(group.fade) + + def work_out_expression(self, expression_group): + exp, exp_brace, title, area_brace, area_word, new_area_brace, new_area_word = expression_group + new_area_form, minus, area_form = exp + + expanded = TexMobject( + "\\pi R^2", "+", "2\\pi R (0.1)", + "+", "\\pi (0.1)^2", "-", "\\pi R^2", + ) + pi_R_squared, plus, two_pi_R_dR, plus2, pi_dR_squared, minus2, pi_R_squared2 = expanded + for subset in two_pi_R_dR[4:7], pi_dR_squared[2:5]: + VGroup(*subset).highlight(self.dR_color) + expanded.next_to(new_area_form, DOWN, aligned_edge = LEFT, buff = MED_BUFF) + expanded.shift(LEFT/2.) + + faders = [area_brace, area_word, new_area_brace, new_area_word] + self.play(*map(FadeOut, faders)) + trips = [ + ([0, 2, 8], pi_R_squared, plus), + ([8, 0, 2, 1, 4, 5, 6, 7], two_pi_R_dR, plus2), + ([0, 1, 4, 5, 6, 7, 8], pi_dR_squared, VGroup()), + ] + to_remove = [] + for subset, target, writer in trips: + starter = VGroup( + *np.array(list(new_area_form.copy()))[subset] + ) + self.play( + Transform(starter, target, run_time = 2), + Write(writer) + ) + to_remove += self.get_mobjects_from_last_animation() + self.dither() + self.play( + Transform(minus.copy(), minus2), + Transform(area_form.copy(), pi_R_squared2), + ) + to_remove += self.get_mobjects_from_last_animation() + self.remove(*to_remove) + self.add(self.pi_creature, *expanded) + self.dither(2) + self.play(*[ + ApplyMethod(mob.highlight, RED) + for mob in pi_R_squared, pi_R_squared2 + ]) + self.dither() + self.play(*[ + ApplyMethod(mob.fade, 0.7) + for mob in plus, pi_R_squared, pi_R_squared2, minus2 + ]) + self.dither() + + approx_brace = Brace(two_pi_R_dR) + error_brace = Brace(pi_dR_squared, buff = SMALL_BUFF) + error_words = error_brace.get_text("Error", buff = SMALL_BUFF) + error_words.highlight(RED) + self.error_words = error_words + + self.play( + GrowFromCenter(approx_brace), + self.pi_creature.change_mode, "hooray" + ) + self.dither() + self.play( + GrowFromCenter(error_brace), + Write(error_words), + self.pi_creature.change_mode, "confused" + ) + self.dither() + self.two_pi_R = VGroup(*two_pi_R_dR[:3]) + + def second_unwrapping(self, outer_ring): + almost_rect = outer_ring.copy() + rect = Rectangle( + width = 2*np.pi*self.radius, + height = self.dR, + fill_color = self.outer_ring_color, + fill_opacity = 1, + stroke_width = 0, + ) + self.play( + almost_rect.highlight, YELLOW, + self.pi_creature.change_mode, "pondering" + ) + self.unwrap_ring(almost_rect) + self.dither() + rect.move_to(almost_rect) + self.play(FadeIn(rect)) + self.dither() + + def get_pi_creature(self): + morty = Mortimer() + morty.scale(0.7) + morty.to_corner(DOWN+RIGHT) + return morty + +class BuildToDADR(CircleScene): + CONFIG = { + "include_pi_creature" : True, + } + def construct(self): + self.outer_ring = self.increase_radius() + self.write_initial_terms() + self.show_fractions() + self.transition_to_dR() + self.elaborate_on_d() + self.not_infinitely_small() + + def get_pi_creature(self): + morty = Mortimer() + morty.flip() + morty.to_corner(DOWN+LEFT) + return morty + + def write_initial_terms(self): + change = TextMobject("Change in area") + change.highlight(GREEN_B) + equals, two_pi_R, dR, plus, pi, dR2, squared = rhs = TexMobject( + "=", "2 \\pi R", "(0.1)", "+", "\\pi", "(0.1)", "^2" + ) + VGroup(dR, dR2).highlight(self.dR_color) + change.next_to(self.circle, buff = LARGE_BUFF) + rhs.next_to(change) + + circum_brace = Brace(two_pi_R, UP) + circum_text = circum_brace.get_text("Circumference") + error_brace = Brace(VGroup(pi, squared), UP) + error_text = error_brace.get_text("Error") + error_text.highlight(RED) + + self.play( + Write(change, run_time = 1), + self.pi_creature.change_mode, "pondering", + ) + self.dither() + self.play(*it.chain( + map(Write, [equals, two_pi_R, dR]), + map(FadeIn, [circum_text, circum_brace]) + )) + self.dither() + self.play(*it.chain( + map(Write, [plus, pi, dR2, squared]), + map(FadeIn, [error_brace, error_text]) + )) + self.dither(2) + self.change = change + self.circum_term = VGroup(two_pi_R, dR) + self.circum_term.label = VGroup(circum_brace, circum_text) + self.error_term = VGroup(pi, dR2, squared) + self.error_term.label = VGroup(error_brace, error_text) + self.equals = equals + self.plus = plus + + def show_fractions(self): + terms = [self.change, self.circum_term, self.error_term] + for term in terms: + term.frac_line = TexMobject("\\frac{\\quad}{\\quad}") + term.frac_line.stretch_to_fit_width(term.get_width()) + term.frac_line.next_to(term, DOWN, buff = SMALL_BUFF) + term.denom = TexMobject("(0.1)") + term.denom.next_to(term.frac_line, DOWN, buff = SMALL_BUFF) + term.denom.highlight(self.dR_color) + term.denom.save_state() + term.denom.replace(self.nudge_label) + + self.equals.generate_target() + self.equals.target.next_to(self.change.frac_line, RIGHT) + self.plus.generate_target() + self.plus.target.next_to(self.circum_term.frac_line, RIGHT) + + self.play(*it.chain( + [Write(term.frac_line) for term in terms], + map(MoveToTarget, [self.equals, self.plus]) + )) + self.play(*[term.denom.restore for term in terms]) + self.dither(2) + canceleres = VGroup(self.circum_term[1], self.circum_term.denom) + self.play(canceleres.highlight, RED) + self.play(FadeOut(canceleres)) + self.remove(self.circum_term) + self.play( + self.circum_term[0].move_to, self.circum_term.frac_line, LEFT, + self.circum_term[0].shift, 0.1*UP, + FadeOut(self.circum_term.frac_line), + MaintainPositionRelativeTo( + self.circum_term.label, + self.circum_term[0] + ) + ) + self.circum_term = self.circum_term[0] + self.dither(2) + self.play( + FadeOut(self.error_term[-1]), + FadeOut(self.error_term.denom) + ) + self.error_term.remove(self.error_term[-1]) + self.play( + self.error_term.move_to, self.error_term.frac_line, + self.error_term.shift, 0.3*LEFT + 0.15*UP, + FadeOut(self.error_term.frac_line), + self.plus.shift, 0.7*LEFT + 0.1*UP, + MaintainPositionRelativeTo( + self.error_term.label, + self.error_term + ) + ) + self.dither() + + def transition_to_dR(self): + dRs = VGroup( + self.nudge_label, + self.change.denom, + self.error_term[1], + ) + error_brace, error_text = self.error_term.label + for s, width in ("(0.01)", 0.05), ("(0.001)", 0.03), ("dR", 0.03): + new_dRs = VGroup(*[ + TexMobject(s).move_to(mob, LEFT) + for mob in dRs + ]) + new_dRs.highlight(self.dR_color) + new_outer_ring = self.get_ring(self.radius, width) + new_nudge_line = self.nudge_line.copy() + new_nudge_line.scale_to_fit_width(width) + new_nudge_line.move_to(self.nudge_line, LEFT) + error_brace.target = error_brace.copy() + error_brace.target.stretch_to_fit_width( + VGroup(self.error_term[0], new_dRs[-1]).get_width() + ) + error_brace.target.move_to(error_brace, LEFT) + self.play( + MoveToTarget(error_brace), + Transform(self.outer_ring, new_outer_ring), + Transform(self.nudge_line, new_nudge_line), + *[ + Transform(*pair) + for pair in zip(dRs, new_dRs) + ] + ) + self.dither() + if s == "(0.001)": + self.plus.generate_target() + self.plus.target.next_to(self.circum_term) + self.error_term.generate_target() + self.error_term.target.next_to(self.plus.target) + error_brace.target = Brace(self.error_term.target) + error_text.target = error_brace.target.get_text("Truly tiny") + error_text.target.highlight(error_text.get_color()) + self.play(*map(MoveToTarget, [ + error_brace, error_text, self.plus, self.error_term + ])) + self.dither() + + difference_text = TextMobject( + "``Tiny " , "d", "ifference in ", "$R$", "''", + arg_separator = "" + + ) + difference_text.highlight_by_tex("d", self.dR_color) + difference_text.next_to(self.pi_creature, UP+RIGHT) + difference_arrow = Arrow(difference_text, self.change.denom) + self.play( + Write(difference_text, run_time = 2), + ShowCreation(difference_arrow), + self.pi_creature.change_mode, "speaking" + ) + self.dither() + + dA = TexMobject("dA") + dA.highlight(self.change.get_color()) + frac_line = self.change.frac_line + frac_line.generate_target() + frac_line.target.stretch_to_fit_width(dA.get_width()) + frac_line.target.next_to(self.equals, LEFT) + dA.next_to(frac_line.target, UP, 2*SMALL_BUFF) + self.change.denom.generate_target() + self.change.denom.target.next_to(frac_line.target, DOWN, 2*SMALL_BUFF) + A = TexMobject("A").replace(difference_text[3]) + difference_arrow.target = Arrow(difference_text, dA.get_left()) + self.play( + Transform(self.change, dA), + MoveToTarget(frac_line), + MoveToTarget(self.change.denom), + Transform(difference_text[3], A), + difference_text[1].highlight, dA.get_color(), + MoveToTarget(difference_arrow), + ) + self.dither(2) + self.play(*map(FadeOut, [difference_text, difference_arrow])) + + def elaborate_on_d(self): + arc = Arc(-np.pi, start_angle = -np.pi/2) + arc.scale_to_fit_height( + self.change.get_center()[1]-self.change.denom.get_center()[1] + ) + arc.next_to(self.change.frac_line, LEFT) + arc.add_tip() + + self.play( + ShowCreation(arc), + self.pi_creature.change_mode, "sassy" + ) + self.dither() + self.play(self.pi_creature.shrug) + self.play(FadeOut(arc)) + self.dither() + + d = TextMobject("``$d$''") + arrow = TexMobject("\\Rightarrow") + arrow.next_to(d) + ignore_error = TextMobject("Ignore error") + d_group = VGroup(d, arrow, ignore_error) + d_group.arrange_submobjects() + d_group.next_to( + self.pi_creature.get_corner(UP+RIGHT), + buff = LARGE_BUFF + ) + error_group = VGroup( + self.error_term, self.error_term.label + ) + + self.play( + Write(d), + self.pi_creature.change_mode, "speaking" + ) + self.play(*map(Write, [arrow, ignore_error])) + self.play(error_group.fade, 0.8) + self.dither(2) + + less_wrong_philosophy = TextMobject("``Less wrong'' philosophy") + less_wrong_philosophy.move_to(ignore_error, LEFT) + self.play(Transform(ignore_error, less_wrong_philosophy)) + self.dither() + + big_dR = 0.3 + big_outer_ring = self.get_ring(self.radius, big_dR) + big_nudge_line = self.nudge_line.copy() + big_nudge_line.stretch_to_fit_width(big_dR) + big_nudge_line.move_to(self.nudge_line, LEFT) + new_nudge_arrow = Arrow(self.nudge_label, big_nudge_line, buff = SMALL_BUFF) + self.outer_ring.save_state() + self.nudge_line.save_state() + self.nudge_arrow.save_state() + self.play( + Transform(self.outer_ring, big_outer_ring), + Transform(self.nudge_line, big_nudge_line), + Transform(self.nudge_arrow, new_nudge_arrow), + ) + self.play( + *[ + mob.restore + for mob in [ + self.outer_ring, + self.nudge_line, + self.nudge_arrow, + ] + ], + rate_func = None, + run_time = 7 + ) + self.play(self.pi_creature.change_mode, "hooray") + self.less_wrong_philosophy = VGroup( + d, arrow, ignore_error + ) + + def not_infinitely_small(self): + randy = Randolph().flip() + randy.scale(0.7) + randy.to_corner(DOWN+RIGHT) + bubble = SpeechBubble() + bubble.write("$dR$ is infinitely small") + bubble.resize_to_content() + bubble.stretch(0.7, 1) + bubble.pin_to(randy) + bubble.set_fill(BLACK, opacity = 1) + bubble.add_content(bubble.content) + self.play(FadeIn(randy)) + self.play( + randy.change_mode, "speaking", + ShowCreation(bubble), + Write(bubble.content), + self.pi_creature.change_mode, "confused" + ) + self.dither() + + to_infs = [self.change, self.change.denom, self.nudge_label] + for mob in to_infs: + mob.save_state() + mob.inf = TexMobject("1/\\infty") + mob.inf.highlight(mob.get_color()) + mob.inf.move_to(mob) + self.play(*[ + Transform(mob, mob.inf) + for mob in to_infs + ]) + self.dither() + self.play(self.pi_creature.change_mode, "pleading") + self.dither() diff --git a/topics/characters.py b/topics/characters.py index 1fca6458..4d92e9ea 100644 --- a/topics/characters.py +++ b/topics/characters.py @@ -162,6 +162,16 @@ class PiCreature(SVGMobject): pi_creature.look_at(self.eyes) return self + def shrug(self): + self.change_mode("shruggie") + top_mouth_point, bottom_mouth_point = [ + self.mouth.points[np.argmax(self.mouth.points[:,1])], + self.mouth.points[np.argmin(self.mouth.points[:,1])] + ] + self.look(top_mouth_point - bottom_mouth_point) + return self + + class Randolph(PiCreature): pass #Nothing more than an alternative name @@ -209,27 +219,54 @@ class Blink(ApplyMethod): -class RandolphScene(Scene): +class PiCreatureScene(Scene): CONFIG = { - "randy_kwargs" : {}, - "randy_corner" : DOWN+LEFT + "total_dither_time" : 0 } def setup(self): - self.randy = Randolph(**self.randy_kwargs) - self.randy.to_corner(self.randy_corner) - if self.randy_corner[0] > 0: - self.randy.flip() - self.add(self.randy) + self.pi_creature = self.get_pi_creature() + self.add(self.pi_creature) + + def get_pi_creature(self): + return Randolph().to_corner() + + def play(self, *args, **kwargs): + if self.pi_creature not in self.get_mobjects(): + Scene.play(self, *args, **kwargs) + return + + if inspect.ismethod(args[0]): + mobject_of_interest = args[0].im_self + elif isinstance(args[0], Transform): + if args[0].mobject is self.pi_creature: + mobject_of_interest = self.pi_creature + else: + mobject_of_interest = args[0].ending_mobject + elif isinstance(args[0], Animation): + mobject_of_interest = args[0].mobject + else: + raise Exception("Invalid play args") + + if mobject_of_interest is self.pi_creature: + new_anims = [] + else: + new_anims = [self.pi_creature.look_at, mobject_of_interest] + Scene.play(self, *list(args) + new_anims, **kwargs) def dither(self, time = 1, blink = True): while time > 0: - if blink and time%2 == 1: - self.play(Blink(self.randy)) + if blink and self.total_dither_time%2 == 1: + self.play(Blink(self.pi_creature)) else: Scene.dither(self, time) time -= 1 + self.total_dither_time += 1 return self + def change_mode(self, mode): + self.play(self.pi_creature.change_mode, mode) + + class TeacherStudentsScene(Scene): def setup(self): self.teacher = Mortimer()