def add_polygons(self): a = self.i_hat.get_end()[0]*RIGHT b = self.j_hat.get_end()[0]*RIGHT c = self.i_hat.get_end()[1]*UP d = self.j_hat.get_end()[1]*UP shapes_colors_and_tex = [ (Polygon(ORIGIN, a, a+c), TEAL, "bd/2"), (Polygon(ORIGIN, d+b, d), TEAL, "\\dfrac{bd}{2}"), (Polygon(a+c, a+b+c, a+b+c+d), MAROON, "\\dfrac{ac}{2}"), (Polygon(b+d, a+b+c+d, b+c+d), MAROON, "ac/2"), (Polygon(a, a+b, a+b+c, a+c), PINK, "bc"), (Polygon(d, d+b, d+b+c, d+c), PINK, "bc"), ] everyone = VMobject() for shape, color, tex in shapes_colors_and_tex: shape.set_stroke(width = 0) shape.set_fill(color = color, opacity = 0.7) tex_mob = TexMobject(tex) tex_mob.scale(0.7) tex_mob.move_to(shape.get_center_of_mass()) everyone.add(shape, tex_mob) self.play(FadeIn( everyone, submobject_mode = "lagged_start", run_time = 1 ))
def move_matrix_parentheses(self, morty, matrices): m1, m2, m3 = matrices parens = TexMobject(["(", ")"]) parens.scale_to_fit_height(1.2*m1.get_height()) lp, rp = parens.split() state1 = VMobject( VectorizedPoint(m1.get_left()), m1, m2, VectorizedPoint(m2.get_right()), m3 ) state2 = VMobject(*[ m.copy() for m in lp, m1, m2, rp, m3 ]) state3 = VMobject(*[ m.copy() for m in m1, lp, m2, m3, rp ]) for state in state2, state3: state.arrange_submobjects(RIGHT, buff = 0.1) m1, lp, m2, m3, rp = state3.split() state3 = VMobject(lp, m1, m2, rp, m3) self.play(morty.change_mode, "angry") for state in state2, state3: self.play(Transform(state1, state)) self.dither() self.play(morty.change_mode, "confused") self.dither()
def show_dependencies(self): linalg = TextMobject("Linear Algebra") subjects = map(TextMobject, [ "Computer science", "Physics", "Electrical engineering", "Mechanical engineering", "Statistics", "\\vdots" ]) prev = subjects[0] for subject in subjects[1:]: subject.next_to(prev, DOWN, aligned_edge = LEFT) prev = subject all_subs = VMobject(*subjects) linalg.to_edge(LEFT) all_subs.next_to(linalg, RIGHT, buff = 2) arrows = VMobject(*[ Arrow(linalg, sub) for sub in subjects ]) self.play(Write(linalg, run_time = 1)) self.dither() self.play( ShowCreation(arrows, submobject_mode = "lagged_start"), FadeIn(all_subs), run_time = 2 ) self.dither() self.linalg = linalg
def construct(self): t_axis = NumberLine( numbers_with_elongated_ticks = [], x_min = 0, x_max = 10, color = WHITE, ) det_axis = NumberLine( numbers_with_elongated_ticks = [], x_min = -2, x_max = 2, color = WHITE ) det_axis.rotate(np.pi/2) t_axis.next_to(ORIGIN, RIGHT, buff = 0) det_axis.move_to(t_axis.get_left()) axes = VMobject(det_axis, t_axis) graph = FunctionGraph(np.cos, x_min = 0, x_max = np.pi) graph.next_to(det_axis, RIGHT, buff = 0) graph.highlight(YELLOW) det_word = TextMobject("Det") det_word.next_to(det_axis, RIGHT, aligned_edge = UP) time_word = TextMobject("time") time_word.next_to(t_axis, UP) time_word.to_edge(RIGHT) everything = VMobject(axes, det_word, time_word, graph) everything.scale(1.5) self.add(axes, det_word, time_word) self.play(ShowCreation( graph, rate_func = None, run_time = 10 ))
def construct(self): matrix = Matrix([ [2, 0], [-1, 1], [-2, 1], ]) matrix.highlight_columns(X_COLOR, Y_COLOR) brace = Brace(matrix) words = VMobject( TextMobject("Span", "of columns"), TexMobject("\\Updownarrow"), TextMobject("``Column space''") ) words.arrange_submobjects(DOWN, buff = 0.1) words.next_to(brace, DOWN) words[0][0].highlight(PINK) words[2].highlight(TEAL) words[0].add_background_rectangle() words[2].add_background_rectangle() VMobject(matrix, brace, words).center() self.add(matrix) self.play( GrowFromCenter(brace), Write(words, run_time = 2) ) self.dither()
def show_overall_effect(self, matrix): everything = self.get_mobjects() everything = list_difference_update( everything, matrix.submobject_family() ) self.play(*map(FadeOut, everything) + [Animation(matrix)]) new_matrix = matrix.copy() new_matrix.center().to_edge(UP) self.play(Transform(matrix, new_matrix)) self.dither() self.remove(matrix) self.setup() everything = self.get_mobjects() self.play(*map(FadeIn, everything) + [Animation(matrix)]) func = self.get_matrix_transformation([[1, 1], [-1, 0]]) bases = VMobject(self.i_hat, self.j_hat) new_bases = VMobject(*[ Vector(func(v.get_end()), color = v.get_color()) for v in bases.split() ]) self.play( ApplyPointwiseFunction(func, self.plane), Transform(bases, new_bases), Animation(matrix), run_time = 3 ) self.dither()
def get_matrix_multiplication_question(self): why = TextMobject("Why?").highlight(BLUE) mult = self.get_matrix_multiplication() why.next_to(mult, UP) result = VMobject(why, mult) result.get_center = lambda : mult.get_center() return result
def show_scaled_vectors(self, vect_array, vect_coords, i_label, j_label): x, y = vect_array.get_entries().split() scaled_i_label = VMobject(x.copy(), i_label.copy()) scaled_j_label = VMobject(y.copy(), j_label.copy()) scaled_i = self.i_hat.copy().scale(vect_coords[0]) scaled_j = self.j_hat.copy().scale(vect_coords[1]) for mob in scaled_i, scaled_j: mob.fade(0.3) scaled_i_label_target = scaled_i_label.copy() scaled_i_label_target.arrange_submobjects(buff = 0.1) scaled_i_label_target.next_to(scaled_i.get_center(), DOWN) scaled_j_label_target = scaled_j_label.copy() scaled_j_label_target.arrange_submobjects(buff = 0.1) scaled_j_label_target.next_to(scaled_j.get_center(), LEFT) self.play( Transform(self.i_hat.copy(), scaled_i), Transform(scaled_i_label, scaled_i_label_target) ) scaled_i = self.get_mobjects_from_last_animation()[0] self.play( Transform(self.j_hat.copy(), scaled_j), Transform(scaled_j_label, scaled_j_label_target) ) scaled_j = self.get_mobjects_from_last_animation()[0] self.play(*[ ApplyMethod(mob.shift, scaled_i.get_end()) for mob in scaled_j, scaled_j_label ]) self.dither() self.play(*map(FadeOut, [ scaled_i, scaled_j, scaled_i_label, scaled_j_label, ]))
def record_basis_coordinates(self, vect_array, vect): i_label = vector_coordinate_label(self.i_hat) i_label.highlight(X_COLOR) j_label = vector_coordinate_label(self.j_hat) j_label.highlight(Y_COLOR) for mob in i_label, j_label: mob.scale_in_place(0.8) background = BackgroundRectangle(mob) self.play(ShowCreation(background), Write(mob)) self.dither() x, y = vect_array.get_entries().split() pre_formula = VMobject( x, i_label, TexMobject("+"), y, j_label ) post_formula = pre_formula.copy() pre_formula.split()[2].fade(1) post_formula.arrange_submobjects(buff = 0.1) post_formula.next_to(vect, DOWN) background = BackgroundRectangle(post_formula) everything = self.get_mobjects() everything.remove(vect) self.play(*[ ApplyMethod(m.fade) for m in everything ] + [ ShowCreation(background, run_time = 2, rate_func = squish_rate_func(smooth, 0.5, 1)), Transform(pre_formula.copy(), post_formula, run_time = 2), ApplyMethod(vect.set_stroke, width = 7) ]) self.dither()
def describe_scalars(self, v, plane): axes = plane.get_axes() long_v = Vector(2*v.get_end()) long_minus_v = Vector(-2*v.get_end()) original_v = v.copy() scaling_word = TextMobject("``Scaling''").to_corner(UP+LEFT) scaling_word.shift(2*RIGHT) scalars = VMobject(*map(TexMobject, [ "2,", "\\dfrac{1}{3},", "-1.8,", "\\dots" ])) scalars.arrange_submobjects(RIGHT, buff = 0.4) scalars.next_to(scaling_word, DOWN, aligned_edge = LEFT) scalars_word = TextMobject("``Scalars''") scalars_word.next_to(scalars, DOWN, aligned_edge = LEFT) self.remove(plane) self.add(axes) self.play( Write(scaling_word), Transform(v, long_v), run_time = 1.5 ) self.play(Transform(v, long_minus_v, run_time = 3)) self.play(Write(scalars)) self.dither() self.play(Write(scalars_word)) self.play(Transform(v, original_v), run_time = 3) self.dither(2)
def construct(self): physy = Physicist() mathy = Mathematician(mode = "pondering") compy = ComputerScientist() creatures = [physy, compy, mathy] physy.title = TextMobject("Physics student").to_corner(DOWN+LEFT) compy.title = TextMobject("CS student").to_corner(DOWN+RIGHT) mathy.title = TextMobject("Mathematician").to_edge(DOWN) names = VMobject(physy.title, mathy.title, compy.title) names.arrange_submobjects(RIGHT, buff = 1) names.to_corner(DOWN+LEFT) for pi in creatures: pi.next_to(pi.title, UP) vector, symbol, coordinates = self.intro_vector() for pi in creatures: self.play( Write(pi.title), FadeIn(pi), run_time = 1 ) self.dither(2) self.remove(symbol, coordinates) self.physics_conception(creatures, vector) self.cs_conception(creatures) self.handle_mathy(creatures)
def construct(self): matrix = Matrix([list("abc"), list("def"), list("ghi")]) matrix.highlight_columns(X_COLOR, Y_COLOR, Z_COLOR) m1 = Matrix([["e", "f"], ["h", "i"]]) m1.highlight_columns(Y_COLOR, Z_COLOR) m2 = Matrix([["d", "f"], ["g", "i"]]) m2.highlight_columns(X_COLOR, Z_COLOR) m3 = Matrix([["d", "e"], ["g", "h"]]) m3.highlight_columns(X_COLOR, Y_COLOR) for m in matrix, m1, m2, m3: m.add(get_det_text(m)) a, b, c = matrix.get_entries().split()[:3] parts = it.starmap(VMobject, [ [matrix], [TexMobject("="), a.copy(), m1], [TexMobject("-"), b.copy(), m2], [TexMobject("+"), c.copy(), m3], ]) parts = list(parts) for part in parts: part.arrange_submobjects(RIGHT, buff = 0.2) parts[1].next_to(parts[0], RIGHT) parts[2].next_to(parts[1], DOWN, aligned_edge = LEFT) parts[3].next_to(parts[2], DOWN, aligned_edge = LEFT) everyone = VMobject(*parts) everyone.center().to_edge(UP) for part in parts: self.play(Write(part)) self.dither(2)
def construct(self): self.setup() blob = Blob( height = self.blob_height, random_seed = 5, random_nudge_size = 0.2, ) blob.next_to(ORIGIN, UP+RIGHT) self.add_transformable_mobject(blob) arange = np.arange( 0, self.blob_height + self.square_size, self.square_size ) square = Square(side_length = self.square_size) square.set_stroke(YELLOW, width = 2) square.set_fill(YELLOW, opacity = 0.3) squares = VMobject() for x, y in it.product(*[arange]*2): point = x*RIGHT + y*UP if blob.probably_contains(point): squares.add(square.copy().shift(point)) self.play(ShowCreation( squares, submobject_mode = "lagged_start", run_time = 2, )) self.add_transformable_mobject(squares) self.dither() self.apply_transposed_matrix([[1, -1], [0.5, 1]]) self.dither()
def construct(self): self.setup() self.add_unit_square() matrix = Matrix(np.array(self.t_matrix).transpose()) matrix.next_to(ORIGIN, LEFT) matrix.to_edge(UP) matrix.highlight_columns(X_COLOR, Y_COLOR) det_text = get_det_text( matrix, determinant = np.linalg.det(self.t_matrix) ) three = VMobject(*det_text.split()[-1].split()[1:]) for mob in det_text.split(): if isinstance(mob, TexMobject): mob.add_background_rectangle() matrix_background = BackgroundRectangle(matrix) self.play( ShowCreation(matrix_background), Write(matrix), Write(det_text), ) self.add_foreground_mobject(matrix_background, matrix, det_text) self.dither() self.apply_transposed_matrix(self.t_matrix) self.play(three.copy().move_to, self.square) self.dither()
def construct(self): line = NumberLine() self.add(line, *self.get_mobjects()) offset = LEFT+DOWN vect = 2*RIGHT+UP dots = VMobject(*[ Dot(offset + a*vect, radius = 0.075) for a in np.linspace(-2, 3, 18) ]) dots.submobject_gradient_highlight(YELLOW_B, YELLOW_C) func = self.get_matrix_transformation(self.t_matrix) new_dots = VMobject(*[ Dot( func(dot.get_center()), color = dot.get_color(), radius = dot.radius ) for dot in dots ]) words = TextMobject( "Line of dots remains evenly spaced" ) words.next_to(line, UP, buff = MED_SMALL_BUFF) self.play(Write(dots)) self.apply_transposed_matrix( self.t_matrix, added_anims = [Transform(dots, new_dots)] ) self.play(Write(words)) self.dither()
def add_braces(self): a = self.i_hat.get_end()[0]*RIGHT b = self.j_hat.get_end()[0]*RIGHT c = self.i_hat.get_end()[1]*UP d = self.j_hat.get_end()[1]*UP quads = [ (ORIGIN, a, DOWN, "a"), (a, a+b, DOWN, "b"), (a+b, a+b+c, RIGHT, "c"), (a+b+c, a+b+c+d, RIGHT, "d"), (a+b+c+d, a+c+d, UP, "a"), (a+c+d, d+c, UP, "b"), (d+c, d, LEFT, "c"), (d, ORIGIN, LEFT, "d"), ] everyone = VMobject() for p1, p2, direction, char in quads: line = Line(p1, p2) brace = Brace(line, direction, buff = 0) text = brace.get_text(char) text.add_background_rectangle() if char in ["a", "c"]: text.highlight(X_COLOR) else: text.highlight(Y_COLOR) everyone.add(brace, text) self.play(Write(everyone), run_time = 1)
class Arrow(Line): CONFIG = { "color" : YELLOW_C, "tip_length" : 0.25, "tip_angle" : np.pi/6, "buff" : MED_SMALL_BUFF, "propogate_style_to_family" : False, "preserve_tip_size_when_scaling" : True, } def __init__(self, *args, **kwargs): points = map(self.pointify, args) if len(args) == 1: args = (points[0]+UP+LEFT, points[0]) Line.__init__(self, *args, **kwargs) self.add_tip() def add_tip(self, add_at_end = True): vect = self.tip_length*RIGHT vect = rotate_vector(vect, self.get_angle()+np.pi) start, end = self.get_start_and_end() if not add_at_end: start, end = end, start vect = -vect tip_points = [ end+rotate_vector(vect, u*self.tip_angle) for u in 1, -1 ] self.tip = VMobject( close_new_points = True, mark_paths_closed = True, fill_color = self.color, fill_opacity = 1, stroke_color = self.color, ) self.tip.set_anchor_points( [tip_points[0], end, tip_points[1]], mode = "corners" ) self.set_points_as_corners( [start, center_of_mass(tip_points)] ) self.add(self.tip) self.init_colors() def get_end(self): if hasattr(self, "tip"): return self.tip.get_anchors()[1] else: return Line.get_end(self) def get_tip(self): return self.tip def scale(self, scale_factor, **kwargs): Line.scale(self, scale_factor, **kwargs) if self.preserve_tip_size_when_scaling: self.remove(self.tip) self.add_tip() return self
def construct(self): mob = VMobject( TextMobject("Computer graphics"), TextMobject("Robotics") ) mob.arrange_submobjects(DOWN, buff = 1) self.play(Write(mob, run_time = 1)) self.dither()
def __init__(self, **kwargs): hand = SVGMobject("RightHandOutline") self.inlines = VMobject(*hand.split()[:-4]) self.outline = VMobject(*hand.split()[-4:]) self.outline.set_stroke(color = WHITE, width = 5) self.inlines.set_stroke(color = DARK_GREY, width = 3) VMobject.__init__(self, self.outline, self.inlines) self.center().scale_to_fit_height(3)
def construct(self): arrays = VMobject(*map(matrix_to_mobject, [ [-2, 3], [1, 2], [2, -1], [4, 0] ])) arrays.arrange_submobjects(buff = 0.4) arrays.scale(2) self.play(Write(arrays)) self.dither(2)
def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke( self.secondary_color, self.secondary_stroke_width ) return self
def introduce_coordinate_plane(self): plane = NumberPlane() x_axis, y_axis = plane.get_axes().copy().split() x_label, y_label = plane.get_axis_labels().split() number_line = NumberLine(tick_frequency = 1) x_tick_marks = number_line.get_tick_marks() y_tick_marks = x_tick_marks.copy().rotate(np.pi/2) tick_marks = VMobject(x_tick_marks, y_tick_marks) tick_marks.highlight(WHITE) plane_lines = filter( lambda m : isinstance(m, Line), plane.submobject_family() ) origin_words = TextMobject("Origin") origin_words.shift(2*UP+2*LEFT) dot = Dot(radius = 0.1).highlight(RED) line = Line(origin_words.get_bottom(), dot.get_corner(UP+LEFT)) unit_brace = Brace(Line(RIGHT, 2*RIGHT)) one = TexMobject("1").next_to(unit_brace, DOWN) self.add(x_axis, x_label) self.dither() self.play(ShowCreation(y_axis)) self.play(Write(y_label, run_time = 1)) self.dither(2) self.play( Write(origin_words), GrowFromCenter(dot), ShowCreation(line), run_time = 1 ) self.dither(2) self.play( FadeOut(VMobject(origin_words, dot, line)) ) self.remove(origin_words, dot, line) self.dither() self.play( ShowCreation(tick_marks, submobject_mode = "one_at_a_time") ) self.play( GrowFromCenter(unit_brace), Write(one, run_time = 1) ) self.dither(2) self.remove(unit_brace, one) self.play( *map(GrowFromCenter, plane_lines) + [ Animation(x_axis), Animation(y_axis) ]) self.dither() self.play( FadeOut(plane), Animation(VMobject(x_axis, y_axis, tick_marks)) ) self.remove(plane) self.add(tick_marks)
def construct(self): arrow = Vector(2*UP+RIGHT) vs = TextMobject("vs.") array = Matrix([1, 2]) array.highlight(TEAL) everyone = VMobject(arrow, vs, array) everyone.arrange_submobjects(RIGHT, buff = 0.5) everyone.scale_to_fit_height(4) self.add(everyone)
def add_symbols(self): v = matrix_to_mobject(self.v_coords).highlight(self.v_color) w = matrix_to_mobject(self.w_coords).highlight(self.w_color) v.add_background_rectangle() w.add_background_rectangle() dot = TexMobject("\\cdot") eq = VMobject(v, dot, w) eq.arrange_submobjects(RIGHT, buff = SMALL_BUFF) eq.to_corner(UP+LEFT) self.play(Write(eq), run_time = 1)
def get_cross_product_question(self): cross = TexMobject("\\vec{v} \\times \\vec{w}") left_right_arrow = DoubleArrow(Point(LEFT), Point(RIGHT)) det = TextMobject("Det") q_mark = TextMobject("?") left_right_arrow.next_to(cross) det.next_to(left_right_arrow) q_mark.next_to(left_right_arrow, UP) cross_question = VMobject(cross, left_right_arrow, q_mark, det) cross_question.get_center = lambda : left_right_arrow.get_center() return cross_question
def animate_product(self, left, right, result): l_matrix = left.get_mob_matrix() r_matrix = right.get_mob_matrix() result_matrix = result.get_mob_matrix() circle = Circle( radius = l_matrix[0][0].get_height(), color = GREEN ) circles = VMobject(*[ entry.get_point_mobject() for entry in l_matrix[0][0], r_matrix[0][0] ]) (m, k), n = l_matrix.shape, r_matrix.shape[1] for mob in result_matrix.flatten(): mob.highlight(BLACK) lagging_anims = [] for a in range(m): for b in range(n): for c in range(k): l_matrix[a][c].highlight(YELLOW) r_matrix[c][b].highlight(YELLOW) for c in range(k): start_parts = VMobject( l_matrix[a][c].copy(), r_matrix[c][b].copy() ) result_entry = result_matrix[a][b].split()[c] new_circles = VMobject(*[ circle.copy().shift(part.get_center()) for part in start_parts.split() ]) self.play(Transform(circles, new_circles)) self.play( Transform( start_parts, result_entry.copy().highlight(YELLOW), path_arc = -np.pi/2, submobject_mode = "all_at_once", ), *lagging_anims ) result_entry.highlight(YELLOW) self.remove(start_parts) lagging_anims = [ ApplyMethod(result_entry.highlight, WHITE) ] for c in range(k): l_matrix[a][c].highlight(WHITE) r_matrix[c][b].highlight(WHITE) self.play(FadeOut(circles), *lagging_anims) self.dither()
def init_colors(self): VMobject.init_colors(self) internal_pis = [ pi for pi in self.submobject_family() if isinstance(pi, PiCreature) ] random.seed(self.random_seed) for pi in reversed(internal_pis): color = random.choice(self.colors) pi.highlight(color) pi.set_stroke(color, width = 0)
def construct(self): self.lock_in_faded_grid() vectors = VMobject(*[ Vector([x, y]) for x in np.arange(-int(SPACE_WIDTH)+0.5, int(SPACE_WIDTH)+0.5) for y in np.arange(-int(SPACE_HEIGHT)+0.5, int(SPACE_HEIGHT)+0.5) ]) vectors.submobject_gradient_highlight(PINK, BLUE_E) words = TextMobject("Span") words.scale(3) words.to_edge(UP) words.add_background_rectangle() self.add(vectors, words)
def construct(self): words = map(TextMobject, [ "Just brushing up", "Has yet to take the course", "Supplementing course concurrently", ]) students = VMobject(*[ Randolph(color = c) for c in BLUE_D, BLUE_C, BLUE_E ]) modes = ["pondering", "speaking_looking_left", "sassy"] students.arrange_submobjects(RIGHT) students.scale(0.8) students.center().to_edge(DOWN) last_word, last_arrow = None, None for word, student, mode in zip(words, students.split(), modes): word.shift(2*UP) arrow = Arrow(word, student) if last_word: word_anim = Transform(last_word, word) arrow_anim = Transform(last_arrow, arrow) else: word_anim = Write(word, run_time = 1) arrow_anim = ShowCreation(arrow, submobject_mode = "one_at_a_time") last_word = word last_arrow = arrow self.play( word_anim, arrow_anim, ApplyMethod(student.change_mode, mode) ) self.play(Blink(student)) self.dither() self.dither()
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__(self, angle, **kwargs): digest_locals(self) VMobject.__init__(self, **kwargs)
def fade_all_but(self, creatures, index): self.play(*[ FadeOut(VMobject(pi, pi.title)) for pi in creatures[:index] + creatures[index + 1:] ])
def construct(self): m1_mob, m2_mob, comp_matrix = self.get_matrices() self.add(m1_mob, m2_mob, m1_mob.label, m2_mob.label, comp_matrix) result = self.get_result() col1, col2 = [ VMobject(*m1_mob.split()[1].get_mob_matrix()[:, i]) for i in 0, 1 ] col1.target_color = X_COLOR col2.target_color = Y_COLOR for col in col1, col2: circle = Circle() circle.stretch_to_fit_height(m1_mob.get_height()) circle.stretch_to_fit_width(m1_mob.get_width() / 2.5) circle.highlight(col.target_color) circle.move_to(col) col.circle = circle triplets = [ (col1, "i", X_COLOR), (col2, "j", Y_COLOR), ] for i, (col, char, color) in enumerate(triplets): self.add(col) start_state = self.get_mobjects() question = TextMobject("Where does $\\hat{\\%smath}$ go?" % char) question.split()[-4].highlight(color) question.split()[-5].highlight(color) question.scale(1.2) question.shift(DOWN) first = TextMobject("First here") first.highlight(color) first.shift(DOWN + LEFT) first_arrow = Arrow(first, col.circle.get_bottom(), color=color) second = TextMobject("Then to whatever this is") second.highlight(color) second.to_edge(RIGHT).shift(DOWN) m2_copy = m2_mob.copy() m2_target = m2_mob.copy() m2_target.next_to(m2_mob, DOWN, buff=1) col_vect = Matrix(col.copy().split()) col_vect.highlight(color) col_vect.next_to(m2_target, RIGHT, buff=0.1) second_arrow = Arrow(second, col_vect, color=color) new_m2_copy = m2_mob.copy().split()[1] intermediate = VMobject(TexMobject("="), col_vect.copy().get_entries().split()[0], Matrix(new_m2_copy.get_mob_matrix()[:, 0]), TexMobject("+"), col_vect.copy().get_entries().split()[1], Matrix(new_m2_copy.get_mob_matrix()[:, 1]), TexMobject("=")) intermediate.arrange_submobjects(buff=0.1) intermediate.next_to(col_vect, RIGHT) product = Matrix(result[:, i]) product.next_to(intermediate, RIGHT) comp_col = VMobject(*comp_matrix.split()[1].get_mob_matrix()[:, i]) self.play(Write(question, run_time=1)) self.dither() self.play(Transform(question, first), ShowCreation(first_arrow), ShowCreation(col.circle), ApplyMethod(col.highlight, col.target_color)) self.dither() self.play( Transform(m2_copy, m2_target, run_time=2), ApplyMethod(col.copy().move_to, col_vect, run_time=2), Write(col_vect.get_brackets()), Transform(first_arrow, second_arrow), Transform(question, second), ) self.dither() self.play(*map(FadeOut, [question, first_arrow])) self.play(Write(intermediate)) self.dither() self.play(Write(product)) self.dither() product_entries = product.get_entries() self.play(ApplyMethod(comp_col.highlight, BLACK), ApplyMethod(product_entries.move_to, comp_col)) self.dither() start_state.append(product_entries) self.play(*[ FadeOut(mob) for mob in self.get_mobjects() if mob not in start_state ] + [Animation(product_entries)]) self.dither()
def construct(self): randy = Randolph(color = PINK) randy.look(LEFT) randy.to_corner() matrix = Matrix([ [3, 1], [4, 1], [5, 9], ]) matrix.next_to(randy, RIGHT, buff = LARGE_BUFF, aligned_edge = DOWN) bubble = randy.get_bubble(height = 4) bubble.make_green_screen() VMobject(randy, bubble, matrix).to_corner(UP+LEFT, buff = MED_SMALL_BUFF) self.add(randy) self.play(Write(matrix)) self.play(randy.look, RIGHT, run_time = 0.5) self.play(randy.change_mode, "sassy") self.play(Blink(randy)) self.play( ShowCreation(bubble), randy.change_mode, "pondering" ) # self.play(matrix.highlight_columns, X_COLOR, Y_COLOR) self.wait() for x in range(3): self.play(Blink(randy)) self.wait(2) new_matrix = Matrix([[3, 1, 4], [1, 5, 9]]) new_matrix.move_to(matrix, aligned_edge = UP+LEFT) self.play( Transform(matrix, new_matrix), FadeOut(bubble) ) self.remove(matrix) matrix = new_matrix self.add(matrix) self.play(randy.look, DOWN+RIGHT, run_time = 0.5) self.play(randy.change_mode, "confused") self.wait() self.play(Blink(randy)) self.wait() top_brace = Brace(matrix, UP) top_words = top_brace.get_text("3 basis vectors") top_words.submobject_gradient_highlight(GREEN, RED, BLUE) side_brace = Brace(matrix, RIGHT) side_words = side_brace.get_text(""" 2 coordinates for each landing spots """) side_words.highlight(YELLOW) self.play( GrowFromCenter(top_brace), Write(top_words), matrix.highlight_columns, X_COLOR, Y_COLOR, Z_COLOR ) self.play(randy.change_mode, "happy") self.play( GrowFromCenter(side_brace), Write(side_words, run_time = 2) ) self.play(Blink(randy)) self.wait()
def get_piece_movement(self, pieces): start = VMobject(*pieces) target = VMobject(*[mob.target for mob in pieces]) if self.leave_ghost_vectors: self.add(start.copy().fade(0.7)) return Transform(start, target, submobject_mode="all_at_once")
def __init__(self, mobject, **kwargs): VMobject.__init__(self, **kwargs) self.mobject = mobject self.submobjects = self.get_eyes().submobjects
def init_colors(self): VMobject.init_colors(self) self.gradient_highlight(*self.colors)
def clear(self): self.add_content(VMobject()) return self
def get_center(self): result = VMobject.get_center(self) if hasattr(self, "center_offset"): result -= self.center_offset return result
def __init__(self, rows, columns, **kwargs): digest_config(self, kwargs, locals()) VMobject.__init__(self, **kwargs)
def __init__(self, *vertices, **kwargs): assert len(vertices) > 1 digest_locals(self) VMobject.__init__(self, **kwargs)
def __init__(self, points, **kwargs): VMobject.__init__(self, **kwargs) self.set_points(points)
def __init__(self, start, end, **kwargs): digest_config(self, kwargs) self.set_start_and_end(start, end) VMobject.__init__(self, **kwargs)
def __init__(self, **kwargs): digest_config(self, kwargs) if self.leftmost_tick is None: self.leftmost_tick = np.ceil(self.x_min) VMobject.__init__(self, **kwargs)
def shift_brace(self, obj, **kwargs): if isinstance(obj, list): obj = VMobject(*obj) self.brace = Brace(obj, self.brace_direction, **kwargs) self.brace.put_at_tip(self.desc) self.submobjects[0] = self.brace return self
class NumberPlane(VMobject): CONFIG = { "color" : BLUE_D, "secondary_color" : BLUE_E, "axes_color" : WHITE, "secondary_stroke_width" : 1, "x_radius": None, "y_radius": None, "x_unit_size" : 1, "y_unit_size" : 1, "center_point" : ORIGIN, "x_line_frequency" : 1, "y_line_frequency" : 1, "secondary_line_ratio" : 1, "written_coordinate_height" : 0.2, "propogate_style_to_family" : False, } def generate_points(self): if self.x_radius is None: center_to_edge = (SPACE_WIDTH + abs(self.center_point[0])) self.x_radius = center_to_edge / self.x_unit_size if self.y_radius is None: center_to_edge = (SPACE_HEIGHT + abs(self.center_point[1])) self.y_radius = center_to_edge / self.y_unit_size self.axes = VMobject() self.main_lines = VMobject() self.secondary_lines = VMobject() tuples = [ ( self.x_radius, self.x_line_frequency, self.y_radius*DOWN, self.y_radius*UP, RIGHT ), ( self.y_radius, self.y_line_frequency, self.x_radius*LEFT, self.x_radius*RIGHT, UP, ), ] for radius, freq, start, end, unit in tuples: main_range = np.arange(0, radius, freq) step = freq/float(freq + self.secondary_line_ratio) for v in np.arange(0, radius, step): line1 = Line(start+v*unit, end+v*unit) line2 = Line(start-v*unit, end-v*unit) if v == 0: self.axes.add(line1) elif v in main_range: self.main_lines.add(line1, line2) else: self.secondary_lines.add(line1, line2) self.add(self.secondary_lines, self.main_lines, self.axes) self.stretch(self.x_unit_size, 0) self.stretch(self.y_unit_size, 1) self.shift(self.center_point) #Put x_axis before y_axis y_axis, x_axis = self.axes.split() self.axes = VMobject(x_axis, y_axis) def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke( self.secondary_color, self.secondary_stroke_width ) return self def get_center_point(self): return self.coords_to_point(0, 0) def coords_to_point(self, x, y): x, y = np.array([x, y]) result = self.axes.get_center() result += x*self.get_x_unit_size()*RIGHT result += y*self.get_y_unit_size()*UP return result def point_to_coords(self, point): new_point = point - self.axes.get_center() x = new_point[0]/self.get_x_unit_size() y = new_point[1]/self.get_y_unit_size() return x, y def get_x_unit_size(self): return self.axes.get_width() / (2.0*self.x_radius) def get_y_unit_size(self): return self.axes.get_height() / (2.0*self.y_radius) def get_coordinate_labels(self, x_vals = None, y_vals = None): result = [] if x_vals == None and y_vals == None: x_vals = range(-int(self.x_radius), int(self.x_radius)) y_vals = range(-int(self.y_radius), int(self.y_radius)) for index, vals in enumerate([x_vals, y_vals]): num_pair = [0, 0] for val in vals: if val == 0: continue num_pair[index] = val point = self.coords_to_point(*num_pair) num = TexMobject(str(val)) num.add_background_rectangle() num.scale_to_fit_height( self.written_coordinate_height ) vect = DOWN if index == 0 else LEFT num.next_to(point, vect, buff = SMALL_BUFF) result.append(num) return result def get_axes(self): return self.axes def get_axis_labels(self, x_label = "x", y_label = "y"): x_axis, y_axis = self.get_axes().split() quads = [ (x_axis, x_label, UP, RIGHT), (y_axis, y_label, RIGHT, UP), ] labels = VGroup() for axis, tex, vect, edge in quads: label = TexMobject(tex) label.add_background_rectangle() label.next_to(axis, vect) label.to_edge(edge) labels.add(label) self.axis_labels = labels return labels def add_coordinates(self, x_vals = None, y_vals = None): self.add(*self.get_coordinate_labels(x_vals, y_vals)) return self def get_vector(self, coords, **kwargs): point = coords[0]*RIGHT + coords[1]*UP arrow = Arrow(ORIGIN, coords, **kwargs) return arrow def prepare_for_nonlinear_transform(self, num_inserted_anchor_points = 50): for mob in self.family_members_with_points(): num_anchors = mob.get_num_anchor_points() if num_inserted_anchor_points > num_anchors: mob.insert_n_anchor_points(num_inserted_anchor_points-num_anchors) mob.make_smooth() return self def apply_function(self, function, maintain_smoothness = True): SVGMobject.apply_function(self, function, maintain_smoothness = maintain_smoothness)
def init_colors(self): VMobject.init_colors(self) self.gradient_highlight(*self.colors) for order in sorted(self.order_to_stroke_width_map.keys()): if self.order >= order: self.set_stroke(width=self.order_to_stroke_width_map[order])
class NumberLine(VMobject): CONFIG = { "color": BLUE, "x_min": -SPACE_WIDTH, "x_max": SPACE_WIDTH, "space_unit_to_num": 1, "tick_size": 0.1, "tick_frequency": 1, "leftmost_tick": None, #Defaults to ceil(x_min) "numbers_with_elongated_ticks": [0], "longer_tick_multiple": 2, "number_at_center": 0, "propogate_style_to_family": True } def __init__(self, **kwargs): digest_config(self, kwargs) if self.leftmost_tick is None: self.leftmost_tick = np.ceil(self.x_min) VMobject.__init__(self, **kwargs) def generate_points(self): self.main_line = Line(self.x_min * RIGHT, self.x_max * RIGHT) self.tick_marks = VMobject() self.add(self.main_line, self.tick_marks) for x in self.get_tick_numbers(): self.add_tick(x, self.tick_size) for x in self.numbers_with_elongated_ticks: self.add_tick(x, self.longer_tick_multiple * self.tick_size) self.stretch(self.space_unit_to_num, 0) self.shift(-self.number_to_point(self.number_at_center)) def add_tick(self, x, size): self.tick_marks.add( Line( x * RIGHT + size * DOWN, x * RIGHT + size * UP, )) return self def get_tick_marks(self): return self.tick_marks def get_tick_numbers(self): return np.arange(self.leftmost_tick, self.x_max, self.tick_frequency) def number_to_point(self, number): return interpolate( self.main_line.get_left(), self.main_line.get_right(), float(number - self.x_min) / (self.x_max - self.x_min)) def point_to_number(self, point): dist_from_left = (point[0] - self.main_line.get_left()[0]) num_dist_from_left = num_dist_from_left / self.space_unit_to_num return self.x_min + dist_from_left def default_numbers_to_display(self): return np.arange(self.leftmost_tick, self.x_max, 1) def get_vertical_number_offset(self, direction=DOWN): return 4 * direction * self.tick_size def get_number_mobjects(self, *numbers, **kwargs): #TODO, handle decimals if len(numbers) == 0: numbers = self.default_numbers_to_display() result = VGroup() for number in numbers: mob = TexMobject(str(int(number))) mob.scale_to_fit_height(3 * self.tick_size) mob.shift(self.number_to_point(number), self.get_vertical_number_offset(**kwargs)) result.add(mob) return result def add_numbers(self, *numbers, **kwargs): self.numbers = self.get_number_mobjects(*numbers, **kwargs) self.add(*self.numbers) return self
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, } 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() looking_direction = None looking_direction = self.get_looking_direction() should_be_flipped = self.is_flipped() self.__init__(mode) self.scale_to_fit_height(curr_height) self.shift(curr_center) self.look(looking_direction) if should_be_flipped ^ self.is_flipped(): self.flip() return self def look(self, direction): 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=1)) 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 NumberPlane(VMobject): CONFIG = { "color": BLUE_D, "secondary_color": BLUE_E, "axes_color": WHITE, "secondary_stroke_width": 1, "x_radius": SPACE_WIDTH, "y_radius": SPACE_HEIGHT, "space_unit_to_x_unit": 1, "space_unit_to_y_unit": 1, "x_line_frequency": 1, "y_line_frequency": 1, "secondary_line_ratio": 1, "written_coordinate_height": 0.2, "written_coordinate_nudge": 0.1 * (DOWN + RIGHT), "num_pair_at_center": (0, 0), "propogate_style_to_family": False, } def generate_points(self): self.axes = VMobject() self.main_lines = VMobject() self.secondary_lines = VMobject() tuples = [ (self.x_radius, self.x_line_frequency, self.y_radius * DOWN, self.y_radius * UP, RIGHT), ( self.y_radius, self.y_line_frequency, self.x_radius * LEFT, self.x_radius * RIGHT, UP, ), ] for radius, freq, start, end, unit in tuples: main_range = np.arange(0, radius, freq) step = freq / float(freq + self.secondary_line_ratio) for v in np.arange(0, radius, step): line1 = Line(start + v * unit, end + v * unit) line2 = Line(start - v * unit, end - v * unit) if v == 0: self.axes.add(line1) elif v in main_range: self.main_lines.add(line1, line2) else: self.secondary_lines.add(line1, line2) self.add(self.axes, self.main_lines, self.secondary_lines) self.stretch(self.space_unit_to_x_unit, 0) self.stretch(self.space_unit_to_y_unit, 1) #Put x_axis before y_axis y_axis, x_axis = self.axes.split() self.axes = VMobject(x_axis, y_axis) def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke(self.secondary_color, self.secondary_stroke_width) return self def get_center_point(self): return self.num_pair_to_point(self.num_pair_at_center) def num_pair_to_point(self, pair): pair = np.array(pair) + self.num_pair_at_center result = self.get_center() result[0] += pair[0] * self.space_unit_to_x_unit result[1] += pair[1] * self.space_unit_to_y_unit return result def point_to_num_pair(self, point): new_point = point - self.get_center() center_x, center_y = self.num_pair_at_center x = center_x + point[0] / self.space_unit_to_x_unit y = center_y + point[1] / self.space_unit_to_y_unit return x, y def get_coordinate_labels(self, x_vals=None, y_vals=None): result = [] if x_vals == None and y_vals == None: x_vals = range(-int(self.x_radius), int(self.x_radius)) y_vals = range(-int(self.y_radius), int(self.y_radius)) for index, vals in enumerate([x_vals, y_vals]): num_pair = [0, 0] for val in vals: num_pair[index] = val point = self.num_pair_to_point(num_pair) num = TexMobject(str(val)) num.scale_to_fit_height(self.written_coordinate_height) num.shift(point - num.get_corner(UP + LEFT), self.written_coordinate_nudge) result.append(num) return result def get_axes(self): return self.axes def get_axis_labels(self, x_label="x", y_label="y"): x_axis, y_axis = self.get_axes().split() x_label_mob = TexMobject(x_label) y_label_mob = TexMobject(y_label) x_label_mob.next_to(x_axis, DOWN) x_label_mob.to_edge(RIGHT) y_label_mob.next_to(y_axis, RIGHT) y_label_mob.to_edge(UP) return VMobject(x_label_mob, y_label_mob) def add_coordinates(self, x_vals=None, y_vals=None): self.add(*self.get_coordinate_labels(x_vals, y_vals)) return self def get_vector(self, coords, **kwargs): point = coords[0] * RIGHT + coords[1] * UP arrow = Arrow(ORIGIN, coords, **kwargs) return arrow def prepare_for_nonlinear_transform(self, num_inserted_anchor_points=50): for mob in self.family_members_with_points(): mob.insert_n_anchor_points(num_inserted_anchor_points) mob.make_smooth() return self
def __init__(self, **kwargs): VMobject.__init__(self, **kwargs) for submobject in self.submobject_family(): submobject.part_of_three_d_mobject = True
class NumberPlane(VMobject): CONFIG = { "color": BLUE_D, "secondary_color": BLUE_E, "axes_color": WHITE, "secondary_stroke_width": 1, # TODO: Allow coordinate center of NumberPlane to not be at (0, 0) "x_radius": None, "y_radius": None, "x_unit_size": 1, "y_unit_size": 1, "center_point": ORIGIN, "x_line_frequency": 1, "y_line_frequency": 1, "secondary_line_ratio": 1, "written_coordinate_height": 0.2, "propagate_style_to_family": False, "make_smooth_after_applying_functions": True, } def generate_points(self): if self.x_radius is None: center_to_edge = (SPACE_WIDTH + abs(self.center_point[0])) self.x_radius = center_to_edge / self.x_unit_size if self.y_radius is None: center_to_edge = (SPACE_HEIGHT + abs(self.center_point[1])) self.y_radius = center_to_edge / self.y_unit_size self.axes = VMobject() self.main_lines = VMobject() self.secondary_lines = VMobject() tuples = [ (self.x_radius, self.x_line_frequency, self.y_radius * DOWN, self.y_radius * UP, RIGHT), ( self.y_radius, self.y_line_frequency, self.x_radius * LEFT, self.x_radius * RIGHT, UP, ), ] for radius, freq, start, end, unit in tuples: main_range = np.arange(0, radius, freq) step = freq / float(freq + self.secondary_line_ratio) for v in np.arange(0, radius, step): line1 = Line(start + v * unit, end + v * unit) line2 = Line(start - v * unit, end - v * unit) if v == 0: self.axes.add(line1) elif v in main_range: self.main_lines.add(line1, line2) else: self.secondary_lines.add(line1, line2) self.add(self.secondary_lines, self.main_lines, self.axes) self.stretch(self.x_unit_size, 0) self.stretch(self.y_unit_size, 1) self.shift(self.center_point) #Put x_axis before y_axis y_axis, x_axis = self.axes.split() self.axes = VMobject(x_axis, y_axis) def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke(self.secondary_color, self.secondary_stroke_width) return self def get_center_point(self): return self.coords_to_point(0, 0) def coords_to_point(self, x, y): x, y = np.array([x, y]) result = self.axes.get_center() result += x * self.get_x_unit_size() * RIGHT result += y * self.get_y_unit_size() * UP return result def point_to_coords(self, point): new_point = point - self.axes.get_center() x = new_point[0] / self.get_x_unit_size() y = new_point[1] / self.get_y_unit_size() return x, y # Does not recompute center, unit_sizes for each call; useful for # iterating over large lists of points, but does assume these # attributes are kept accurate. (Could alternatively have a method # which returns a function dynamically created after a single # call to each of get_center(), get_x_unit_size(), etc.) def point_to_coords_cheap(self, point): new_point = point - self.center_point x = new_point[0] / self.x_unit_size y = new_point[1] / self.y_unit_size return x, y def get_x_unit_size(self): return self.axes.get_width() / (2.0 * self.x_radius) def get_y_unit_size(self): return self.axes.get_height() / (2.0 * self.y_radius) def get_coordinate_labels(self, x_vals=None, y_vals=None): coordinate_labels = VGroup() if x_vals == None: x_vals = range(-int(self.x_radius), int(self.x_radius) + 1) if y_vals == None: y_vals = range(-int(self.y_radius), int(self.y_radius) + 1) for index, vals in enumerate([x_vals, y_vals]): num_pair = [0, 0] for val in vals: if val == 0: continue num_pair[index] = val point = self.coords_to_point(*num_pair) num = TexMobject(str(val)) num.add_background_rectangle() num.scale_to_fit_height(self.written_coordinate_height) num.next_to(point, DOWN + LEFT, buff=SMALL_BUFF) coordinate_labels.add(num) self.coordinate_labels = coordinate_labels return coordinate_labels def get_axes(self): return self.axes def get_axis_labels(self, x_label="x", y_label="y"): x_axis, y_axis = self.get_axes().split() quads = [ (x_axis, x_label, UP, RIGHT), (y_axis, y_label, RIGHT, UP), ] labels = VGroup() for axis, tex, vect, edge in quads: label = TexMobject(tex) label.add_background_rectangle() label.next_to(axis, vect) label.to_edge(edge) labels.add(label) self.axis_labels = labels return labels def add_coordinates(self, x_vals=None, y_vals=None): self.add(*self.get_coordinate_labels(x_vals, y_vals)) return self def get_vector(self, coords, **kwargs): point = coords[0] * RIGHT + coords[1] * UP arrow = Arrow(ORIGIN, coords, **kwargs) return arrow def prepare_for_nonlinear_transform(self, num_inserted_anchor_points=50): for mob in self.family_members_with_points(): num_anchors = mob.get_num_anchor_points() if num_inserted_anchor_points > num_anchors: mob.insert_n_anchor_points(num_inserted_anchor_points - num_anchors) mob.make_smooth() return self
def __init__(self, function, **kwargs): self.function = function VMobject.__init__(self, **kwargs)
def construct(self): plus = TexMobject("+") equals = TexMobject("=") randy = Randolph() randy.scale_to_fit_height(1) randy.shift(-randy.get_bottom()) axes = self.add_axes() x_axis, y_axis = axes.split() v1 = self.add_vector([1, 2]) coords1, x_line1, y_line1 = self.vector_to_coords(v1, clean_up=False) self.play( ApplyFunction(lambda m: m.next_to(y_axis, RIGHT).to_edge(UP), coords1)) plus.next_to(coords1, RIGHT) v2 = self.add_vector([3, -1], color=MAROON_B) coords2, x_line2, y_line2 = self.vector_to_coords(v2, clean_up=False) self.dither() self.play( ApplyMethod(coords2.next_to, plus, RIGHT), Write(plus, run_time=1), *[ ApplyMethod(mob.shift, v1.get_end()) for mob in v2, x_line2, y_line2 ]) equals.next_to(coords2, RIGHT) self.dither() self.play(FadeIn(randy)) for step in [RIGHT, 2 * UP, 3 * RIGHT, DOWN]: self.play(ApplyMethod(randy.shift, step, run_time=1.5)) self.dither() self.play(ApplyMethod(randy.shift, -randy.get_bottom())) self.play(ApplyMethod(x_line2.shift, 2 * DOWN)) self.play(ApplyMethod(y_line1.shift, 3 * RIGHT)) for step in [4 * RIGHT, 2 * UP, DOWN]: self.play(ApplyMethod(randy.shift, step)) self.play(FadeOut(randy)) self.remove(randy) one_brace = Brace(x_line1) three_brace = Brace(x_line2) one = TexMobject("1").next_to(one_brace, DOWN) three = TexMobject("3").next_to(three_brace, DOWN) self.play(GrowFromCenter(one_brace), GrowFromCenter(three_brace), Write(one), Write(three), run_time=1) self.dither() two_brace = Brace(y_line1, RIGHT) two = TexMobject("2").next_to(two_brace, RIGHT) new_y_line = Line(4 * RIGHT, 4 * RIGHT + UP, color=Y_COLOR) two_minus_one_brace = Brace(new_y_line, RIGHT) two_minus_one = TexMobject("2+(-1)").next_to(two_minus_one_brace, RIGHT) self.play(GrowFromCenter(two_brace), Write(two, run_time=1)) self.dither() self.play(Transform(two_brace, two_minus_one_brace), Transform(two, two_minus_one), Transform(y_line1, new_y_line), Transform(y_line2, new_y_line)) self.dither() self.add_vector(v2.get_end(), color=PINK) sum_coords = Matrix(["1+3", "2+(-1)"]) sum_coords.scale_to_fit_height(coords1.get_height()) sum_coords.next_to(equals, RIGHT) brackets = sum_coords.get_brackets() x1, y1 = coords1.get_mob_matrix().flatten() x2, y2 = coords2.get_mob_matrix().flatten() sum_x, sum_y = sum_coords.get_mob_matrix().flatten() sum_x_start = VMobject(x1, x2).copy() sum_y_start = VMobject(y1, y2).copy() self.play(Write(brackets), Write(equals), Transform(sum_x_start, sum_x), run_time=1) self.play(Transform(sum_y_start, sum_y)) self.dither(2) starters = [x1, y1, x2, y2, sum_x_start, sum_y_start] variables = map(TexMobject, ["x_1", "y_1", "x_2", "y_2", "x_1+y_1", "x_2+y_2"]) for i, (var, starter) in enumerate(zip(variables, starters)): if i % 2 == 0: var.highlight(X_COLOR) else: var.highlight(Y_COLOR) var.scale(VECTOR_LABEL_SCALE_FACTOR) var.move_to(starter) self.play(Transform(VMobject(*starters[:4]), VMobject(*variables[:4])), FadeOut(sum_x_start), FadeOut(sum_y_start)) sum_x_end, sum_y_end = variables[-2:] self.dither(2) self.play(Transform(VMobject(x1, x2).copy(), sum_x_end)) self.play(Transform(VMobject(y1, y2).copy(), sum_y_end)) self.dither(3)
def construct(self): vect = Matrix(["x", "y"]) vect.get_entries().highlight(YELLOW) rot_matrix = Matrix([[0, -1], [1, 0]]) rot_matrix.highlight(TEAL) shear_matrix = Matrix([[1, 1], [0, 1]]) shear_matrix.highlight(PINK) l_paren, r_paren = map(TexMobject, ["\\Big(", "\\Big)"]) for p in l_paren, r_paren: p.scale_to_fit_height(1.4 * vect.get_height()) long_way = VMobject(shear_matrix, l_paren, rot_matrix, vect, r_paren) long_way.arrange_submobjects(buff=0.1) long_way.to_edge(LEFT).shift(UP) equals = TexMobject("=").next_to(long_way, RIGHT) comp_matrix = Matrix([[1, -1], [1, 0]]) comp_matrix.highlight_columns(X_COLOR, Y_COLOR) vect_copy = vect.copy() short_way = VMobject(comp_matrix, vect_copy) short_way.arrange_submobjects(buff=0.1) short_way.next_to(equals, RIGHT) pairs = [ (rot_matrix, "Rotation"), (shear_matrix, "Shear"), (comp_matrix, "Composition"), ] for matrix, word in pairs: brace = Brace(matrix) text = TextMobject(word).next_to(brace, DOWN) brace.highlight(matrix.get_color()) text.highlight(matrix.get_color()) matrix.add(brace, text) comp_matrix.split()[-1].submobject_gradient_highlight(TEAL, PINK) self.add(vect) groups = [ [rot_matrix], [l_paren, r_paren, shear_matrix], [equals, comp_matrix, vect_copy], ] for group in groups: self.play(*map(Write, group)) self.dither() self.play(*map(FadeOut, [l_paren, r_paren, vect, vect_copy])) comp_matrix.add(equals) matrices = VMobject(shear_matrix, rot_matrix, comp_matrix) self.play( ApplyMethod(matrices.arrange_submobjects, buff=0.1, aligned_edge=UP)) self.dither() arrow = Arrow(rot_matrix.get_right(), shear_matrix.get_left()) arrow.shift((rot_matrix.get_top()[1] + 0.2) * UP) words = TextMobject("Read right to left") words.submobjects.reverse() words.next_to(arrow, UP) functions = TexMobject("f(g(x))") functions.next_to(words, UP) self.play(ShowCreation(arrow)) self.play(Write(words)) self.dither() self.play(Write(functions)) self.dither()
def __init__(self, *args, **kwargs): VMobject.__init__(self, *args, **kwargs) shade_in_3d(self)
def construct(self): v_sum = VMobject( Vector([1, 1], color=YELLOW), Vector([3, 1], color=BLUE).shift(RIGHT + UP), Vector([4, 2], color=GREEN), ) scalar_multiplication = VMobject(TexMobject("2 \\cdot "), Vector([1, 1]), TexMobject("="), Vector([2, 2], color=WHITE)) scalar_multiplication.arrange_submobjects(RIGHT) both = VMobject(v_sum, scalar_multiplication) both.arrange_submobjects(RIGHT, buff=1) both.shift(2 * DOWN) self.add(both) UpcomingSeriesOfVidoes.construct(self) last_video = self.mobjects[-1] self.play(ApplyMethod(last_video.highlight, YELLOW)) self.dither() everything = VMobject(*self.mobjects) everything.remove(last_video) big_last_video = last_video.copy() big_last_video.center() big_last_video.scale_to_fit_height(2.5 * SPACE_HEIGHT) big_last_video.set_fill(opacity=0) self.play(ApplyMethod(everything.shift, 2 * SPACE_WIDTH * LEFT), Transform(last_video, big_last_video), run_time=2)
def apply_function(self, function, maintain_smoothness=True): VMobject.apply_function(self, function, maintain_smoothness=maintain_smoothness)
def __init__(self, angle, **kwargs): self.angle = angle VMobject.__init__(self, **kwargs)
class NumberLine(VMobject): CONFIG = { "color" : BLUE, "x_min" : -SPACE_WIDTH, "x_max" : SPACE_WIDTH, "unit_size" : 1, "tick_size" : 0.1, "tick_frequency" : 1, "leftmost_tick" : None, #Defaults to ceil(x_min) "numbers_with_elongated_ticks" : [0], "numbers_to_show" : None, "longer_tick_multiple" : 2, "number_at_center" : 0, "propogate_style_to_family" : True } def __init__(self, **kwargs): digest_config(self, kwargs) if self.leftmost_tick is None: self.leftmost_tick = np.ceil(self.x_min) VMobject.__init__(self, **kwargs) def generate_points(self): self.main_line = Line(self.x_min*RIGHT, self.x_max*RIGHT) self.tick_marks = VMobject() self.add(self.main_line, self.tick_marks) for x in self.get_tick_numbers(): self.add_tick(x, self.tick_size) for x in self.numbers_with_elongated_ticks: self.add_tick(x, self.longer_tick_multiple*self.tick_size) self.stretch(self.unit_size, 0) self.shift(-self.number_to_point(self.number_at_center)) def add_tick(self, x, size): self.tick_marks.add(Line( x*RIGHT+size*DOWN, x*RIGHT+size*UP, )) return self def get_tick_marks(self): return self.tick_marks def get_tick_numbers(self): epsilon = 0.001 return np.arange( self.leftmost_tick, self.x_max+epsilon, self.tick_frequency ) def number_to_point(self, number): alpha = float(number-self.x_min)/(self.x_max - self.x_min) return interpolate( self.main_line.get_start(), self.main_line.get_end(), alpha ) def point_to_number(self, point): left_point, right_point = self.main_line.get_start_and_end() full_vect = right_point-left_point def distance_from_left(p): return np.dot(p-left_point, full_vect)/np.linalg.norm(full_vect) return interpolate( self.x_min, self.x_max, distance_from_left(point)/distance_from_left(right_point) ) def default_numbers_to_display(self): if self.numbers_to_show is not None: return self.numbers_to_show return np.arange(self.leftmost_tick, self.x_max, 1) def get_vertical_number_offset(self, direction = DOWN): return 4*direction*self.tick_size def get_number_mobjects(self, *numbers, **kwargs): #TODO, handle decimals if len(numbers) == 0: numbers = self.default_numbers_to_display() if "force_integers" in kwargs and kwargs["force_integers"]: numbers = map(int, numbers) result = VGroup() for number in numbers: mob = TexMobject(str(number)) mob.scale_to_fit_height(3*self.tick_size) mob.shift( self.number_to_point(number), self.get_vertical_number_offset(**kwargs) ) result.add(mob) return result def add_numbers(self, *numbers, **kwargs): self.numbers = self.get_number_mobjects( *numbers, **kwargs ) self.add(*self.numbers) return self