def coords_to_vector(self, vector, coords_start=2 * RIGHT + 2 * UP, clean_up=True): starting_mobjects = list(self.mobjects) array = Matrix(vector) array.shift(coords_start) arrow = Vector(vector) x_line = Line(ORIGIN, vector[0] * RIGHT) y_line = Line(x_line.get_end(), arrow.get_end()) x_line.set_color(X_COLOR) y_line.set_color(Y_COLOR) x_coord, y_coord = array.get_mob_matrix().flatten() self.play(Write(array, run_time=1)) self.wait() self.play( ApplyFunction( lambda x: self.position_x_coordinate(x, x_line, vector), x_coord)) self.play(ShowCreation(x_line)) self.play( ApplyFunction( lambda y: self.position_y_coordinate(y, y_line, vector), y_coord), FadeOut(array.get_brackets())) y_coord, brackets = self.get_mobjects_from_last_animation() self.play(ShowCreation(y_line)) self.play(ShowCreation(arrow)) self.wait() if clean_up: self.clear() self.add(*starting_mobjects)
def __init__(self, mobject, **kwargs): VGroup.__init__(self, Line(UP + LEFT, DOWN + RIGHT), Line(UP + RIGHT, DOWN + LEFT), ) self.replace(mobject, stretch=True) self.set_stroke(self.stroke_color, self.stroke_width)
def add_axes(self): x_axis = Line(self.tick_width * LEFT / 2, self.width * RIGHT) y_axis = Line(MED_LARGE_BUFF * DOWN, self.height * UP) ticks = VGroup() heights = np.linspace(0, self.height, self.n_ticks + 1) values = np.linspace(0, self.max_value, self.n_ticks + 1) for y, value in zip(heights, values): tick = Line(LEFT, RIGHT) tick.scale_to_fit_width(self.tick_width) tick.move_to(y * UP) ticks.add(tick) y_axis.add(ticks) self.add(x_axis, y_axis) self.x_axis, self.y_axis = x_axis, y_axis if self.label_y_axis: labels = VGroup() for tick, value in zip(ticks, values): label = TexMobject(str(np.round(value, 2))) label.scale_to_fit_height(self.y_axis_label_height) label.next_to(tick, LEFT, SMALL_BUFF) labels.add(label) self.y_axis_labels = labels self.add(labels)
def add_lines(self, left, right): line_kwargs = { "color": BLUE, "stroke_width": 2, } left_rows = [VGroup(*row) for row in left.get_mob_matrix()] h_lines = VGroup() for row in left_rows[:-1]: h_line = Line(row.get_left(), row.get_right(), **line_kwargs) h_line.next_to(row, DOWN, buff=left.v_buff / 2.) h_lines.add(h_line) right_cols = [ VGroup(*col) for col in np.transpose(right.get_mob_matrix()) ] v_lines = VGroup() for col in right_cols[:-1]: v_line = Line(col.get_top(), col.get_bottom(), **line_kwargs) v_line.next_to(col, RIGHT, buff=right.h_buff / 2.) v_lines.add(v_line) self.play(ShowCreation(h_lines)) self.play(ShowCreation(v_lines)) self.wait() self.show_frame()
def vector_to_coords(self, vector, integer_labels=True, clean_up=True): starting_mobjects = list(self.mobjects) show_creation = False if isinstance(vector, Arrow): arrow = vector vector = arrow.get_end()[:2] else: arrow = Vector(vector) show_creation = True array = vector_coordinate_label(arrow, integer_labels=integer_labels) x_line = Line(ORIGIN, vector[0] * RIGHT) y_line = Line(x_line.get_end(), arrow.get_end()) x_line.set_color(X_COLOR) y_line.set_color(Y_COLOR) x_coord, y_coord = array.get_mob_matrix().flatten() x_coord_start = self.position_x_coordinate( x_coord.copy(), x_line, vector ) y_coord_start = self.position_y_coordinate( y_coord.copy(), y_line, vector ) brackets = array.get_brackets() if show_creation: self.play(ShowCreation(arrow)) self.play( ShowCreation(x_line), Write(x_coord_start), run_time=1 ) self.play( ShowCreation(y_line), Write(y_coord_start), run_time=1 ) self.wait() self.play( Transform(x_coord_start, x_coord, submobject_mode="all_at_once"), Transform(y_coord_start, y_coord, submobject_mode="all_at_once"), Write(brackets, run_time=1), ) self.wait() self.remove(x_coord_start, y_coord_start, brackets) self.add(array) if clean_up: self.clear() self.add(*starting_mobjects) return array, x_line, y_line
def __init__(self, **kwargs): circle = Circle() ticks = [] for x in range(12): alpha = x / 12. point = complex_to_R3(np.exp(2 * np.pi * alpha * complex(0, 1))) length = 0.2 if x % 3 == 0 else 0.1 ticks.append(Line(point, (1 - length) * point)) self.hour_hand = Line(ORIGIN, 0.3 * UP) self.minute_hand = Line(ORIGIN, 0.6 * UP) # for hand in self.hour_hand, self.minute_hand: # #Balance out where the center is # hand.add(VectorizedPoint(-hand.get_end())) VGroup.__init__(self, circle, self.hour_hand, self.minute_hand, *ticks)
def __init__(self, **kwargs): SVGMobject.__init__(self, **kwargs) self.scale_to_fit_height(self.height) self.set_stroke(color = WHITE, width = 0) self.set_fill(self.color, opacity = 1) from for_3b1b_videos.pi_creature import Randolph randy = Randolph(mode = "happy") randy.scale_to_fit_height(0.6*self.get_height()) randy.stretch(0.8, 0) randy.look(RIGHT) randy.move_to(self) randy.shift(0.07*self.height*(RIGHT+UP)) self.randy = self.pi_creature = randy self.add_to_back(randy) orientation_line = Line(self.get_left(), self.get_right()) orientation_line.set_stroke(width = 0) self.add(orientation_line) self.orientation_line = orientation_line for light, color in zip(self.get_lights(), self.light_colors): light.set_fill(color, 1) light.is_subpath = False self.add_treds_to_tires()
def get_tick(self, x, size=None): if size is None: size = self.tick_size result = Line(size * DOWN, size * UP) result.rotate(self.main_line.get_angle()) result.move_to(self.number_to_point(x)) return result
def generate_points(self): start_angle = np.pi/2 + self.arc_angle/2 end_angle = np.pi/2 - self.arc_angle/2 self.add(Arc( start_angle = start_angle, angle = -self.arc_angle )) tick_angle_range = np.linspace(start_angle, end_angle, self.num_ticks) for index, angle in enumerate(tick_angle_range): vect = rotate_vector(RIGHT, angle) tick = Line((1-self.tick_length)*vect, vect) label = TexMobject(str(10*index)) label.scale_to_fit_height(self.tick_length) label.shift((1+self.tick_length)*vect) self.add(tick, label) needle = Polygon( LEFT, UP, RIGHT, stroke_width = 0, fill_opacity = 1, fill_color = self.needle_color ) needle.stretch_to_fit_width(self.needle_width) needle.stretch_to_fit_height(self.needle_height) needle.rotate(start_angle - np.pi/2, about_point = ORIGIN) self.add(needle) self.needle = needle self.center_offset = self.get_center()
def scroll_through_patrons(self): logo_box = Square(side_length=2.5) logo_box.to_corner(DOWN + LEFT, buff=MED_LARGE_BUFF) total_width = FRAME_X_RADIUS - logo_box.get_right()[0] black_rect = Rectangle( fill_color=BLACK, fill_opacity=1, stroke_width=0, width=FRAME_WIDTH, height=0.6 * FRAME_HEIGHT, ) black_rect.to_edge(UP, buff=0) line = DashedLine(FRAME_X_RADIUS * LEFT, FRAME_X_RADIUS * RIGHT) line.move_to(ORIGIN) self.add(line) thanks = TextMobject("Funded by the community, with special thanks to:") thanks.scale(0.9) thanks.next_to(black_rect.get_bottom(), UP, SMALL_BUFF) thanks.set_color(YELLOW) underline = Line(LEFT, RIGHT) underline.scale_to_fit_width(thanks.get_width() + MED_SMALL_BUFF) underline.next_to(thanks, DOWN, SMALL_BUFF) thanks.add(underline) self.add(thanks) patrons = VGroup(*map(TextMobject, self.specific_patrons)) patrons.scale(self.patron_scale_val) for patron in patrons: if patron.get_width() > self.max_patron_width: patron.scale_to_fit_width(self.max_patron_width) columns = VGroup(*[ VGroup(*patrons[i::self.n_patron_columns]) for i in range(self.n_patron_columns) ]) for column in columns: for n, name in enumerate(column): name.shift(n * self.name_y_spacing * DOWN) columns.arrange_submobjects( RIGHT, buff=LARGE_BUFF, aligned_edge=UP, ) columns.scale_to_fit_width(total_width - 1) columns.next_to(black_rect, DOWN, 3 * LARGE_BUFF) columns.to_edge(RIGHT) thanks.align_to(columns, alignment_vect=RIGHT) self.play( columns.move_to, 2 * DOWN, DOWN, columns.to_edge, RIGHT, Animation(black_rect), Animation(line), Animation(thanks), rate_func=None, run_time=self.run_time, )
def generate_points(self): if self.x_radius is None: center_to_edge = (FRAME_X_RADIUS + abs(self.center_point[0])) self.x_radius = center_to_edge / self.x_unit_size if self.y_radius is None: center_to_edge = (FRAME_Y_RADIUS + 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 generate_points(self): body = Cube(side_length=1) for dim, scale_factor in enumerate(self.body_dimensions): body.stretch(scale_factor, dim=dim) body.scale_to_fit_width(self.width) body.set_fill(self.shaded_body_color, opacity=1) body.sort_submobjects(lambda p: p[2]) body[-1].set_fill(self.body_color) keyboard = VGroup(*[ VGroup(*[ Square(**self.key_color_kwargs) for x in range(12 - y % 2) ]).arrange_submobjects(RIGHT, buff=SMALL_BUFF) for y in range(4) ]).arrange_submobjects(DOWN, buff=MED_SMALL_BUFF) keyboard.stretch_to_fit_width( self.keyboard_width_to_body_width * body.get_width(), ) keyboard.stretch_to_fit_height( self.keyboard_height_to_body_height * body.get_height(), ) keyboard.next_to(body, OUT, buff=0.1 * SMALL_BUFF) keyboard.shift(MED_SMALL_BUFF * UP) body.add(keyboard) screen_plate = body.copy() screen_plate.stretch(self.screen_thickness / self.body_dimensions[2], dim=2) screen = Rectangle( stroke_width=0, fill_color=BLACK, fill_opacity=1, ) screen.replace(screen_plate, stretch=True) screen.scale_in_place(self.screen_width_to_screen_plate_width) screen.next_to(screen_plate, OUT, buff=0.1 * SMALL_BUFF) screen_plate.add(screen) screen_plate.next_to(body, UP, buff=0) screen_plate.rotate( self.open_angle, RIGHT, about_point=screen_plate.get_bottom() ) self.screen_plate = screen_plate self.screen = screen axis = Line( body.get_corner(UP + LEFT + OUT), body.get_corner(UP + RIGHT + OUT), color=BLACK, stroke_width=2 ) self.axis = axis self.add(body, screen_plate, axis) self.rotate(5 * np.pi / 12, LEFT, about_point=ORIGIN) self.rotate(np.pi / 6, DOWN, about_point=ORIGIN)
def __init__(self, *text_parts, **kwargs): TextMobject.__init__(self, *text_parts, **kwargs) self.scale(self.scale_factor) self.to_edge(UP) if self.include_underline: underline = Line(LEFT, RIGHT) underline.next_to(self, DOWN, buff=self.underline_buff) if self.match_underline_width_to_text: underline.match_width(self) else: underline.set_width(self.underline_width) self.add(underline) self.underline = underline
def generate_points(self): self.line_group = VGroup() self.dot_group = VGroup() vertices = self.generate_vertices_from_string(self.walk_string) for k in range(len(vertices) - 1): line = Line(vertices[k], vertices[k + 1], color=self.get_mob_color_by_number(k)) dot = Dot(vertices[k], color=self.dot_color) self.line_group.add(line) self.dot_group.add(dot) self.dot_group.add(Dot(vertices[-1], color=self.dot_color)) self.add(self.line_group, self.dot_group) self.horizontally_center()
def add_treds_to_tires(self): for tire in self.get_tires(): radius = tire.get_width() / 2 center = tire.get_center() tred = Line(0.9 * radius * RIGHT, 1.4 * radius * RIGHT, stroke_width=2, color=BLACK) tred.rotate_in_place(np.pi / 4) for theta in np.arange(0, 2 * np.pi, np.pi / 4): new_tred = tred.copy() new_tred.rotate(theta, about_point=ORIGIN) new_tred.shift(center) tire.add(new_tred) return self
def generate_points(self): self.main_line = Line(self.x_min * RIGHT, self.x_max * RIGHT) self.tick_marks = VGroup() self.add(self.main_line, self.tick_marks) rounding_value = int(-np.log10(0.1 * self.tick_frequency)) rounded_numbers_with_elongated_ticks = np.round( self.numbers_with_elongated_ticks, rounding_value) for x in self.get_tick_numbers(): rounded_x = np.round(x, rounding_value) if rounded_x in rounded_numbers_with_elongated_ticks: tick_size_used = self.longer_tick_multiple * self.tick_size else: tick_size_used = self.tick_size self.add_tick(x, tick_size_used) self.stretch(self.unit_size, 0) self.shift(-self.number_to_point(self.number_at_center))
def generate_points(self): outer_rect = Rectangle( width=self.diameter, height=self.thickness, fill_color=self.fill_color, fill_opacity=self.fill_opacity, stroke_color=self.stroke_color, stroke_width=0, # self.stroke_width ) self.add(outer_rect) PI = TAU / 2 ridge_angles = np.arange(PI / self.nb_ridges, PI, PI / self.nb_ridges) ridge_positions = 0.5 * self.diameter * np.array( [np.cos(theta) for theta in ridge_angles]) ridge_color = interpolate_color(BLACK, self.stroke_color, 0.5) for x in ridge_positions: ridge = Line(x * RIGHT + 0.5 * self.thickness * DOWN, x * RIGHT + 0.5 * self.thickness * UP, stroke_color=ridge_color, stroke_width=self.stroke_width) self.add(ridge)
def generate_mobject(self, dic, labels_dict): # Create a dictionary with all attributes to create a new Mobject. # Unspecified values will be filled from the current Mobject if it # exists, and with default values if not. if "stroke_width" not in dic: if hasattr(self, "mobject"): dic["stroke_width"] = self.mobject.stroke_width else: print("Attempted to initialize Edge without stroke_width") breakpoint(context=7) if "rectangular_stem_width" not in dic: if hasattr(self, "mobject"): dic["rectangular_stem_width"] = \ self.mobject.rectangular_stem_width else: print("Attempted to initialize Edge without " "rectangular_stem_width") breakpoint(context=7) if "scale_factor" not in dic: if hasattr(self, "mobject"): dic["scale_factor"] = self.mobject.scale_factor else: print("Attempted to initialize Edge without scale_factor") breakpoint(context=7) if "color" not in dic: if hasattr(self, "mobject"): dic["color"] = self.mobject.color if "directed" not in dic: if hasattr(self, "mobject") and self.mobject.directed: dic["directed"] = self.mobject.directed else: dic["directed"] = False if "curved" not in dic: if hasattr(self, "mobject") and self.mobject.curved: dic["curved"] = self.mobject.curved else: dic["curved"] = False # where along the edge the label is placed if "label_location" not in dic: if hasattr(self, "mobject") and self.mobject.label_location: dic["label_location"] = self.mobject.label_location else: dic["label_location"] = 0.5 # which side of the edge on which the label is placed if "label_side" not in dic: if hasattr(self, "mobject") and self.mobject.label_side: dic["label_side"] = self.mobject.label_side else: dic["label_side"] = Side.CLOCKWISE normalized_vec = self.end_node.mobject.get_center() - \ self.start_node.mobject.get_center() normalized_vec = normalized_vec / numpy.linalg.norm(normalized_vec) normal_vec = rotate_vector(normalized_vec, numpy.pi / 2) if dic["directed"]: mob = Arrow(self.start_node.mobject.get_center() + normalized_vec * (self.start_node.mobject.radius - 0.0), self.end_node.mobject.get_center() - normalized_vec * (self.end_node.mobject.radius - 0.0), buff=0, **dic) else: mob = Line( self.start_node.mobject.get_center() + normalized_vec * self.start_node.mobject.radius, self.end_node.mobject.get_center() - normalized_vec * self.end_node.mobject.radius, **dic) if dic["curved"]: start, end = mob.get_start_and_end() midpoint = (start + end) / 2 def f(x): return x - 0.1 * normal_vec * \ (numpy.linalg.norm(start - midpoint) - numpy.linalg.norm(x - midpoint)) mob.shift(-0.05 * normal_vec).apply_function(f) return mob
def get_secant_slope_group( self, x, graph, dx=None, dx_line_color=None, df_line_color=None, dx_label=None, df_label=None, include_secant_line=True, secant_line_color=None, secant_line_length=10, ): """ Resulting group is of the form VGroup( dx_line, df_line, dx_label, (if applicable) df_label, (if applicable) secant_line, (if applicable) ) with attributes of those names. """ kwargs = locals() kwargs.pop("self") group = VGroup() group.kwargs = kwargs dx = dx or float(self.x_max - self.x_min) / 10 dx_line_color = dx_line_color or self.default_input_color df_line_color = df_line_color or graph.get_color() p1 = self.input_to_graph_point(x, graph) p2 = self.input_to_graph_point(x + dx, graph) interim_point = p2[0] * RIGHT + p1[1] * UP group.dx_line = Line(p1, interim_point, color=dx_line_color) group.df_line = Line(interim_point, p2, color=df_line_color) group.add(group.dx_line, group.df_line) labels = VGroup() if dx_label is not None: group.dx_label = TexMobject(dx_label) labels.add(group.dx_label) group.add(group.dx_label) if df_label is not None: group.df_label = TexMobject(df_label) labels.add(group.df_label) group.add(group.df_label) if len(labels) > 0: max_width = 0.8 * group.dx_line.get_width() max_height = 0.8 * group.df_line.get_height() if labels.get_width() > max_width: labels.scale_to_fit_width(max_width) if labels.get_height() > max_height: labels.scale_to_fit_height(max_height) if dx_label is not None: group.dx_label.next_to(group.dx_line, np.sign(dx) * DOWN, buff=group.dx_label.get_height() / 2) group.dx_label.set_color(group.dx_line.get_color()) if df_label is not None: group.df_label.next_to(group.df_line, np.sign(dx) * RIGHT, buff=group.df_label.get_height() / 2) group.df_label.set_color(group.df_line.get_color()) if include_secant_line: secant_line_color = secant_line_color or self.default_derivative_color group.secant_line = Line(p1, p2, color=secant_line_color) group.secant_line.scale_in_place(secant_line_length / group.secant_line.get_length()) group.add(group.secant_line) return group
def construct(self): # Chart on the left colors = [WHITE, ORANGE, GREEN] titles = VGroup(*[ TexMobject(text).set_color(color) for text, color in zip(["n", "p_n", "q_n"], colors) ]) contents = VGroup(*[ VGroup(*[ TexMobject("%d" % num) for num in [k, central_binomial_coeff(k), central_binomial_coeff(k)] ]) for k in range(8) ]) titles.arrange_submobjects(RIGHT, buff=1) for num, line in enumerate(contents): for k, element in enumerate(line): buff = 0.6 + 0.8 * num element.next_to(titles[k], DOWN, aligned_edge=LEFT, buff=buff) element.set_color(colors[k]) sep_line = Line(ORIGIN, 4.5 * RIGHT, stroke_width=5) sep_line.next_to(titles, DOWN) chart = VGroup(titles, contents, sep_line) chart.set_height(7) chart.center().to_edge(LEFT) self.add(chart) # Figures on the right std_zero_pos_axis = NumberLine(x_min=-2, x_max=2, color=GREY, unit_size=0.25, tick_size=0.05) std_zero_pos_axis.rotate(np.pi / 2) std_nocross_pos_axis = NumberLine(x_min=-4, x_max=4, color=GREY, unit_size=0.25, tick_size=0.05) std_nocross_pos_axis.rotate(np.pi / 2) std_time_axis = NumberLine(x_min=0, x_max=5.5, color=GREY, unit_size=0.25, tick_size=0.05) std_zero_axes = VGroup(std_zero_pos_axis, std_time_axis) std_nocross_axes = VGroup(std_nocross_pos_axis, std_time_axis) zero_walks = VGroup() for sequence in ["UUDD", "UDUD", "UDDU", "DDUU", "DUDU", "DUUD"]: axes = std_zero_axes.copy() zero_walk = RandomWalk1DArrow(sequence, step_size=0.25) zero_walk.move_start_to(axes[0].number_to_point(0)) zero_walks.add(VGroup(axes, zero_walk)) zero_walks.arrange_submobjects_in_grid(2, 3, buff=0.5) zero_rect = SurroundingRectangle(zero_walks, color=ORANGE, buff=0.4) zero_walks.add(zero_rect) nocross_walks = VGroup() for sequence in ["DDDD", "DDUD", "DDDU", "UUUU", "UUDU", "UUUD"]: axes = std_nocross_axes.copy() nocross_walk = RandomWalk1DArrow(sequence, step_size=0.25) nocross_walk.move_start_to(axes[0].number_to_point(0)) nocross_walks.add(VGroup(axes, nocross_walk)) nocross_walks.arrange_submobjects_in_grid(2, 3, buff=0.5) nocross_rect = SurroundingRectangle(nocross_walks, color=GREEN, buff=0.4) nocross_walks.add(nocross_rect) relation = TexMobject("p_2", "=", "q_2", "=", "6") relation[0].set_color(ORANGE) relation[2].set_color(GREEN) relation.scale(1.5) figure = VGroup(zero_walks, relation, nocross_walks) figure.arrange_submobjects(DOWN) figure.set_height(7) figure.center().to_edge(RIGHT) self.add(figure) self.wait()
def construct(self): colors = ["#FF0000", "#FF8000", "#FFFF00", "#00FF00", "#0080FF"] wallis_rects_4 = WallisRectangles( order=5, rect_colors=colors, ) vert_lines = VGroup(*[ Line(3.5*UP, 3.5*DOWN, color = GREY, stroke_width = 3) \ .next_to(wallis_rects_4.get_rectangle(0, k), direction, buff = 0) for k, direction in zip(list(range(5))+[4], [LEFT]*5+[RIGHT]) ]) horiz_lines = VGroup(*[ Line(3.5*LEFT, 3.5*RIGHT, color = GREY, stroke_width = 3) \ .next_to(wallis_rects_4.get_rectangle(k, 0), direction, buff = 0) for k, direction in zip(list(range(5))+[4], [DOWN]*5+[UP]) ]) for vert_line in vert_lines: vert_line.vertically_center() for horiz_line in horiz_lines: horiz_line.horizontally_center() vert_labels = VGroup(*[ TexMobject("a_%d" % k) \ .move_to((vert_lines[k].get_center() + vert_lines[k+1].get_center())/2) \ .shift(3.5*DOWN) for k in range(5) ]) horiz_labels = VGroup(*[ TexMobject("a_%d" % k) \ .move_to((horiz_lines[k].get_center() + horiz_lines[k+1].get_center())/2) \ .shift(3.5*LEFT) for k in range(5) ]) area_texs = VGroup() factors = [1.25, 1, 0.9, 0.7, 0.6] for p in range(5): for q in range(5 - p): rect = wallis_rects_4.get_rectangle(p, q) tex = TexMobject("{a_%d} {a_%d}" % (q, p)) tex.scale(factors[p + q]) tex.move_to(rect) area_texs.add(tex) figure = VGroup() figure.add(wallis_rects_4, vert_lines, horiz_lines, vert_labels, horiz_labels, area_texs) figure.to_edge(LEFT) self.add(figure) tex_list = VGroup() for p in range(5): formula_string = ( " + ".join(["a_%d a_%d" % (q, p - q) for q in range(p + 1)]) + "=1") formula = TexMobject(formula_string) tex_list.add(formula) # tex_list.add(TexMobject("\\vdots")) tex_factors = np.linspace(1, 0.7, 5) for tex, color, factor in zip(tex_list, colors, tex_factors): tex.set_color(color) tex.scale(factor) tex_list.arrange_submobjects(DOWN, aligned_edge=LEFT) tex_list.to_edge(RIGHT) self.add(tex_list) self.wait()