예제 #1
0
    def construct(self):
        expr2 = self.expr.subs(e, math.e)
        f = lambdify(x, expr2, 'numpy')

        domains = continuous_domain(self.expr, x,
                                    Interval(self.x_min, self.x_max))

        if type(domains) is Union:
            domains = domains.args
        else:
            domains = [domains]

        self.setup_axes(animate=True)

        func_graph = VGroup()

        for domain in domains:
            graph = self.get_graph(f, self.function_color,
                                   get_left_bound(domain),
                                   get_right_bound(domain))
            func_graph.add(graph)

        graph_lab = self.get_graph_label(func_graph[0], label=latex(self.expr))
        self.play(ShowCreation(func_graph, run_time=3))
        self.play(ShowCreation(graph_lab))
        self.wait(5)
예제 #2
0
    def __init__(self, diagram: SequenceDiagram, title: str):
        super().__init__()
        self.diagram = diagram

        self.title = title

        tblock = VGroup()
        title = Text(title,
                     font=self.CONFIG["text_font"],
                     color=self.CONFIG["actor_text_color"])
        rect = (Rectangle(height=self._get_text_height(title) + 0.5,
                          width=title.get_width() +
                          0.5).round_corners(0.2).set_fill(
                              self.CONFIG["actor_fill_color"], 1))
        title.move_to(rect)
        tblock.add(rect, title)
        self.block = tblock

        self.line = DashedLine(
            start=rect.get_edge_center(DOWN),
            end=[rect.get_center()[0],
                 rect.get_bottom()[1], 0],
            stroke_style="dashed",
            dash_length=DEFAULT_DASH_LENGTH * 4,
            stroke_width=DEFAULT_STROKE_WIDTH / 2,
            positive_space_ratio=0.7,
        )
        self.bblock = tblock.copy()
        self.bblock.next_to(self.line, direction=DOWN, buff=0)
        self.add(tblock, self.line, self.bblock)
예제 #3
0
def test_vgroup_init(using_opengl_renderer):
    """Test the VGroup instantiation."""
    VGroup()
    VGroup(OpenGLVMobject())
    VGroup(OpenGLVMobject(), OpenGLVMobject())
    with pytest.raises(TypeError):
        VGroup(OpenGLMobject())
    with pytest.raises(TypeError):
        VGroup(OpenGLMobject(), OpenGLMobject())
예제 #4
0
def test_vgroup_init():
    """Test the VGroup instantiation."""
    VGroup()
    VGroup(VMobject())
    VGroup(VMobject(), VMobject())
    with pytest.raises(TypeError):
        VGroup(Mobject())
    with pytest.raises(TypeError):
        VGroup(Mobject(), Mobject())
예제 #5
0
def test_vgroup_item_assignment_at_correct_position(using_opengl_renderer):
    """Test VGroup item-assignment adds to correct position for OpenGLVMObjects"""
    n_items = 10
    vgroup = VGroup()
    for _i in range(n_items):
        vgroup.add(OpenGLVMobject())
    new_obj = OpenGLVMobject()
    vgroup[6] = new_obj
    assert vgroup[6] == new_obj
    assert len(vgroup) == n_items
예제 #6
0
def test_vgroup_item_assignment_at_correct_position():
    """Test VGroup item-assignment adds to correct position for VMObjects"""
    n_items = 10
    vgroup = VGroup()
    for _i in range(n_items):
        vgroup.add(VMobject())
    new_obj = VMobject()
    vgroup[6] = new_obj
    assert vgroup[6] == new_obj
    assert len(vgroup) == n_items
예제 #7
0
 def construct(self):
     data = [20, 0, 0, -5]
     x = [0, 8, 38, 39]
     self.setup_axes()
     dot_collection = VGroup()
     for time, val in enumerate(data):
         dot = Dot().move_to(self.coords_to_point(x[time], val))
         self.add(dot)
         dot_collection.add(dot)
     l1 = Line(dot_collection[0].get_center(), dot_collection[1].get_center())
     l2 = Line(dot_collection[1].get_center(), dot_collection[2].get_center())
     l3 = Line(dot_collection[2].get_center(), dot_collection[3].get_center())
     self.add(l1, l2, l3)
예제 #8
0
def test_vgroup_remove():
    """Test the VGroup remove method."""
    a = VMobject()
    c = VMobject()
    b = VGroup(c)
    obj = VGroup(a, b)
    assert len(obj.submobjects) == 2
    assert len(b.submobjects) == 1
    obj.remove(a)
    b.remove(c)
    assert len(obj.submobjects) == 1
    assert len(b.submobjects) == 0
    obj.remove(b)
    assert len(obj.submobjects) == 0
예제 #9
0
    def bordered_group(
        self,
        *children: Mobject,
        border_attrs: Optional[Dict] = None,
        title: Optional[str] = None,
        title_attrs: Optional[Dict] = None,
    ):
        group = VGroup(*children)

        width = (
            abs(max(child.get_x(RIGHT) for child in children) - min(child.get_x(LEFT) for child in children)) + 1.2
        )
        height = abs(max(child.get_y(UP) for child in children) - min(child.get_y(DOWN) for child in children)) + 1.2

        rect = Rectangle(**_merge(border_attrs, width=width, height=height))
        rect.move_to(group.get_center_of_mass())
        group.add_to_back(rect)
        if title:
            label = self.text_box(
                title, **_merge(title_attrs, bg_color=BLACK, border_color=BLACK, rounded=False, shadow=False)
            )
            label.scale(0.8)
            label.move_to(group.get_top())
            group.add(label)
        return group
예제 #10
0
def test_vgroup_remove(using_opengl_renderer):
    """Test the VGroup remove method."""
    a = OpenGLVMobject()
    c = OpenGLVMobject()
    b = VGroup(c)
    obj = VGroup(a, b)
    assert len(obj.submobjects) == 2
    assert len(b.submobjects) == 1
    obj.remove(a)
    b.remove(c)
    assert len(obj.submobjects) == 1
    assert len(b.submobjects) == 0
    obj.remove(b)
    assert len(obj.submobjects) == 0
예제 #11
0
def test_vgroup_add():
    """Test the VGroup add method."""
    obj = VGroup()
    assert len(obj.submobjects) == 0
    obj.add(VMobject())
    assert len(obj.submobjects) == 1
    with pytest.raises(TypeError):
        obj.add(Mobject())
    assert len(obj.submobjects) == 1
    with pytest.raises(TypeError):
        # If only one of the added object is not an instance of VMobject, none of them should be added
        obj.add(VMobject(), Mobject())
    assert len(obj.submobjects) == 1
    with pytest.raises(ValueError):
        # a Mobject cannot contain itself
        obj.add(obj)
예제 #12
0
def test_vgroup_add(using_opengl_renderer):
    """Test the VGroup add method."""
    obj = VGroup()
    assert len(obj.submobjects) == 0
    obj.add(OpenGLVMobject())
    assert len(obj.submobjects) == 1
    with pytest.raises(TypeError):
        obj.add(OpenGLMobject())
    assert len(obj.submobjects) == 1
    with pytest.raises(TypeError):
        # If only one of the added object is not an instance of OpenGLVMobject, none of them should be added
        obj.add(OpenGLVMobject(), OpenGLMobject())
    assert len(obj.submobjects) == 1
    with pytest.raises(ValueError):
        # a OpenGLMobject cannot contain itself
        obj.add(obj)
예제 #13
0
def test_vgroup_remove_dunder():
    """Test the VGroup __sub__ magic method."""
    a = VMobject()
    c = VMobject()
    b = VGroup(c)
    obj = VGroup(a, b)
    assert len(obj.submobjects) == 2
    assert len(b.submobjects) == 1
    assert len(obj - a) == 1
    assert len(obj.submobjects) == 2
    obj -= a
    b -= c
    assert len(obj.submobjects) == 1
    assert len(b.submobjects) == 0
    obj -= b
    assert len(obj.submobjects) == 0
예제 #14
0
def test_vgroup_supports_item_assigment():
    """Test VGroup supports array-like assignment for VMObjects"""
    a = VMobject()
    b = VMobject()
    vgroup = VGroup(a)
    assert vgroup[0] == a
    vgroup[0] = b
    assert vgroup[0] == b
    assert len(vgroup) == 1
예제 #15
0
def test_vgroup_supports_item_assigment(using_opengl_renderer):
    """Test VGroup supports array-like assignment for OpenGLVMObjects"""
    a = OpenGLVMobject()
    b = OpenGLVMobject()
    vgroup = VGroup(a)
    assert vgroup[0] == a
    vgroup[0] = b
    assert vgroup[0] == b
    assert len(vgroup) == 1
    def __init__(self,
                 icon=SVGS.ANTIBODY,
                 name='Antibody binding\nprediction',
                 **kwargs):
        super().__init__(**kwargs)
        self.icon = icon
        self.name = name
        if not isinstance(self.icon, Mobject):
            self.icon = SVGMobject(
                self.icon).set_color_by_gradient(BLUE).scale(0.6)
        if not isinstance(self.name, Mobject):
            self.name = (VGroup(
                *(Text(name)
                  for name in self.name.splitlines())).scale(0.5).arrange(
                      DOWN, center=True, buff=SMALL_BUFF))
        self.name.next_to(self.icon, DOWN)

        self.icon_name = VGroup(self.icon, self.name)
        self.frame = SurroundingRectangle(
            self.icon_name).round_corners(0.5).set_color(WHITE)

        self.add(self.frame, self.icon_name)
def hiv_antibody():
    """Returns mobjects for the hiv antibody sequence"""

    # Amino acid level - heavy and HCDR3 of the HIV antibody
    # Likely this is too complex for the purpose in this video
    heavy = [
        aa_text(line)
        for line in HIV_ANTIBODY_HEAVY.splitlines(keepends=False)[:-1]
        # N.B. for aesthetics, remove the last line
    ]
    heavy = VGroup(*heavy).scale(0.4).arrange(DOWN, center=False)
    hcdr3 = aa_text(HIB_ANTIBODY_HCDR3).scale(0.4)

    return heavy, hcdr3
예제 #18
0
    def __init__(self, source: Actor, label: str):
        super().__init__(source)
        self.label = "\n".join(wrap(label, 30))

        line_block = VGroup()

        spacing = 0.4
        distance = 0.8
        center_x = source.get_center()[0]
        line = Polygon(
            [center_x, spacing, 0],
            [center_x + distance, spacing, 0],
            [center_x + distance, -1 * spacing, 0],
            [center_x + distance / 2, -1 * spacing, 0],
            [center_x + distance, -1 * spacing, 0],
            [center_x + distance, spacing, 0],
            [center_x, spacing, 0],
            color=WHITE,
        )
        line.set_stroke(width=ARROW_STROKE_WIDTH)

        arrow = Arrow(
            start=[center_x + distance, -1 * spacing, 0],
            end=[center_x, -1 * spacing, 0],
            buff=0,
            stroke_width=ARROW_STROKE_WIDTH,
        )
        line_block.add(line, arrow)

        title = Text(self.label, font=self.source.font, size=0.5, slant=ITALIC)
        title.next_to(line_block)

        block = VGroup()
        block.add(line_block, title)
        block.next_to(source.get_center(), RIGHT)
        self.add(block)
    def construct(self):

        rng = np.random.RandomState(self.seed)

        moleculizer = partial(
            new_molecule,
            colors=lambda: rng.choice(MOLECULE_COLORS, size=3, replace=True))

        proteins = molecule_universe_grid(
            moleculizer, num_molecules=(5, 8)).scale(0.3).center()
        proteins_frame = SurroundingRectangle(proteins,
                                              color=WHITE).round_corners(0.5)
        universe_title = Text('Known proteins').scale(0.7).next_to(
            proteins_frame, UP)
        framed_proteins = VGroup(universe_title, proteins_frame,
                                 proteins).to_edge(LEFT)

        self.play(FadeIn(framed_proteins))
        self.wait(3)

        # --- Sequence -> Number -> MLM

        heavy, hcdr3 = hiv_antibody()
        heavy.next_to(framed_proteins, RIGHT)
        self.play(Transform(proteins[23], heavy))
        self.wait(5)

        mlm = MLM().next_to(framed_proteins, RIGHT)
        self.play(ShrinkToCenter(heavy))  # , mlm.text
        self.wait(5)

        # FIXME: just to break here
        if mlm.text is not None:
            return

        # --- Subsets
        selected_proteins = [
            proteins[i].copy() for i in rng.choice(
                len(proteins), replace=False, size=len(proteins) // 3)
        ]

        antibodies = VGroup(
            *
            [molecule_from_molecule(protein) for protein in selected_proteins])
        antibodies_frame = (SurroundingRectangle(antibodies).match_color(
            proteins_frame).match_width(proteins_frame).match_height(
                proteins_frame)).round_corners(0.5)
        framed_antibodies = VGroup(antibodies_frame, antibodies)

        framed_antibodies.next_to(proteins, RIGHT, buff=MED_LARGE_BUFF)
        self.play(
            *[
                ReplacementTransform(protein, antibody)
                for protein, antibody in zip(selected_proteins, antibodies)
            ], Write(antibodies_frame))
        self.wait(4)
예제 #20
0
    def _box(
        self,
        parent: VGroup,
        bg_color,
        color,
        rounded,
        shadow,
        text,
        wrap_at,
        border_color,
        text_attrs: Optional[dict],
        border_builder: Callable[[Text], Polygon],
    ) -> VGroup:
        if wrap_at:
            text = "\n".join(wrap(text, wrap_at))
        title = Text(text, font=self.text_font, color=color, **(text_attrs if text_attrs else {}))

        border = border_builder(title)
        border.set_color(border_color)
        bg_color, bg_opacity = self._color_and_opacity(bg_color, text)
        border.set_fill(color=bg_color, opacity=bg_opacity)
        if rounded:
            border.round_corners(ROUNDED_RADIUS)
        title.move_to(border)
        parent.add(border, title)
        if shadow and bg_opacity:
            s_rect = border.copy()
            s_rect.set_color(SHADOW_COLOR)
            shadow_opacity = SHADOW_OPACITY
            s_rect.set_stroke(width=0)
            s_rect.set_background_stroke(color=GREEN, opacity=shadow_opacity, width=0)
            s_rect.set_fill(opacity=shadow_opacity)
            s_rect.scale(1 + SHADOW_SHIFT)
            s_rect.shift(SHADOW_SHIFT * DR)
            parent.add_to_back(s_rect)
        return parent
예제 #21
0
    def __init__(self, target: Actor, label: str):
        super().__init__(target)
        self.target = target
        self.label = "\n".join(wrap(label, 30))

        line_block = VGroup()

        spacing = 0.4
        distance = 0.8
        line = Polygon(
            [target.get_center()[0], spacing, 0],
            [target.get_center()[0] + distance, spacing, 0],
            [target.get_center()[0] + distance, -1 * spacing, 0],
            [target.get_center()[0] + distance / 2, -1 * spacing, 0],
            [target.get_center()[0] + distance, -1 * spacing, 0],
            [target.get_center()[0] + distance, spacing, 0],
            [target.get_center()[0], spacing, 0],
            color=WHITE,
        )
        line.set_stroke(width=ARROW_STROKE_WIDTH)

        arrow = Arrow(
            start=[target.get_center()[0] + distance, -1 * spacing, 0],
            end=[target.get_center()[0], -1 * spacing, 0],
            buff=0,
            stroke_width=ARROW_STROKE_WIDTH,
        )
        line_block.add(line, arrow)

        title = Text(self.label, font="Helvetica", size=0.7, slant=ITALIC)
        title.next_to(line_block)

        block = VGroup()
        block.add(line_block, title)
        block.next_to(target.get_center(), RIGHT)
        self.add(block)
예제 #22
0
def test_vgroup_add_dunder():
    """Test the VGroup __add__ magic method."""
    obj = VGroup()
    assert len(obj.submobjects) == 0
    obj + VMobject()
    assert len(obj.submobjects) == 0
    obj += VMobject()
    assert len(obj.submobjects) == 1
    with pytest.raises(TypeError):
        obj += Mobject()
    assert len(obj.submobjects) == 1
    with pytest.raises(TypeError):
        # If only one of the added object is not an instance of VMobject, none of them should be added
        obj += (VMobject(), Mobject())
    assert len(obj.submobjects) == 1
    with pytest.raises(Exception):  # TODO change this to ValueError once #307 is merged
        # a Mobject cannot contain itself
        obj += obj
예제 #23
0
    def __init__(self, target: Actor, label: str, direction: np.array):
        super().__init__(target)
        self.target = target
        self.label = "\n".join(wrap(label, 30))
        self.direction = direction

        block = VGroup()
        title = Text(self.label, font="Helvetica").scale(0.7)
        rect = Rectangle(height=title.get_height() + 0.3,
                         width=title.get_width() + 0.3)
        title.move_to(rect)
        block.add(rect, title)
        block.next_to(target.get_center(), direction)
        self.add(block)
def molecule_universe_grid(moleculer=new_molecule,
                           num_molecules: Union[int, Tuple[int, int]] = 5):

    try:
        num_rows, num_cols = num_molecules
    except TypeError:
        num_rows = num_cols = num_molecules

    proteins = []
    prev_row = None
    for _ in range(num_rows):
        protein = moleculer()
        if prev_row is not None:
            protein.next_to(prev_row, RIGHT, buff=SMALL_BUFF)
        prev_row = protein
        proteins.append(protein)
        prev_col = protein
        for _ in range(num_cols):
            protein = moleculer()
            protein.next_to(prev_col, DOWN, buff=SMALL_BUFF)
            prev_col = protein
            proteins.append(protein)

    return VGroup(*proteins)
    def construct(self):

        a_million_forks = Text('A million forks in the road to our products'
                               )  # to drug development
        a_million_forks.to_edge(UP)

        new_drugs_per_billion_RD = [('1950', 100), ('1960', 50), ('1970', 10),
                                    ('1980', 5), ('1990', 3), ('2000', 2),
                                    ('2010', 1), ('2020', 1)]
        x = [x for x, _ in new_drugs_per_billion_RD]
        y = [y for _, y in new_drugs_per_billion_RD]
        colors = ["#003f5c", "#58508d", "#bc5090", "#ff6361", "#ffa600"]
        chart = BarChart(
            values=y,
            max_value=max(y),
            bar_colors=colors,
            bar_names=x,
            bar_label_scale_val=0.5,
        ).scale(0.8).to_edge(LEFT).shift(0.5 * DOWN)

        # text_top = (
        #     Text('EROOM\'s Law: More expensive, slower drug discovery')
        #     .scale(0.9)
        #     .next_to(chart, UP, buff=0.1)
        # )

        # text_left = (
        #     Text('Number of drugs per billion US$ R&D spending')
        #     .rotate(angle=TAU / 4, axis=OUT)
        #     .scale(0.3)
        #     .next_to(chart, LEFT, buff=0.5)
        # )

        text_top = (
            Text('Number of drugs per billion US$ R&D spending (log-scale)'
                 ).scale(0.3).next_to(chart, UP, buff=0.5))

        text_bottom = (
            Text("Eroom's law: a continuous decline in Pharma R&D productivity"
                 ).scale(0.3).next_to(chart, DOWN, buff=0.5))

        # --- Examples of forks in the road

        icons_text = [
            (SVGMobject(SVGS.ANTIBODY).set_color(BLUE).scale(0.8),
             Text('Is my antibody a potent, functional binder?')),
            (SVGMobject(SVGS.CYCLIC_PEPTIDE).set_color_by_gradient(
                BLUE, GREEN).scale(0.8),
             Text('Can we generate peptides without liabilities?')),
            (SVGMobject(SVGS.DNA).set_color_by_gradient(GREEN,
                                                        BLUE).scale(0.8),
             Text(
                 'Can we optimize DNA to better express traits in plants or humans?'
             )),
            (SVGMobject(SVGS.PACMAN).set_color(RED).scale(0.8),
             Text('What will an enzyme in the human gut do?')),
            # (
            #     SVGMobject(SVGS.PACMAN).set_color_by_gradient(GREEN, BLUE, RED).scale(0.8),
            #     Text('Can we optimize enzymes to better manufacture bioproducts?')
            # ),
            (SVGMobject(SVGS.PROTEIN3D).set_color_by_gradient(
                GREEN, BLUE, RED).scale(0.8),
             Text('What is the 3D structure of my biomolecule?')),
            (SVGMobject(SVGS.PATIENT).set_color_by_gradient(BLUE,
                                                            RED).scale(0.8),
             Text('Will a patient respond to treatment in a clinical trial?')),
            (SVGMobject(SVGS.MUTATION).set_color_by_gradient(BLUE, GREEN,
                                                             RED).scale(0.8),
             Text('...'))
        ]

        # icons_text = icons_text[:1]

        for (icon1, _), (icon2, _) in zip(icons_text, icons_text[1:]):
            icon2.next_to(icon1, DOWN, buff=0.5)

        for icon, text in icons_text:
            text.next_to(icon, RIGHT)

        questions = VGroup(*chain.from_iterable(icons_text))
        questions.scale(0.32)
        questions.next_to(chart, RIGHT, buff=1).shift(0.5 * UP)

        logo = LoLLogo().scale(0.7)
        # logo.next_to(questions, DOWN, buff=0.5)
        logo.move_to(questions)

        # --- Animate

        self.play(FadeIn(text_top))
        self.play(Write(chart), Write(text_bottom), run_time=4)
        self.wait(2)
        self.play(Write(a_million_forks))
        for icon, text in icons_text:
            self.play(FadeIn(icon, text))
            self.wait(3)

        # if we have put the logo under the questions...
        # self.play(FadeIn(logo))

        # if we prefer the logo to morph from the questions
        actionable_insights = (Text(
            'Getting actionable insights from biological sequences').next_to(
                logo, DOWN, buff=0.5).scale(0.4))
        self.play(ReplacementTransform(questions, logo))
        self.play(Write(actionable_insights))
        self.wait(2)
예제 #26
0
def test_vmobject_different_num_points_and_submobjects_become():
    a = Square()
    b = VGroup(Circle(), Square())
    a.become(b)
    np.testing.assert_array_equal(a.points, b.points)
    assert len(a.submobjects) == len(b.submobjects)
예제 #27
0
def test_vgroup_item_assignment_only_allows_vmobjects():
    """Test VGroup item-assignment raises TypeError when invalid type is passed"""
    vgroup = VGroup(VMobject())
    with pytest.raises(TypeError,
                       match="All submobjects must be of type VMobject"):
        vgroup[0] = "invalid object"
예제 #28
0
def code_line(*args, language=None):
    out = VGroup(*[DraculaCode(text=text, language=language) for text in args])
    out.arrange(direction=RIGHT)
    return out
예제 #29
0
def code_group(*args, language=None):
    out = VGroup(*[code_line(*text, language=language) for text in args])
    out.arrange(direction=DOWN, center=False)
    for elm in out:
        elm.align_to(out.get_left(), direction=LEFT)
    return out
    def construct(self):
        super().construct()

        # Group first row and second row
        use_cases = []
        for use_case1, use_case2 in zip(USE_CASES, USE_CASES[5:]):
            use_case2.next_to(use_case1, DOWN, buff=0.5)
            use_cases.append(VGroup(use_case1, use_case2))
        use_cases = VGroup(*use_cases)

        # Make all use cases same width and height
        widest = sorted(use_cases, key=lambda x: x.width)[-1]
        tallest = sorted(chain.from_iterable(use_cases),
                         key=lambda x: x.height)[-1]
        for use_case_top, use_case_bottom in use_cases:
            use_case_top.frame.stretch_to_fit_width(widest.width)
            use_case_top.frame.stretch_to_fit_height(tallest.height)
            use_case_bottom.frame.stretch_to_fit_width(widest.width)
            use_case_bottom.frame.stretch_to_fit_height(tallest.height)

        use_cases.arrange().scale(0.8).center()

        # Human friendly names for use cases

        antibodies = use_cases[0][0]
        peptides_ppi = use_cases[0][1]

        toxins = use_cases[1][0]
        peptides_sol = use_cases[1][1]

        enzyme_stabilisation = use_cases[2][0]
        microbiome = use_cases[2][1]

        codons = use_cases[3][0]
        detoxification = use_cases[3][1]

        clinical = use_cases[4][0]
        mutation = use_cases[4][1]

        # --- Animate!

        # Create the use cases
        self.play(Write(use_cases))
        self.wait(1)

        # Example setting the camera
        old_width = self.camera.frame_width
        if self.zoom_each_use_case:
            for use_case in chain.from_iterable(use_cases):
                self.play(
                    self.camera.frame.animate.move_to(use_case).set(
                        width=use_case.width * 2))
                self.wait(2)

        self.play(
            self.camera.frame.animate.move_to(ORIGIN).set(width=old_width))
        self.wait(2)

        # Highlight groups with common / synergies
        # Indications to apply to group visually, temporarily, use cases
        # https://docs.manim.community/en/stable/reference/manim.animation.indication.html

        # Similar modalities (some of these are a bit of a stretch to group together)

        if self.all_groups_at_same_time:
            self.play(
                FocusOn(antibodies),
                # Peptides
                Wiggle(peptides_sol),
                Wiggle(peptides_ppi),
                # DNA + RNA
                Indicate(codons),
                Indicate(mutation),
                Indicate(clinical),
                # Proteins
                Circumscribe(toxins.frame),
                Circumscribe(detoxification.frame),
                # Enzyme
                ApplyWave(enzyme_stabilisation),
                ApplyWave(microbiome),
                run_time=3)
            self.wait(6)