def construct(self): # Generate transformation animations of the twin dragon curve anims = list() fractal = VMobject() fractal.shift(UP) for order in range(-1, self.max_order+1): new_fractal = TwinDragon(order = order) new_fractal.shift(UP) run_time = 0.5 if order >= 0 else 0 anims.append( Transform( fractal, new_fractal, submobject_mode = "all_at_once", run_time = run_time, ) ) fractal = new_fractal # Add the channel name text = TextMobject("Solara570") text.scale(2).to_edge(DOWN, buff = 1.2) # Now sit back and watch self.play( Succession(*anims, rate_func = smooth), Write(text, lag_factor = 2.5, rate_func = squish_rate_func(smooth, 0.1, 0.9)), run_time = 4.5, )
def construct(self): square = Square(side_length=5, fill_color=YELLOW, fill_opacity=1) label = TextMobject("Text at an angle") label_bg = BackgroundRectangle(label, fill_opacity=1) label_group = VGroup(label_bg, label) label_group.rotate(TAU / 8) label2 = TextMobject("Boxed text", color=BLACK) label2_bg = SurroundingRectangle(label2, color=BLUE, fill_color=RED, fill_opacity=.5) label2_group = VGroup(label2, label2_bg) label2_group.next_to(label_group, DOWN) label3 = TextMobject("Rainbow") label3.scale(2) label3.set_color_by_gradient(RED, GREEN, BLUE) label3.to_edge(DOWN) self.add(square) self.play(FadeIn(label_group)) self.play(FadeIn(label2_group)) self.play(FadeIn(label3)) self.wait(2)
def get_text(self, text, scale=1, buff=0.1, invert_dir=False, invert_texto=False, remove_rot=False, **moreargs): linea_referencia = Line(self[0][0].get_start(), self[0][-1].get_end()) texto = TextMobject(text, **moreargs) ancho = texto.get_height() / 2 if invert_texto: inv = PI else: inv = 0 if remove_rot: texto.scale(scale).move_to(self) else: texto.rotate( linea_referencia.get_angle()).scale(scale).move_to(self) texto.rotate(inv) if invert_dir: inv = -1 else: inv = 1 texto.shift(self.direccion * (buff + 1) * ancho * inv) return texto
def initialize_texts(self): geom_text = TextMobject("一个边长为$n$的正六边形", "和一些边长为1的菱形") eqtri_text = TextMobject("它们都是由若干正三角形组成的") three_types_text = TextMobject("因为朝向不同,菱形被分成三种") try_tiling_text = TextMobject("现在用这些菱形", "镶嵌", "正六边形...") remark = TextMobject("(无间隙且不重叠地覆盖)") claim_text = TextMobject("最终的图案中", "每种菱形的数量一定都是$n^2$") twist_text = TextMobject("改变菱形的摆放方式", "或者改变正六边形的大小", "这个结论依然成立") how_to_prove_text = TextMobject("如何证明?", "") for text in (geom_text, claim_text, twist_text): text.arrange_submobjects(DOWN, aligned_edge=LEFT) try_tiling_text[1].set_color(GREEN) remark.scale(0.5) remark.set_color(GREEN) remark.next_to(try_tiling_text[1], DOWN, buff=0.1) how_to_prove_text.set_color(YELLOW) bg_texts = VGroup( geom_text, eqtri_text, three_types_text, VGroup(try_tiling_text, remark), ) q_texts = VGroup( claim_text, twist_text, ) for texts in (bg_texts, q_texts, how_to_prove_text): texts.arrange_submobjects(DOWN, aligned_edge=LEFT, buff=1) texts.to_corner(LEFT + UP) self.bg_texts = bg_texts self.q_texts = q_texts self.how_to_prove_text = how_to_prove_text
def construct(self): quote = TextMobject("Imagination is more important than knowledge") quote.set_color(RED) quote.to_edge(UP) quote2 = TextMobject( "A person who never made a mistake never tried anything new") quote2.set_color(YELLOW) author = TextMobject("- Albert Einstein") author.scale(0.75) corner = quote.get_corner(DOWN + RIGHT) print("corner", corner) author.next_to(corner, ORIGIN) self.add(quote, author) self.wait(2) self.play( Transform(quote, quote2), ApplyMethod(author.move_to, quote2.get_corner(DOWN + RIGHT) + DOWN + 2 * LEFT)) self.play(ApplyMethod(author.scale, 1.5)) author.match_color(quote2) self.play(FadeOut(quote), FadeOut(author)) self.wait()
def add_label(self): self.id_to_label = [ "0", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og" ] self.label_to_id = {} for i, label in enumerate(self.id_to_label): self.label_to_id[label] = i labels = VGroup() for i in range(0, 119): label = TextMobject(self.id_to_label[i], color=BLACK, background_stroke_width=0) label.scale(0.5).move_to(self[0][i][1].get_center()).set_shade_in_3d(z_index_as_group=True).shift(OUT * 1e-3) labels.add(label) labels[0].set_fill(opacity=0) for i in [43, 61, *list(range(84, 119))]: labels[i].set_color(RED) self.add(labels) return self
def setup(self): MovingCameraScene.setup(self) frame = self.camera_frame frame.shift(DOWN) self.logo = Logo() name = TextMobject("3Blue1Brown") name.scale(2.5) name.next_to(self.logo, DOWN, buff=MED_LARGE_BUFF) name.set_sheen(-0.2, DR) self.channel_name = name
def setup(self): super().setup() frame = self.camera.frame frame.shift(DOWN) self.logo = Logo() name = TextMobject("3Blue1Brown") name.scale(2.5) name.next_to(self.logo, DOWN, buff=MED_LARGE_BUFF) name.set_gloss(0.2) self.channel_name = name
def setup(self): MovingCameraScene.setup(self) frame = self.camera_frame frame.shift(DOWN) self.logo = Logo() name = TextMobject("3Blue1Brown") name.scale(2.5) name.next_to(self.logo, DOWN, buff=MED_LARGE_BUFF) name.set_sheen(-0.2, DR) self.channel_name = name
def construct(self): theorem_sc = TextMobject("可利颂镶嵌定理") theorem_sc.scale(1.8) theorem_eng = TextMobject("(Calisson Tiling Theorem)") theorem_eng.set_width(theorem_sc.get_width()) theorem = VGroup(theorem_sc, theorem_eng) theorem.arrange_submobjects(DOWN) theorem.to_edge(RIGHT).shift(UP) self.play(FadeIn(theorem), run_time=1) author = TextMobject("@Solara570") author.scale(1.5) support = TextMobject("(Powered by @3Blue1Brown)") support.set_width(author.get_width()) names = VGroup(author, support) names.arrange_submobjects(DOWN) names.to_corner(RIGHT + DOWN) self.play(FadeIn(names), run_time=1) self.wait(3)
def add_remark(self): nl_text = TextMobject("数轴") nl_arrow = Arrow(ORIGIN, UP).match_height(nl_text) nl_remark = VGroup(nl_arrow, nl_text) nl_remark.scale(0.8) nl_remark.set_color(LIGHT_GREY) nl_remark.arrange_submobjects(RIGHT, buff = 0.1) nl_remark.next_to(self.axes.coords_to_point(0, 0), DOWN, buff = 0.1) nl_remark.to_edge(LEFT, buff = 0.15) frac_remark = TextMobject("圆内分数为圆心横坐标") frac_remark.scale(0.6) frac_remark.to_corner(DL, buff = 0.15) farey_sum_remark = TexMobject( "\\text{Farey Sum: }", "\\dfrac{a}{b} \\oplus \\dfrac{c}{d}", "=", "\\dfrac{a+c}{b+d}" ) farey_sum_remark[1].set_color(YELLOW) farey_sum_remark[-1].set_color(PINK) farey_sum_remark.to_corner(DR, buff = 0.15) self.add(nl_remark, frac_remark, farey_sum_remark)
def get_det_text(matrix, determinant=None, background_rect=False, initial_scale_factor=2): parens = TexMobject("(", ")") parens.scale(initial_scale_factor) parens.stretch_to_fit_height(matrix.get_height()) l_paren, r_paren = parens.split() l_paren.next_to(matrix, LEFT, buff=0.1) r_paren.next_to(matrix, RIGHT, buff=0.1) det = TextMobject("det") det.scale(initial_scale_factor) det.next_to(l_paren, LEFT, buff=0.1) if background_rect: det.add_background_rectangle() det_text = VGroup(det, l_paren, r_paren) if determinant is not None: eq = TexMobject("=") eq.next_to(r_paren, RIGHT, buff=0.1) result = TexMobject(str(determinant)) result.next_to(eq, RIGHT, buff=0.2) det_text.add(eq, result) return det_text
def get_det_text(matrix, determinant=None, background_rect=False, initial_scale_factor=2): parens = TexMobject("(", ")") parens.scale(initial_scale_factor) parens.stretch_to_fit_height(matrix.get_height()) l_paren, r_paren = parens.split() l_paren.next_to(matrix, LEFT, buff=0.1) r_paren.next_to(matrix, RIGHT, buff=0.1) det = TextMobject("det") det.scale(initial_scale_factor) det.next_to(l_paren, LEFT, buff=0.1) if background_rect: det.add_background_rectangle() det_text = VGroup(det, l_paren, r_paren) if determinant is not None: eq = TexMobject("=") eq.next_to(r_paren, RIGHT, buff=0.1) result = TexMobject(str(determinant)) result.next_to(eq, RIGHT, buff=0.2) det_text.add(eq, result) return det_text
def _main_title(self): paper_name_a = TextMobject( "Simulating human interactions in supermarkets to") paper_name_b = TextMobject( "measure the risk of COVID-19 contagion at scale") author_list_a = TextMobject( "Serge Plata\\quad Sumanas Sarma\\quad Melvin Lancelot") author_list_b = TextMobject( "Kristine Bagrova\\quad David Romano-Critchley") arxiv = TextMobject("arXiv:2006.15213") paper_name_a.shift(UP) paper_name_b.next_to(paper_name_a, DOWN) author_list_a.scale(0.8) author_list_b.scale(0.8) author_list_a.next_to(paper_name_b, DOWN + DOWN) author_list_b.next_to(author_list_a, DOWN) arxiv.scale(0.6) arxiv.next_to(author_list_b, DOWN + DOWN) self.play(FadeIn(paper_name_a), FadeIn(paper_name_b)) self.wait() self.play(FadeIn(author_list_a), FadeIn(author_list_b)) self.play(FadeIn(arxiv)) self.wait(4) self.play(FadeOut(paper_name_a), FadeOut(paper_name_b), FadeOut(author_list_a), FadeOut(author_list_b), ApplyMethod(arxiv.move_to, BOTTOM + (UP * 0.5)))
def construct(self): words = TextMobject( "3", "Blue", "1", "Brown", ) words.scale(2) words[::2].scale(1.2) buff = 0.2 words.arrange( DOWN, buff=buff, aligned_edge=LEFT, ) words[0].match_x(words[1][0]) words[2].match_x(words[3][0]) self.add(words) logo = Logo() logo.next_to(words, LEFT) self.add(logo) VGroup(logo, words).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=3, stroke_color=BLACK, 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) thanks = TextMobject(self.thanks_words) thanks.scale(0.9) thanks.next_to(black_rect.get_bottom(), UP, SMALL_BUFF) thanks.set_color(YELLOW) underline = Line(LEFT, RIGHT) underline.match_width(thanks) underline.scale(1.1) underline.next_to(thanks, DOWN, SMALL_BUFF) thanks.add(underline) changed_patron_names = map( self.modify_patron_name, self.specific_patrons, ) patrons = VGroup(*map( TextMobject, changed_patron_names, )) patrons.scale(self.patron_scale_val) for patron in patrons: if patron.get_width() > self.max_patron_width: patron.set_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( RIGHT, buff=LARGE_BUFF, aligned_edge=UP, ) max_width = FRAME_WIDTH - 1 if columns.get_width() > max_width: columns.set_width(max_width) underline.match_width(columns) # thanks.to_edge(RIGHT, buff=MED_SMALL_BUFF) columns.next_to(underline, DOWN, buff=2) columns.generate_target() columns.target.to_edge(DOWN, buff=2) vect = columns.target.get_center() - columns.get_center() distance = get_norm(vect) wait_time = 20 always_shift( columns, direction=normalize(vect), rate=(distance / wait_time) ) self.add(columns, black_rect, line, thanks) self.wait(wait_time)
def construct(self): # Add title title = self.title = TextMobject("Clicky Stuffs") title.scale(1.5) title.to_edge(UP, buff=MED_SMALL_BUFF) pi_creatures = VGroup(Randolph(), Mortimer()) for pi, vect in zip(pi_creatures, [LEFT, RIGHT]): pi.set_height(title.get_height()) pi.change_mode("thinking") pi.look(DOWN) pi.next_to(title, vect, buff=MED_LARGE_BUFF) self.add(title, pi_creatures) # Set the top of the screen logo_box = Square(side_length=2.5) logo_box.to_corner(DOWN + LEFT, buff=MED_LARGE_BUFF) black_rect = Rectangle( fill_color=BLACK, fill_opacity=1, stroke_width=3, stroke_color=BLACK, 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) # Add thanks thanks = TextMobject(self.thanks_words) thanks.scale(0.9) thanks.next_to(black_rect.get_bottom(), UP, SMALL_BUFF) thanks.set_color(YELLOW) underline = Line(LEFT, RIGHT) underline.match_width(thanks) underline.scale(1.1) underline.next_to(thanks, DOWN, SMALL_BUFF) thanks.add(underline) # Build name list file_name = os.path.join(get_directories()["data"], "patrons.txt") with open(file_name, "r") as fp: names = [ self.modify_patron_name(name.strip()) for name in fp.readlines() ] if self.randomize_order: random.shuffle(names) else: names.sort() name_labels = VGroup(*map(TextMobject, names)) name_labels.scale(self.patron_scale_val) for label in name_labels: if label.get_width() > self.max_patron_width: label.set_width(self.max_patron_width) columns = VGroup(*[ VGroup(*name_labels[i::self.n_patron_columns]) for i in range(self.n_patron_columns) ]) column_x_spacing = 0.5 + max([c.get_width() for c in columns]) for i, column in enumerate(columns): for n, name in enumerate(column): name.shift(n * self.name_y_spacing * DOWN) name.align_to(ORIGIN, LEFT) column.move_to(i * column_x_spacing * RIGHT, UL) columns.center() max_width = FRAME_WIDTH - 1 if columns.get_width() > max_width: columns.set_width(max_width) underline.match_width(columns) columns.next_to(underline, DOWN, buff=3) # Set movement columns.generate_target() distance = columns.get_height() + 2 wait_time = self.scroll_time frame = self.camera.frame frame_shift = ApplyMethod( frame.shift, distance * DOWN, run_time=wait_time, rate_func=linear, ) blink_anims = [] blank_mob = Mobject() for x in range(wait_time): if random.random() < 0.25: blink_anims.append(Blink(random.choice(pi_creatures))) else: blink_anims.append(Animation(blank_mob)) blinks = Succession(*blink_anims) static_group = VGroup(black_rect, line, thanks, pi_creatures, title) static_group.fix_in_frame() self.add(columns, static_group) self.play(frame_shift, blinks)
def construct(self): self._main_title() text_one = TextMobject("Given a list of items sold") text_two: TextMobject = TextMobject( "Randomly choose items matching this distribution") text_two.next_to(text_one, DOWN) number_line = NumberLine( numbers_with_elongated_ticks=[0, 1], include_numbers=True, x_min=0, x_max=1, unit_size=10, tick_frequency=0.1, # decimal_number_config={"num_decimal_places": 1}, numbers_to_show=[0, 1]) number_line.next_to(text_two, UP) self.play(ShowCreation(text_one)) self.wait() self.play(ShowCreation(text_two)) self.wait(4) apples_text = TextMobject("Apples:") apples_text.set_color(self._apple_colour) apples_text.to_edge(UP) apples_text.align_to(text_two, LEFT) apple_count_text = TextMobject(f"{self._apple_count}") apple_count_text.set_color(self._apple_colour) apple_count_text.next_to(apples_text, RIGHT) banana_text = TextMobject("Bananas:") banana_text.set_color(self._banana_colour) banana_text.next_to(apples_text, DOWN) banana_text.align_to(apples_text, LEFT) banana_count_text = TextMobject(f"{self._banana_count}") banana_count_text.set_color(self._banana_colour) banana_count_text.next_to(banana_text, RIGHT) self.play(Transform(text_one, apples_text)) self.play(ShowCreation(apple_count_text)) self.play(ShowCreation(banana_text), ShowCreation(banana_count_text)) banana_bar = Rectangle( height=0.4, width=number_line.point_to_number(self._banana_fraction * 10) * (number_line.number_to_point(1)[0]), color=self._banana_colour, fill_color=self._banana_colour, fill_opacity=0.75) banana_bar.next_to(banana_count_text, RIGHT + RIGHT) apple_bar = Rectangle( height=0.4, width=number_line.point_to_number(self._apple_fraction * 10) * (number_line.number_to_point(1)[0]), color=self._apple_colour, fill_color=self._apple_colour, fill_opacity=0.75) apple_bar.next_to(banana_bar, UP) apple_bar.align_to(banana_bar, LEFT) self.play(FadeIn(apple_bar), FadeIn(banana_bar)) self.wait(1.5) apple_fraction_text = TextMobject("$\\frac{" + str(self._apple_count) + "}{" + str(self._apple_count + self._banana_count) + "} = " + str(self._apple_fraction) + "$") apple_fraction_text.next_to(apple_bar, RIGHT) banana_fraction_text = TextMobject("$\\frac{" + str(self._banana_count) + "}{" + str(self._apple_count + self._banana_count) + "} = " + str(self._banana_fraction) + "$") banana_fraction_text.next_to(banana_bar, RIGHT) self.play(ShowCreation(apple_fraction_text)) self.play(ShowCreation(banana_fraction_text)) self.wait(2) number_line_map_text = TextMobject( "Map these counts to values between 0 and 1") number_line_map_text.next_to(text_two, UP) self.play(ShowCreation(number_line_map_text)) self.wait(3) self.play(Transform(number_line_map_text, number_line)) apple_num_ln_bar = Rectangle( height=0.4, # width=1 - self._apple_fraction * (number_line.number_to_point(1)[0]), width=number_line.point_to_number(self._apple_fraction * 10) * (number_line.number_to_point(1)[0]), color=self._apple_colour, fill_color=self._apple_colour, fill_opacity=0.25) apple_num_ln_bar.move_to(apple_bar, LEFT) self.add(apple_num_ln_bar) self.wait(2) self.play( ApplyMethod(apple_num_ln_bar.move_to, number_line.number_to_point(0), LEFT)) banana_num_ln_bar = Rectangle( height=0.4, width=number_line.point_to_number(self._banana_fraction * 10) * (number_line.number_to_point(1)[0]), color=self._banana_colour, fill_color=self._banana_colour, fill_opacity=0.25) banana_num_ln_bar.move_to(banana_bar, LEFT) self.add(banana_num_ln_bar) self.wait(2) self.play( ApplyMethod(banana_num_ln_bar.move_to, number_line.number_to_point(1), RIGHT)) text_scale: float = 0.75 get_rnd_full = TextMobject( "Get a random number $n$ between 0 and 1 (uniform distribution)") get_apple_text = TextMobject( f"Apple\\quad if $n <= {self._apple_fraction}$", tex_to_color_map={"Apple": self._apple_colour}) get_banana_text = TextMobject( f"Banana\\quad if $n > {self._apple_fraction}$", tex_to_color_map={"Banana": self._banana_colour}) get_rnd_full.scale(text_scale) get_rnd_full.next_to(text_two, DOWN) get_banana_text.next_to(get_apple_text, DOWN) step_group = VGroup(get_apple_text, get_banana_text) brace = Brace(step_group, LEFT) step_text_d = brace.get_text("$n \\sim U(0, 1)$") step_text_d.scale(text_scale) step_text_d.next_to(get_rnd_full, DOWN + DOWN) step_text_d.shift(LEFT) brace.next_to(step_text_d, RIGHT) step_group.scale(text_scale) step_group.next_to(step_text_d, RIGHT + RIGHT + RIGHT) self.wait(2) self.play(ShowCreation(get_rnd_full)) self.wait(2) self.play(ShowCreation(step_text_d)) self.wait(2) self.play(GrowFromCenter(brace)) self.wait() self.play(ShowCreation(get_apple_text)) self.wait(2) self.play(ShowCreation(get_banana_text)) # random_nos_to_draw = 10 # main_arrow = Arrow(ORIGIN, DOWN * 1.3) # helper_arrow = Arrow(ORIGIN, LEFT * 1.3) # # for i in range(random_nos_to_draw): # num: float = np.random.random_sample(1) # point = number_line.number_to_point(num) # arrow_colour = self._apple_colour if num <= self._apple_fraction else self._banana_colour # arrow_recipient = get_apple_text if num <= self._apple_fraction else get_banana_text # # main_arrow.set_color(arrow_colour) # # if i == 0: # main_arrow.next_to(point, UP) # helper_arrow.next_to(arrow_recipient, RIGHT) # self.play(GrowArrow(main_arrow), GrowArrow(helper_arrow)) # else: # self.play(ApplyMethod(helper_arrow.next_to, arrow_recipient, RIGHT), # ApplyMethod(main_arrow.next_to, point, UP)) # self.wait() # # self.play(FadeOut(main_arrow), FadeOut(helper_arrow)) self.wait()
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=3, stroke_color=BLACK, 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) thanks = TextMobject(self.thanks_words) thanks.scale(0.9) thanks.next_to(black_rect.get_bottom(), UP, SMALL_BUFF) thanks.set_color(YELLOW) underline = Line(LEFT, RIGHT) underline.match_width(thanks) underline.scale(1.1) underline.next_to(thanks, DOWN, SMALL_BUFF) thanks.add(underline) changed_patron_names = map( self.modify_patron_name, self.specific_patrons, ) patrons = VGroup(*map( TextMobject, changed_patron_names, )) patrons.scale(self.patron_scale_val) for patron in patrons: if patron.get_width() > self.max_patron_width: patron.set_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( RIGHT, buff=LARGE_BUFF, aligned_edge=UP, ) if columns.get_width() > self.max_patron_width: columns.set_width(total_width - 1) thanks.to_edge(RIGHT, buff=MED_SMALL_BUFF) columns.next_to(underline, DOWN, buff=2) columns.generate_target() columns.target.to_edge(DOWN, buff=2) vect = columns.target.get_center() - columns.get_center() distance = get_norm(vect) wait_time = 20 always_shift( columns, direction=normalize(vect), rate=(distance / wait_time) ) self.add(columns, black_rect, line, thanks) self.wait(wait_time)