diff --git a/animation/animation.py b/animation/animation.py index 5e502cdf..10eae528 100644 --- a/animation/animation.py +++ b/animation/animation.py @@ -21,6 +21,7 @@ class Animation(object): #Options are lagged_start, smoothed_lagged_start, #one_at_a_time, all_at_once "submobject_mode" : "lagged_start", + "lag_factor" : 2, } def __init__(self, mobject, **kwargs): mobject = instantiate(mobject) @@ -77,7 +78,8 @@ class Animation(object): prop = float(index)/num_submobjects if self.submobject_mode is "smoothed_lagged_start": prop = smooth(prop) - return np.clip(2*alpha - prop, 0, 1) + lf = self.lag_factor + return np.clip(lf*alpha - (lf-1)*prop, 0, 1) elif self.submobject_mode == "one_at_a_time": lower = float(index)/num_submobjects upper = float(index+1)/num_submobjects diff --git a/animation/simple_animations.py b/animation/simple_animations.py index 0bdb0894..0740d368 100644 --- a/animation/simple_animations.py +++ b/animation/simple_animations.py @@ -48,6 +48,12 @@ class ShowCreation(ShowPartial): def get_bounds(self, alpha): return (0, alpha) +class Uncreate(ShowCreation): + CONFIG = { + "rate_func" : lambda t : smooth(1-t), + "remover" : True + } + class Write(ShowCreation): CONFIG = { "rate_func" : None, diff --git a/eola/chapter5.py b/eola/chapter5.py index f060c8a8..6b4a8a18 100644 --- a/eola/chapter5.py +++ b/eola/chapter5.py @@ -85,7 +85,7 @@ class OpeningQuote(Scene): ", not ", "numbers.", "''", - ], separate_list_arg_with_spaces = False) + ], arg_separator = "") # words.scale_to_fit_width(2*SPACE_WIDTH - 2) words.to_edge(UP) words.split()[1].highlight(BLUE) diff --git a/eola/chapter7.py b/eola/chapter7.py index 9550e399..a9666274 100644 --- a/eola/chapter7.py +++ b/eola/chapter7.py @@ -51,7 +51,9 @@ class OpeningQuote(Scene): "\\\\Calvin:" , "Yeah. All these equations are like miracles." "You take two numbers and when you add them, " - "they magically become one NEW number!" + "they magically become one NEW number! " + "No one can say how it happens. " + "You either believe it or you don't.", ) words.scale_to_fit_width(2*SPACE_WIDTH - 1) words.to_edge(UP) @@ -60,8 +62,14 @@ class OpeningQuote(Scene): words[4].highlight(YELLOW) for i in range(3): - self.play(Write(VMobject(*words[2*i:2*i+1]))) - # self.play(FadeIn(words)) + speaker, quote = words[2*i:2*i+2] + self.play(FadeIn(speaker, run_time = 0.5)) + rt = max(1, len(quote.split())/18) + self.play(Write( + quote, + run_time = rt, + lag_factor = 5 if rt > 3 else 2, + )) self.dither(2) class TraditionalOrdering(RandolphScene): @@ -94,6 +102,7 @@ class TraditionalOrdering(RandolphScene): class ThisSeriesOrdering(RandolphScene): def construct(self): title = TextMobject("Essence of linear algebra") + self.randy.rotate(np.pi, UP) title.scale(1.2).highlight(BLUE) title.to_corner(UP+LEFT) line = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT, color = WHITE) @@ -124,7 +133,9 @@ class ThisSeriesOrdering(RandolphScene): chapters.to_edge(RIGHT) self.add(title) - self.play(ShowCreation(line)) + self.play( + ShowCreation(line), + ) self.play( FadeIn( chapters, @@ -133,48 +144,53 @@ class ThisSeriesOrdering(RandolphScene): ), self.randy.change_mode, "sassy" ) + self.play(self.randy.look, UP+LEFT) self.play(chapters[6].highlight, PINK) - self.dither(2) - self.dither(2) + self.dither(6) class OneMustViewThroughTransformations(TeacherStudentsScene): def construct(self): - self.teacher_says(""" - Only with transformations - can we truly understand - """) + words = TextMobject( + "Only with" , "transformations", + "\n can we truly understand", + ) + words.highlight_by_tex("transformations", BLUE) + self.teacher_says(words) self.change_student_modes( "pondering", "plain", "raise_right_hand" ) self.random_blink(2) - self.teacher_says(""" - First, the - standard view... - """) + words = TextMobject( + "First, the ", + "standard view...", + arg_separator = "\n" + ) + self.teacher_says(words) self.random_blink(2) class ShowNumericalDotProduct(Scene): CONFIG = { "v1" : [2, 7, 1], "v2" : [8, 2, 8], + "write_dot_product_words" : True, } def construct(self): v1 = Matrix(self.v1) v2 = Matrix(self.v2) inter_array_dot = TexMobject("\\cdot").scale(1.5) - dot_product = VMobject(v1, inter_array_dot, v2) - dot_product.arrange_submobjects(RIGHT) + dot_product = Group(v1, inter_array_dot, v2) + dot_product.arrange_submobjects(RIGHT, buff = MED_BUFF/2) dot_product.to_edge(LEFT) pairs = zip(v1.get_entries(), v2.get_entries()) for pair, color in zip(pairs, [X_COLOR, Y_COLOR, Z_COLOR, PINK]): - VMobject(*pair).highlight(color) + Group(*pair).highlight(color) dot = TexMobject("\\cdot") - products = VMobject(*[ - VMobject( + products = Group(*[ + Group( p1.copy(), dot.copy(), p2.copy() ).arrange_submobjects(RIGHT, buff = SMALL_BUFF) for p1, p2 in pairs @@ -185,24 +201,38 @@ class ShowNumericalDotProduct(Scene): products.target = products.copy() plusses = ["+"]*(len(self.v1)-1) - symbols = VMobject(*map(TexMobject, ["="] + plusses)) - final_sum = VMobject(*it.chain(*zip( + symbols = Group(*map(TexMobject, ["="] + plusses)) + final_sum = Group(*it.chain(*zip( symbols, products.target ))) final_sum.arrange_submobjects(RIGHT, buff = SMALL_BUFF) final_sum.next_to(dot_product, RIGHT) + title = TextMobject("Two vectors of the same dimension") + title.to_edge(UP) + + arrow = Arrow(DOWN, UP).next_to(inter_array_dot, DOWN) + dot_product_words = TextMobject("Dot product") + dot_product_words.highlight(YELLOW) + dot_product_words.next_to(arrow, DOWN) + dot_product_words.shift_onto_screen() self.play( Write(v1), Write(v2), - FadeIn(inter_array_dot) + FadeIn(inter_array_dot), + FadeIn(title) ) self.dither() - - self.dither() + if self.write_dot_product_words: + self.play( + inter_array_dot.highlight, YELLOW, + ShowCreation(arrow), + Write(dot_product_words, run_time = 2) + ) + self.dither() self.play(Transform( - VMobject(*it.starmap(VMobject, pairs)).copy(), + Group(*it.starmap(Group, pairs)).copy(), products, path_arc = -np.pi/2, run_time = 2 @@ -227,6 +257,7 @@ class FourDDotProductExample(ShowNumericalDotProduct): CONFIG = { "v1" : [6, 2, 8, 3], "v2" : [1, 8, 5, 3], + "write_dot_product_words" : False, } class GeometricInterpretation(VectorScene): @@ -323,7 +354,7 @@ class GeometricInterpretation(VectorScene): "(", "Length of $\\vec{\\textbf{%s}}$"%stable_char, ")", - separate_list_arg_with_spaces = False + arg_separator = "" ) product.scale(0.9) product.next_to(self.dot_product, RIGHT) @@ -524,7 +555,7 @@ class SymmetricVAndW(VectorScene): line_of_symmetry.rotate(np.mean([v.get_angle(), w.get_angle()])) line_of_symmetry_words = TextMobject("Line of symmetry") line_of_symmetry_words.add_background_rectangle() - line_of_symmetry_words.next_to(ORIGIN, UP+RIGHT) + line_of_symmetry_words.next_to(ORIGIN, UP+LEFT) line_of_symmetry_words.rotate(line_of_symmetry.get_angle()) for vect in v, w: @@ -539,11 +570,14 @@ class SymmetricVAndW(VectorScene): run_time = 2 ) self.dither() - self.play(ShowCreation(line_of_symmetry)) - self.play(Write(line_of_symmetry_words)) - self.dither() - self.play(Transform(line_of_symmetry_words, line_of_symmetry)) - for vect in v, w: + self.play( + ShowCreation(line_of_symmetry), + Write(line_of_symmetry_words, run_time = 1) + ) + self.dither(0.5) + self.remove(line_of_symmetry_words) + self.play(*map(Uncreate, line_of_symmetry_words)) + for vect in w, v: self.play(ShowCreation(vect.proj_line)) vect_copy = vect.copy() self.play(Transform(vect_copy, vect.proj)) @@ -556,9 +590,8 @@ class SymmetricVAndW(VectorScene): def show_doubling(self, v, w): scalar = 2 new_v = v.copy().scale(scalar) - new_v.label = VMobject(TexMobject(str(scalar)), v.label.copy()) - new_v.label.arrange_submobjects() - new_v.label.highlight(new_v.get_color()) + new_v.label = VMobject(TexMobject("c"), v.label.copy()) + new_v.label.arrange_submobjects(aligned_edge = DOWN) new_v.label.next_to(new_v.get_end(), DOWN+RIGHT) new_v.proj = v.proj.copy().scale(scalar) new_v.proj.fade() @@ -569,9 +602,9 @@ class SymmetricVAndW(VectorScene): v_tex, w_tex = ["\\vec{\\textbf{%s}}"%c for c in "v", "w"] equation = TexMobject( - "(", "2", v_tex, ")", "\\cdot", w_tex, + "(", "c", v_tex, ")", "\\cdot", w_tex, "=", - "2(", v_tex, "\\cdot", w_tex, ")" + "c(", v_tex, "\\cdot", w_tex, ")" ) equation.highlight_by_tex(v_tex, V_COLOR) equation.highlight_by_tex(w_tex, W_COLOR) @@ -584,23 +617,37 @@ class SymmetricVAndW(VectorScene): v.save_state() v.proj.save_state() self.play(Transform(*[ - VMobject(mob, mob.proj, mob.proj_line, mob.label) + Group(mob, mob.proj, mob.proj_line, mob.label) for mob in v, new_v ]), run_time = 2) + last_mob = self.get_mobjects_from_last_animation()[0] + self.remove(last_mob) + self.add(*last_mob) self.play(Write(words)) self.dither() two_v_parts = equation[1:3] equation.remove(*two_v_parts) - self.play( - Write(equation), - Transform(new_v.label.copy(), VMobject(*two_v_parts)) - ) - self.dither() - for vect in v, v.proj: + for v_part, projector, stable in zip([v, v.proj], [w, v], [v, w]): + self.play(*map(FadeOut, [ + stable.proj, stable.proj_line + ])) self.play( - vect.restore, + Transform(projector.copy(), projector.proj), + ShowCreation(projector.proj_line) + ) + self.remove(self.get_mobjects_from_last_animation()[0]) + self.add(projector.proj) + self.dither() + if equation not in self.get_mobjects(): + self.play( + Write(equation), + Transform(new_v.label.copy(), VMobject(*two_v_parts)) + ) + self.dither() + self.play( + v_part.restore, rate_func = there_and_back, run_time = 2 ) @@ -1759,58 +1806,246 @@ class AbstractNumericAssociation(AssociationBetweenMatricesAndVectors): } class TwoDOneDTransformationSeparateSpace(Scene): + CONFIG = { + "v_coords" : [4, 1] + } def construct(self): - x_rad = SPACE_WIDTH/2-0.5 - plane = NumberPlane(x_radius = x_rad) - squish_plane = plane.copy().apply_function( - lambda p : sum(p)*RIGHT - ) - plane.to_edge(LEFT) - squish_plane.scale_to_fit_width(2*x_rad) - squish_plane.to_edge(RIGHT) + width = SPACE_WIDTH-1 + plane = NumberPlane(x_radius = 6, y_radius = 7) + squish_plane = plane.copy() + i_hat = Vector([1, 0], color = X_COLOR) + j_hat = Vector([0, 1], color = Y_COLOR) + vect = Vector(self.v_coords, color = YELLOW) + plane.add(vect, i_hat, j_hat) + plane.scale_to_fit_width(SPACE_WIDTH) + plane.to_edge(LEFT, buff = 0) + plane.remove(vect, i_hat, j_hat) - number_line = NumberLine().stretch_to_fit_width(2*x_rad) + squish_plane.apply_function( + lambda p : np.dot(p, [4, 1, 0])*RIGHT + ) + squish_plane.add(Vector(self.v_coords[0]*RIGHT, color = X_COLOR)) + squish_plane.add(Vector(self.v_coords[1]*RIGHT, color = Y_COLOR)) + squish_plane.scale(width/(2*SPACE_WIDTH)) + plane.add(i_hat, j_hat) + + number_line = NumberLine().stretch_to_fit_width(width) number_line.to_edge(RIGHT) + squish_plane.move_to(number_line) + numbers = number_line.get_numbers(*range(-6, 8, 2)) v_line = Line(UP, DOWN).scale(SPACE_HEIGHT) + v_line.highlight(GREY) + v_line.set_stroke(width = 10) - words = TextMobject(""" - Any time you have a - 2d-to-1d linear transform... - """) - words.add_background_rectangle() - words.to_edge(UP) + matrix = Matrix([self.v_coords]) + matrix.highlight_columns(X_COLOR, Y_COLOR) + matrix.next_to(number_line, UP, buff = LARGE_BUFF) + v_coords = Matrix(self.v_coords) + v_coords.highlight_columns(YELLOW) + v_coords.scale(0.75) + v_coords.next_to(vect.get_end(), RIGHT) + for array in matrix, v_coords: + array.add_to_back(BackgroundRectangle(array)) + + start_words = TextMobject( + "\\centering Any time you have a \\\\", + "2d-to-1d linear transform..." + ) + end_words = TextMobject( + "\\centering ...it's associated \\\\", + "with some vector", + ) + for words in start_words, end_words: + words.add_background_rectangle() + words.scale(0.8) + start_words.next_to(ORIGIN, RIGHT, buff = MED_BUFF).to_edge(UP) + end_words.next_to(ORIGIN, DOWN+LEFT, buff = MED_BUFF/2) - self.play(Write(words, run_time = 1)) self.play(*map(ShowCreation, [ plane, number_line, v_line - ])+[Animation(words)]) - self.play(Write(numbers, run_time = 1)) - self.play( - Transform(plane, squish_plane), - Animation(words), - path_arc = -np.pi/4, - run_time = 3 - ) + ])+[ + Write(numbers, run_time = 2) + ]) + self.play(Write(start_words, run_time = 2)) + self.play(Write(matrix, run_time = 1)) + mover = plane.copy() + interim = plane.copy().scale(0.8).move_to(number_line) + for target in interim, squish_plane: + self.play( + Transform(mover, target), + Animation(plane), + Animation(start_words), + run_time = 1, + ) + self.dither() + self.play(Transform(start_words.copy(), end_words)) + self.play(ShowCreation(vect)) + self.play(Transform(matrix.copy(), v_coords)) self.dither() class IsntThisBeautiful(TeacherStudentsScene): def construct(self): self.teacher.look(DOWN+LEFT) self.teacher_says( - "Isn't this beautiful", + "Isn't this", "beautiful", pi_creature_target_mode = "surprised" ) for student in self.get_students(): self.play(student.change_mode, "happy") self.random_blink() - - - - - - - + duality_words = TextMobject( + "It's called", "duality" + ) + duality_words[1].gradient_highlight(BLUE, YELLOW) + self.teacher_says(duality_words) + self.random_blink() + +class DualOfAVector(ScaleUpUHat): + pass #Exact copy + +class DualOfATransform(TwoDOneDTransformationSeparateSpace): + pass #Exact copy + +class UnderstandingProjection(ProjectOntoUnitVectorNumberline): + pass ##Copy + +class ShowQualitativeDotProductValuesCopy(ShowQualitativeDotProductValues): + pass + +class TranslateToTheWorldOfTransformations(Scene): + def construct(self): + v1, v2 = [ + Matrix(["x_%d"%n, "y_%d"%n]) + for n in 1, 2 + ] + v1.highlight_columns(V_COLOR) + v2.highlight_columns(W_COLOR) + dot = TexMobject("\\cdot") + + matrix = Matrix([["x_1", "y_1"]]) + matrix.highlight_columns(X_COLOR, Y_COLOR) + + dot_product = Group(v1, dot, v2) + dot_product.arrange_submobjects(RIGHT) + matrix.next_to(v2, LEFT) + + brace = Brace(matrix, UP) + word = TextMobject("Transform") + word.scale_to_fit_width(brace.get_width()) + brace.put_at_tip(word) + word.highlight(BLUE) + + self.play(Write(dot_product)) + self.dither() + self.play( + dot.highlight, BLACK, + Transform(v1, matrix), + ) + self.play( + GrowFromCenter(brace), + Write(word) + ) + self.dither() + +class NumericalAssociationSilliness(GeneralTwoDOneDMatrixMultiplication): + pass #copy + +class YouMustKnowPersonality(TeacherStudentsScene): + def construct(self): + self.teacher_says(""" + You should learn a + vector's personality + """) + self.random_blink() + self.change_student_modes("pondering") + self.random_blink() + self.change_student_modes("pondering", "plain", "pondering") + self.random_blink() + +class WhatTheVectorWantsToBe(Scene): + CONFIG = { + "v_coords" : [2, 4] + } + def construct(self): + width = SPACE_WIDTH-1 + plane = NumberPlane(x_radius = 6, y_radius = 7) + squish_plane = plane.copy() + i_hat = Vector([1, 0], color = X_COLOR) + j_hat = Vector([0, 1], color = Y_COLOR) + vect = Vector(self.v_coords, color = YELLOW) + plane.add(vect, i_hat, j_hat) + plane.scale_to_fit_width(SPACE_WIDTH) + plane.to_edge(LEFT, buff = 0) + plane.remove(vect, i_hat, j_hat) + + squish_plane.apply_function( + lambda p : np.dot(p, [4, 1, 0])*RIGHT + ) + squish_plane.add(Vector(self.v_coords[1]*RIGHT, color = Y_COLOR)) + squish_plane.add(Vector(self.v_coords[0]*RIGHT, color = X_COLOR)) + squish_plane.scale(width/(2*SPACE_WIDTH)) + plane.add(j_hat, i_hat) + + number_line = NumberLine().stretch_to_fit_width(width) + number_line.to_edge(RIGHT) + squish_plane.move_to(number_line) + + numbers = number_line.get_numbers(*range(-6, 8, 2)) + v_line = Line(UP, DOWN).scale(SPACE_HEIGHT) + v_line.highlight(GREY) + v_line.set_stroke(width = 10) + + matrix = Matrix([self.v_coords]) + matrix.highlight_columns(X_COLOR, Y_COLOR) + matrix.next_to(number_line, UP, buff = LARGE_BUFF) + v_coords = Matrix(self.v_coords) + v_coords.highlight_columns(YELLOW) + v_coords.scale(0.75) + v_coords.next_to(vect.get_end(), RIGHT) + for array in matrix, v_coords: + array.add_to_back(BackgroundRectangle(array)) + + words = TextMobject( + "What the vector", + "\\\\ wants", + "to be" + ) + words[1].highlight(BLUE) + words.next_to(matrix, UP, buff = MED_BUFF) + + self.add(plane, v_line, number_line, numbers) + self.play(ShowCreation(vect)) + self.play(Write(v_coords)) + self.dither() + self.play( + Transform(v_coords.copy(), matrix), + Write(words) + ) + self.play( + Transform(plane.copy(), squish_plane), + Animation(words), + Animation(matrix), + Animation(plane), + ) + self.dither() + +class NextVideo(Scene): + def construct(self): + title = TextMobject(""" + Next video: Cross products in the + light of linear transformations + """) + title.scale_to_fit_height(1.2) + title.to_edge(UP, buff = MED_BUFF/2) + rect = Rectangle(width = 16, height = 9, color = BLUE) + rect.scale_to_fit_height(6) + rect.next_to(title, DOWN) + Group(title, rect).show() + + self.add(title) + self.play(ShowCreation(rect)) + self.dither() diff --git a/extract_scene.py b/extract_scene.py index 36b6a993..d9230902 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -204,10 +204,13 @@ def main(): scene_kwargs["construct_args"] = tuplify(args) try: handle_scene(SceneClass(**scene_kwargs), **config) + play_finish_sound() except: print "\n\n" traceback.print_exc() print "\n\n" + play_error_sound() + if __name__ == "__main__": diff --git a/helpers.py b/helpers.py index 35b30734..fdf4a128 100644 --- a/helpers.py +++ b/helpers.py @@ -20,18 +20,24 @@ def play_chord(*nums): "play", "-n", "-c1", + "--no-show-progress", "synth", ] + [ "sin %-"+str(num) for num in nums ] + [ - "fade h 0.5 1 0.5" + "fade h 0.5 1 0.5", + "> /dev/null" ] try: os.system(" ".join(commands)) except: pass +def play_error_sound(): + play_chord(12, 11, 8, 6, 1) + + def play_finish_sound(): play_chord(12, 9, 5, 2) diff --git a/mobject/tex_mobject.py b/mobject/tex_mobject.py index 7b257275..895297d9 100644 --- a/mobject/tex_mobject.py +++ b/mobject/tex_mobject.py @@ -33,7 +33,7 @@ class TexMobject(SVGMobject): "fill_opacity" : 1.0, "fill_color" : WHITE, "should_center" : True, - "separate_list_arg_with_spaces" : True, + "arg_separator" : " ", "enforce_new_line_structure" : False, "initial_scale_factor" : TEX_MOB_SCALE_FACTOR, "organize_left_to_right" : False, @@ -68,10 +68,7 @@ class TexMobject(SVGMobject): self.handle_multiple_args() def get_modified_expression(self): - separator = "" - if self.separate_list_arg_with_spaces: - separator = " " - result = separator.join(self.args) + result = self.arg_separator.join(self.args) if self.enforce_new_line_structure: result = result.replace("\n", " \\\\ \n ") return result diff --git a/scene/scene.py b/scene/scene.py index 88a9b953..b4a8ec20 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -351,7 +351,6 @@ class Scene(object): process.stdin.write(frame.tostring()) process.stdin.close() process.wait() - play_finish_sound() # To list possible args that subclasses have # Elements should always be a tuple diff --git a/topics/characters.py b/topics/characters.py index 3abea55a..f1e0a944 100644 --- a/topics/characters.py +++ b/topics/characters.py @@ -304,10 +304,12 @@ class RandolphScene(Scene): self.add(self.randy) def dither(self, time = 1, blink = True): - if blink: - self.play(Blink(self.randy)) + while time > 0: + if blink and time%2 == 1: + self.play(Blink(self.randy)) + else: + Scene.dither(self, time) time -= 1 - Scene.dither(self, time) return self class TeacherStudentsScene(Scene):