Example #1
0
    def get_cross(self):
        cross = VGroup(Line(UP + LEFT, DOWN + RIGHT, **self.cross_kwargs),
                       Line(UP + RIGHT, DOWN + LEFT, **self.cross_kwargs))

        cross.stretch_to_fit_width(self.box.get_width())
        cross.stretch_to_fit_height(self.box.get_height())
        cross.scale(0.5)
        cross.move_to(self.box)
        return cross
Example #2
0
    def get_checkmark(self):
        checkmark = VGroup(
            Line(UP / 2 + 2 * LEFT, DOWN + LEFT, **self.checkmark_kwargs),
            Line(DOWN + LEFT, UP + RIGHT, **self.checkmark_kwargs))

        checkmark.stretch_to_fit_width(self.box.get_width())
        checkmark.stretch_to_fit_height(self.box.get_height())
        checkmark.scale(0.5)
        checkmark.move_to(self.box)
        return checkmark
Example #3
0
 def __init__(self, *args, **kwargs):
     PiCreature.__init__(self, *args, **kwargs)
     self.scale(self.scale_factor)
     self.shift(LEFT)
     self.to_edge(DOWN, buff=LARGE_BUFF)
     eyes = VGroup(self.eyes, self.pupils)
     eyes_bottom = eyes.get_bottom()
     eyes.scale(self.eye_scale_factor)
     eyes.move_to(eyes_bottom, aligned_edge=DOWN)
     looking_direction = self.get_looking_direction()
     for pupil in self.pupils:
         pupil.scale_in_place(self.pupil_scale_factor)
     self.look(looking_direction)
Example #4
0
 def __init__(self, *args, **kwargs):
     PiCreature.__init__(self, *args, **kwargs)
     self.scale(self.scale_factor)
     self.shift(LEFT)
     self.to_edge(DOWN, buff=LARGE_BUFF)
     eyes = VGroup(self.eyes, self.pupils)
     eyes_bottom = eyes.get_bottom()
     eyes.scale(self.eye_scale_factor)
     eyes.move_to(eyes_bottom, aligned_edge=DOWN)
     looking_direction = self.get_looking_direction()
     for pupil in self.pupils:
         pupil.scale_in_place(self.pupil_scale_factor)
     self.look(looking_direction)
Example #5
0
 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)
Example #6
0
class TeacherStudentsScene(PiCreatureScene):
    CONFIG = {
        "student_colors": [BLUE_D, BLUE_E, BLUE_C],
        "teacher_color": GREY_BROWN,
        "background_color": GREY_E,
        "student_scale_factor": 0.8,
        "seconds_to_blink": 2,
        "screen_height": 3,
    }

    def setup(self):
        self.background = FullScreenFadeRectangle(
            fill_color=self.background_color,
            fill_opacity=1,
        )
        self.add(self.background)
        PiCreatureScene.setup(self)
        self.screen = ScreenRectangle(height=self.screen_height)
        self.screen.to_corner(UP + LEFT)
        self.hold_up_spot = self.teacher.get_corner(UP +
                                                    LEFT) + MED_LARGE_BUFF * UP

    def create_pi_creatures(self):
        self.teacher = Mortimer(color=self.teacher_color)
        self.teacher.to_corner(DOWN + RIGHT)
        self.teacher.look(DOWN + LEFT)
        self.students = VGroup(
            *[Randolph(color=c) for c in self.student_colors])
        self.students.arrange(RIGHT)
        self.students.scale(self.student_scale_factor)
        self.students.to_corner(DOWN + LEFT)
        self.teacher.look_at(self.students[-1].eyes)
        for student in self.students:
            student.look_at(self.teacher.eyes)

        return [self.teacher] + list(self.students)

    def get_teacher(self):
        return self.teacher

    def get_students(self):
        return self.students

    def teacher_says(self, *content, **kwargs):
        return self.pi_creature_says(self.get_teacher(), *content, **kwargs)

    def student_says(self, *content, **kwargs):
        if "target_mode" not in kwargs:
            target_mode = random.choice([
                "raise_right_hand",
                "raise_left_hand",
            ])
            kwargs["target_mode"] = target_mode
        if "bubble_kwargs" not in kwargs:
            kwargs["bubble_kwargs"] = {"direction": LEFT}
        student = self.get_students()[kwargs.get("student_index", 2)]
        return self.pi_creature_says(student, *content, **kwargs)

    def teacher_thinks(self, *content, **kwargs):
        return self.pi_creature_thinks(self.get_teacher(), *content, **kwargs)

    def student_thinks(self, *content, **kwargs):
        student = self.get_students()[kwargs.get("student_index", 2)]
        return self.pi_creature_thinks(student, *content, **kwargs)

    def change_all_student_modes(self, mode, **kwargs):
        self.change_student_modes(*[mode] * len(self.students), **kwargs)

    def change_student_modes(self, *modes, **kwargs):
        added_anims = kwargs.pop("added_anims", [])
        self.play(self.get_student_changes(*modes, **kwargs), *added_anims)

    def get_student_changes(self, *modes, **kwargs):
        pairs = list(zip(self.get_students(), modes))
        pairs = [(s, m) for s, m in pairs if m is not None]
        start = VGroup(*[s for s, m in pairs])
        target = VGroup(*[s.copy().change_mode(m) for s, m in pairs])
        if "look_at_arg" in kwargs:
            for pi in target:
                pi.look_at(kwargs["look_at_arg"])
        anims = [Transform(s, t) for s, t in zip(start, target)]
        return LaggedStart(
            *anims,
            lag_ratio=kwargs.get("lag_ratio", 0.5),
            run_time=1,
        )
        # return Transform(
        #     start, target,
        #     lag_ratio=lag_ratio,
        #     run_time=2
        # )

    def zoom_in_on_thought_bubble(self,
                                  bubble=None,
                                  radius=FRAME_Y_RADIUS + FRAME_X_RADIUS):
        if bubble is None:
            for pi in self.get_pi_creatures():
                if hasattr(pi, "bubble") and isinstance(
                        pi.bubble, ThoughtBubble):
                    bubble = pi.bubble
                    break
            if bubble is None:
                raise Exception("No pi creatures have a thought bubble")
        vect = -bubble.get_bubble_center()

        def func(point):
            centered = point + vect
            return radius * centered / get_norm(centered)

        self.play(
            *
            [ApplyPointwiseFunction(func, mob) for mob in self.get_mobjects()])

    def teacher_holds_up(self,
                         mobject,
                         target_mode="raise_right_hand",
                         added_anims=None,
                         **kwargs):
        mobject.move_to(self.hold_up_spot, DOWN)
        mobject.shift_onto_screen()
        added_anims = added_anims or []
        self.play(FadeIn(mobject, shift=UP), self.teacher.change, target_mode,
                  *added_anims)
Example #7
0
    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)
Example #8
0
class CountingScene(Scene):
    CONFIG = {
        "digit_place_colors": [YELLOW, MAROON_B, RED, GREEN, BLUE, PURPLE_D],
        "counting_dot_starting_position":
        (FRAME_X_RADIUS - 1) * RIGHT + (FRAME_Y_RADIUS - 1) * UP,
        "count_dot_starting_radius":
        0.5,
        "dot_configuration_height":
        2,
        "ones_configuration_location":
        UP + 2 * RIGHT,
        "num_scale_factor":
        2,
        "num_start_location":
        2 * DOWN,
    }

    def setup(self):
        self.dots = VGroup()
        self.number = 0
        self.max_place = 0
        self.number_mob = VGroup(TexMobject(str(self.number)))
        self.number_mob.scale(self.num_scale_factor)
        self.number_mob.shift(self.num_start_location)

        self.dot_templates = []
        self.dot_template_iterators = []
        self.curr_configurations = []

        self.arrows = VGroup()

        self.add(self.number_mob)

    def get_template_configuration(self, place):
        # This should probably be replaced for non-base-10 counting scenes
        down_right = (0.5) * RIGHT + (np.sqrt(3) / 2) * DOWN
        result = []
        for down_right_steps in range(5):
            for left_steps in range(down_right_steps):
                result.append(down_right_steps * down_right +
                              left_steps * LEFT)
        return reversed(result[:self.get_place_max(place)])

    def get_dot_template(self, place):
        # This should be replaced for non-base-10 counting scenes
        dots = VGroup(*[
            Dot(
                point,
                radius=0.25,
                fill_opacity=0,
                stroke_width=2,
                stroke_color=WHITE,
            ) for point in self.get_template_configuration(place)
        ])
        dots.set_height(self.dot_configuration_height)
        return dots

    def add_configuration(self):
        new_template = self.get_dot_template(len(self.dot_templates))
        new_template.move_to(self.ones_configuration_location)
        left_vect = (new_template.get_width() + LARGE_BUFF) * LEFT
        new_template.shift(left_vect * len(self.dot_templates))
        self.dot_templates.append(new_template)
        self.dot_template_iterators.append(it.cycle(new_template))
        self.curr_configurations.append(VGroup())

    def count(self, max_val, run_time_per_anim=1):
        for x in range(max_val):
            self.increment(run_time_per_anim)

    def increment(self, run_time_per_anim=1):
        moving_dot = Dot(
            self.counting_dot_starting_position,
            radius=self.count_dot_starting_radius,
            color=self.digit_place_colors[0],
        )
        moving_dot.generate_target()
        moving_dot.set_fill(opacity=0)
        kwargs = {"run_time": run_time_per_anim}

        continue_rolling_over = True
        first_move = True
        place = 0
        while continue_rolling_over:
            added_anims = []
            if first_move:
                added_anims += self.get_digit_increment_animations()
                first_move = False
            moving_dot.target.replace(next(self.dot_template_iterators[place]))
            self.play(MoveToTarget(moving_dot), *added_anims, **kwargs)
            self.curr_configurations[place].add(moving_dot)

            if len(self.curr_configurations[place].split()
                   ) == self.get_place_max(place):
                full_configuration = self.curr_configurations[place]
                self.curr_configurations[place] = VGroup()
                place += 1
                center = full_configuration.get_center_of_mass()
                radius = 0.6 * max(
                    full_configuration.get_width(),
                    full_configuration.get_height(),
                )
                circle = Circle(
                    radius=radius,
                    stroke_width=0,
                    fill_color=self.digit_place_colors[place],
                    fill_opacity=0.5,
                )
                circle.move_to(center)
                moving_dot = VGroup(circle, full_configuration)
                moving_dot.generate_target()
                moving_dot[0].set_fill(opacity=0)
            else:
                continue_rolling_over = False

    def get_digit_increment_animations(self):
        result = []
        self.number += 1
        is_next_digit = self.is_next_digit()
        if is_next_digit:
            self.max_place += 1
        new_number_mob = self.get_number_mob(self.number)
        new_number_mob.move_to(self.number_mob, RIGHT)
        if is_next_digit:
            self.add_configuration()
            place = len(new_number_mob.split()) - 1
            result.append(FadeIn(self.dot_templates[place]))
            arrow = Arrow(new_number_mob[place].get_top(),
                          self.dot_templates[place].get_bottom(),
                          color=self.digit_place_colors[place])
            self.arrows.add(arrow)
            result.append(ShowCreation(arrow))
        result.append(Transform(self.number_mob, new_number_mob,
                                lag_ratio=0.5))
        return result

    def get_number_mob(self, num):
        result = VGroup()
        place = 0
        max_place = self.max_place
        while place < max_place:
            digit = TexMobject(str(self.get_place_num(num, place)))
            if place >= len(self.digit_place_colors):
                self.digit_place_colors += self.digit_place_colors
            digit.set_color(self.digit_place_colors[place])
            digit.scale(self.num_scale_factor)
            digit.next_to(result, LEFT, buff=SMALL_BUFF, aligned_edge=DOWN)
            result.add(digit)
            place += 1
        return result

    def is_next_digit(self):
        return False

    def get_place_num(self, num, place):
        return 0

    def get_place_max(self, place):
        return 0
Example #9
0
    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)
Example #10
0
class TeacherStudentsScene(PiCreatureScene):
    CONFIG = {
        "student_colors": [BLUE_D, BLUE_E, BLUE_C],
        "teacher_color": GREY_BROWN,
        "student_scale_factor": 0.8,
        "seconds_to_blink": 2,
        "screen_height": 3,
    }

    def setup(self):
        PiCreatureScene.setup(self)
        self.screen = ScreenRectangle(height=self.screen_height)
        self.screen.to_corner(UP + LEFT)
        self.hold_up_spot = self.teacher.get_corner(UP + LEFT) + MED_LARGE_BUFF * UP

    def create_pi_creatures(self):
        self.teacher = Mortimer(color=self.teacher_color)
        self.teacher.to_corner(DOWN + RIGHT)
        self.teacher.look(DOWN + LEFT)
        self.students = VGroup(*[
            Randolph(color=c)
            for c in self.student_colors
        ])
        self.students.arrange(RIGHT)
        self.students.scale(self.student_scale_factor)
        self.students.to_corner(DOWN + LEFT)
        self.teacher.look_at(self.students[-1].eyes)
        for student in self.students:
            student.look_at(self.teacher.eyes)

        return [self.teacher] + list(self.students)

    def get_teacher(self):
        return self.teacher

    def get_students(self):
        return self.students

    def teacher_says(self, *content, **kwargs):
        return self.pi_creature_says(
            self.get_teacher(), *content, **kwargs
        )

    def student_says(self, *content, **kwargs):
        if "target_mode" not in kwargs:
            target_mode = random.choice([
                "raise_right_hand",
                "raise_left_hand",
            ])
            kwargs["target_mode"] = target_mode
        if "bubble_kwargs" not in kwargs:
            kwargs["bubble_kwargs"] = {"direction": LEFT}
        student = self.get_students()[kwargs.get("student_index", 2)]
        return self.pi_creature_says(
            student, *content, **kwargs
        )

    def teacher_thinks(self, *content, **kwargs):
        return self.pi_creature_thinks(
            self.get_teacher(), *content, **kwargs
        )

    def student_thinks(self, *content, **kwargs):
        student = self.get_students()[kwargs.get("student_index", 2)]
        return self.pi_creature_thinks(student, *content, **kwargs)

    def change_all_student_modes(self, mode, **kwargs):
        self.change_student_modes(*[mode] * len(self.students), **kwargs)

    def change_student_modes(self, *modes, **kwargs):
        added_anims = kwargs.pop("added_anims", [])
        self.play(
            self.get_student_changes(*modes, **kwargs),
            *added_anims
        )

    def get_student_changes(self, *modes, **kwargs):
        pairs = list(zip(self.get_students(), modes))
        pairs = [(s, m) for s, m in pairs if m is not None]
        start = VGroup(*[s for s, m in pairs])
        target = VGroup(*[s.copy().change_mode(m) for s, m in pairs])
        if "look_at_arg" in kwargs:
            for pi in target:
                pi.look_at(kwargs["look_at_arg"])
        anims = [
            Transform(s, t)
            for s, t in zip(start, target)
        ]
        return LaggedStart(
            *anims,
            lag_ratio=kwargs.get("lag_ratio", 0.5),
            run_time=1,
        )
        # return Transform(
        #     start, target,
        #     lag_ratio=lag_ratio,
        #     run_time=2
        # )

    def zoom_in_on_thought_bubble(self, bubble=None, radius=FRAME_Y_RADIUS + FRAME_X_RADIUS):
        if bubble is None:
            for pi in self.get_pi_creatures():
                if hasattr(pi, "bubble") and isinstance(pi.bubble, ThoughtBubble):
                    bubble = pi.bubble
                    break
            if bubble is None:
                raise Exception("No pi creatures have a thought bubble")
        vect = -bubble.get_bubble_center()

        def func(point):
            centered = point + vect
            return radius * centered / get_norm(centered)
        self.play(*[
            ApplyPointwiseFunction(func, mob)
            for mob in self.get_mobjects()
        ])

    def teacher_holds_up(self, mobject, target_mode="raise_right_hand", added_anims=None, **kwargs):
        mobject.move_to(self.hold_up_spot, DOWN)
        mobject.shift_onto_screen()
        mobject_copy = mobject.copy()
        mobject_copy.shift(DOWN)
        mobject_copy.fade(1)
        added_anims = added_anims or []
        self.play(
            ReplacementTransform(mobject_copy, mobject),
            self.teacher.change, target_mode,
            *added_anims
        )
Example #11
0
class CountingScene(Scene):
    CONFIG = {
        "digit_place_colors": [YELLOW, MAROON_B, RED, GREEN, BLUE, PURPLE_D],
        "counting_dot_starting_position": (FRAME_X_RADIUS - 1) * RIGHT + (FRAME_Y_RADIUS - 1) * UP,
        "count_dot_starting_radius": 0.5,
        "dot_configuration_height": 2,
        "ones_configuration_location": UP + 2 * RIGHT,
        "num_scale_factor": 2,
        "num_start_location": 2 * DOWN,
    }

    def setup(self):
        self.dots = VGroup()
        self.number = 0
        self.max_place = 0
        self.number_mob = VGroup(TexMobject(str(self.number)))
        self.number_mob.scale(self.num_scale_factor)
        self.number_mob.shift(self.num_start_location)

        self.dot_templates = []
        self.dot_template_iterators = []
        self.curr_configurations = []

        self.arrows = VGroup()

        self.add(self.number_mob)

    def get_template_configuration(self, place):
        # This should probably be replaced for non-base-10 counting scenes
        down_right = (0.5) * RIGHT + (np.sqrt(3) / 2) * DOWN
        result = []
        for down_right_steps in range(5):
            for left_steps in range(down_right_steps):
                result.append(
                    down_right_steps * down_right + left_steps * LEFT
                )
        return reversed(result[:self.get_place_max(place)])

    def get_dot_template(self, place):
        # This should be replaced for non-base-10 counting scenes
        dots = VGroup(*[
            Dot(
                point,
                radius=0.25,
                fill_opacity=0,
                stroke_width=2,
                stroke_color=WHITE,
            )
            for point in self.get_template_configuration(place)
        ])
        dots.set_height(self.dot_configuration_height)
        return dots

    def add_configuration(self):
        new_template = self.get_dot_template(len(self.dot_templates))
        new_template.move_to(self.ones_configuration_location)
        left_vect = (new_template.get_width() + LARGE_BUFF) * LEFT
        new_template.shift(
            left_vect * len(self.dot_templates)
        )
        self.dot_templates.append(new_template)
        self.dot_template_iterators.append(
            it.cycle(new_template)
        )
        self.curr_configurations.append(VGroup())

    def count(self, max_val, run_time_per_anim=1):
        for x in range(max_val):
            self.increment(run_time_per_anim)

    def increment(self, run_time_per_anim=1):
        moving_dot = Dot(
            self.counting_dot_starting_position,
            radius=self.count_dot_starting_radius,
            color=self.digit_place_colors[0],
        )
        moving_dot.generate_target()
        moving_dot.set_fill(opacity=0)
        kwargs = {
            "run_time": run_time_per_anim
        }

        continue_rolling_over = True
        first_move = True
        place = 0
        while continue_rolling_over:
            added_anims = []
            if first_move:
                added_anims += self.get_digit_increment_animations()
                first_move = False
            moving_dot.target.replace(
                next(self.dot_template_iterators[place])
            )
            self.play(MoveToTarget(moving_dot), *added_anims, **kwargs)
            self.curr_configurations[place].add(moving_dot)

            if len(self.curr_configurations[place].split()) == self.get_place_max(place):
                full_configuration = self.curr_configurations[place]
                self.curr_configurations[place] = VGroup()
                place += 1
                center = full_configuration.get_center_of_mass()
                radius = 0.6 * max(
                    full_configuration.get_width(),
                    full_configuration.get_height(),
                )
                circle = Circle(
                    radius=radius,
                    stroke_width=0,
                    fill_color=self.digit_place_colors[place],
                    fill_opacity=0.5,
                )
                circle.move_to(center)
                moving_dot = VGroup(circle, full_configuration)
                moving_dot.generate_target()
                moving_dot[0].set_fill(opacity=0)
            else:
                continue_rolling_over = False

    def get_digit_increment_animations(self):
        result = []
        self.number += 1
        is_next_digit = self.is_next_digit()
        if is_next_digit:
            self.max_place += 1
        new_number_mob = self.get_number_mob(self.number)
        new_number_mob.move_to(self.number_mob, RIGHT)
        if is_next_digit:
            self.add_configuration()
            place = len(new_number_mob.split()) - 1
            result.append(FadeIn(self.dot_templates[place]))
            arrow = Arrow(
                new_number_mob[place].get_top(),
                self.dot_templates[place].get_bottom(),
                color=self.digit_place_colors[place]
            )
            self.arrows.add(arrow)
            result.append(ShowCreation(arrow))
        result.append(Transform(
            self.number_mob, new_number_mob,
            lag_ratio=0.5
        ))
        return result

    def get_number_mob(self, num):
        result = VGroup()
        place = 0
        max_place = self.max_place
        while place < max_place:
            digit = TexMobject(str(self.get_place_num(num, place)))
            if place >= len(self.digit_place_colors):
                self.digit_place_colors += self.digit_place_colors
            digit.set_color(self.digit_place_colors[place])
            digit.scale(self.num_scale_factor)
            digit.next_to(result, LEFT, buff=SMALL_BUFF, aligned_edge=DOWN)
            result.add(digit)
            place += 1
        return result

    def is_next_digit(self):
        return False

    def get_place_num(self, num, place):
        return 0

    def get_place_max(self, place):
        return 0
    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()
Example #13
0
    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)