示例#1
0
def overview(scene):
    title = Text(
        """
    Manim is a Python library used to generate videos,
    and Code Video Generator extends it to make it easy
    to generate code-related videos
    ... in fact, it is what was used to generate this video!
    """,
        font="Helvetica",
        line_spacing=0.5,
    ).scale(0.7)
    scene.play(Create(title, run_time=10, rate_func=linear))
    scene.wait(3)
    sub = (Text(
        """
        Here is an example:
        """,
        font="Helvetica",
    ).scale(0.7).next_to(title,
                         direction=DOWN,
                         buff=MED_LARGE_BUFF,
                         aligned_edge=LEFT))
    scene.play(Create(sub))
    scene.wait(2)
    scene.clear()
    def __init__(self, commons_left=SVGS.DNA, commons_right=None, **kwargs):

        super().__init__(**kwargs)

        # Logo level
        lol = Text('Language of Life')
        lol[1].set_color(BLUE)
        if is_manimce(self):
            # Beware: currently semantics seem to have changed in manim ce and this should be 11
            #         are they not counting spaces?
            lol[11].set_color(GREEN)
        else:
            lol[13].set_color(GREEN)

        commons = Text('commons')
        commons.next_to(lol, DOWN)

        self.lol = lol
        self.commons = commons
        self.add(
            self.lol,
            self.commons,
        )

        self.commons_left = self.commons_right = None
        self._set_left_right(commons_left, commons_right)
    def construct(self):

        # Atom level
        adenine = Text(ADENINE_SMILES)

        # Nucleotide level
        dna = Text('ACTGAATATAGACTATA',
                   t2c={
                       'A': RED,
                       'C': BLUE,
                       'T': GREEN,
                       'G': PURPLE
                   })

        # Amino acid level
        aas = 'VQGGAAVQQEVLA'
        aas = Text(aas, t2c=get_aa_hex_color_dict('skylign_protein'))

        # Logo level
        logo = LoLLogo()

        # Animate... from nucleotide to LoL Commons
        self.wait()
        self.play(Write(adenine))
        self.wait()

        if self.add_vector_transform:
            # FIXME: This is not quite pretty / useful at the moment
            numbers = Text(
                '[0.12 0.32 0.01 0.88 0.99 0.22 0.55 0.42 0.13 0.07]')
            replacements = [adenine, dna, aas, numbers, logo.lol]
        else:
            replacements = [adenine, dna, aas, logo.lol]

        for source, target in zip(replacements, replacements[1:]):
            self.play(ReplacementTransform(source, target))
            self.wait()

        self.play(Write(logo.commons), FadeIn(logo.commons_left),
                  FadeIn(logo.commons_right))
        self.wait()
        for replacer in self.replacers:
            logo.replace_left_right(self, replacer, run_time_s=0.8)
            self.wait(0.3)

        # Move logo to left lower corner
        if self.logo_down:
            mini_logo = logo.copy()
            mini_logo.scale(0.5)
            mini_logo.to_corner()
            self.play(Transform(logo, mini_logo))

        # And keep the scene
        # self.play(Write(Text('Corporate info follows...')))
        self.wait(5)
示例#4
0
def title_scene(scene):

    scene.add_background(f"{example_dir}/resources/blackboard.jpg")
    title = Text("How to use Code Video Generator", font="Helvetica")
    scene.play(Create(title))
    scene.play(
        FadeIn(
            Text(f"Code and examples from version {__version__}",
                 font="Helvetica").scale(0.6).next_to(title,
                                                      direction=DOWN,
                                                      buff=LARGE_BUFF)))

    scene.wait(3)
    scene.clear()
示例#5
0
 def get_node(self, value) -> VDict:
     node = VDict({
         'node': Circle(radius=self.radius),
         'label': Text(value)
     })
     node['label'].scale(self.font_scale)
     return node
示例#6
0
    def connect(self, source: Mobject, target: Mobject, label: Optional[str] = None) -> Connection:

        result = Connection()
        label_direction = UP
        label_buff = 0

        arrow: Optional[Arrow] = None
        if source.get_x(RIGHT) <= target.get_x(LEFT):
            arrow = Arrow(start=source.get_edge_center(RIGHT), end=target.get_edge_center(LEFT), buff=0)
            label_direction = UP
        elif source.get_x(LEFT) >= target.get_x(RIGHT):
            arrow = Arrow(start=source.get_edge_center(LEFT), end=target.get_edge_center(RIGHT), buff=0)
            label_direction = UP
        elif source.get_y(DOWN) >= target.get_y(UP):
            arrow = Arrow(start=source.get_edge_center(DOWN), end=target.get_edge_center(UP), buff=0)
            label_direction = RIGHT
            label_buff = VERTICAL_ARROW_LABEL_BUFF
        elif source.get_y(UP) <= target.get_y(DOWN):
            arrow = Arrow(start=source.get_edge_center(UP), end=target.get_edge_center(DOWN), buff=0)
            label_direction = RIGHT
            label_buff = VERTICAL_ARROW_LABEL_BUFF

        if not arrow:
            raise ValueError("Unable to connect")

        result.add(arrow)
        if label:
            text = Text(label, font=self.text_font, size=0.7, slant=ITALIC)
            text.next_to(arrow, direction=label_direction, buff=label_buff)
            result.add(text)

        return result
示例#7
0
    def _box(
        self,
        text,
        border_builder: Callable[[Text], Polygon],
    ):

        if self.wrap_at:
            text = "\n".join(wrap(text, self.wrap_at))
        title = Text(text, **self.text_attrs)

        border = border_builder(title)
        border.set_color(self.border_color)
        bg_color, bg_opacity = self._color_and_opacity(self.bg_color, text)
        border.set_fill(color=bg_color, opacity=bg_opacity)
        if self.rounded:
            border.round_corners(ROUNDED_RADIUS)
        title.move_to(border)
        self.add(border, title)
        if self.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(width=0)
            s_rect.set_fill(opacity=shadow_opacity)
            s_rect.scale(1 + SHADOW_SHIFT)
            s_rect.shift(SHADOW_SHIFT * DR)
            self.add_to_back(s_rect)
示例#8
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)
示例#9
0
def demo_boxes(scene: CodeScene):
    scene.add_background(f"{example_dir}/resources/blackboard.jpg")

    title = Text("examples/boxes.py", font=DEFAULT_FONT)
    title.to_edge(UP)
    scene.add(title)
    comp1 = TextBox("Component A", shadow=False)
    comp2 = TextBox("Component B", shadow=False)
    comp3 = TextBox("Component C", shadow=False)
    comp1.next_to(title, DOWN, buff=2)
    comp1.to_edge(LEFT)
    comp2.next_to(comp1, DOWN, buff=1)
    comp3.next_to(comp1, RIGHT, buff=4)
    arrow1 = Connection(comp2, comp1, "Do something")
    arrow2 = Connection(comp1, comp3, "Do another thing")

    scene.play(FadeIn(comp2))
    scene.wait_until_beat(1)
    scene.play(Create(arrow1))
    scene.play(FadeIn(comp1))
    scene.wait_until_beat(1)
    scene.play(Create(arrow2))
    scene.play(FadeIn(comp3))

    scene.wait_until_beat(4)
    scene.clear()
示例#10
0
def test_whether_svg_file_created() -> None:
    """Checks Whether SVG file is created in desired location"""
    temp_pango_text = Text("hello", size=1)
    theo_path = os.path.abspath(
        os.path.join(folder,
                     temp_pango_text.text2hash() + ".svg"))
    actual_path = os.path.abspath(temp_pango_text.text2svg())
    assert theo_path == actual_path
    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)
示例#12
0
def test_graph_add_vertices():
    G = Graph([1, 2, 3], [(1, 2), (2, 3)])
    G.add_vertices(4)
    assert str(G) == "Graph on 4 vertices and 2 edges"
    G.add_vertices(5, labels={5: Text("5")})
    assert str(G) == "Graph on 5 vertices and 2 edges"
    assert 5 in G._labels
    assert 5 in G._vertex_config
    G.add_vertices(6, 7, 8)
    assert len(G.vertices) == 8
    assert len(G._graph.nodes()) == 8
示例#13
0
    def finish(self, target: Actor):
        self.target = target

        line = Arrow(start=[self.source.get_center()[0], 0, 0],
                     end=[self.target.get_center()[0], 0, 0],
                     buff=0)
        self.add(line)
        text = Text(self.label,
                    font=self.source.CONFIG["text_font"]).scale(0.7).next_to(
                        line, direction=UP, buff=0)
        self.add(text)
示例#14
0
def demo_sequence(scene: CodeScene):
    title = Text(
        """
        You can use Code Video Generator to also illustrate
        high-level concepts through sequence diagrams, or
        if you want more control, your own block diagrams:
        """,
        font=DEFAULT_FONT,
        line_spacing=0.5,
    ).scale(0.7)
    scene.play(Create(title, run_time=4, rate_func=linear))
    scene.wait(3)
    scene.clear()

    scene.add_background(f"{example_dir}/resources/blackboard.jpg")

    title = Text("examples/sequence-diagrams.py", font=DEFAULT_FONT)
    title.to_edge(UP)
    scene.add(title)

    diagram = AutoScaled(SequenceDiagram())
    browser, web, app = diagram.add_objects("Browser", "Web", "App")

    browser.to(web, "Make a request")
    web.to(app, "Request with no response")
    web.to(app, "Retrieve a json object")
    app.to(app, "Calls itself")
    app.note("Do lots and lots and lots of thinking")
    app.to(web, "Value from db")
    web.to(browser, "HTML response")

    diagram.next_to(title, DOWN)
    scene.play(Create(diagram))
    for interaction in diagram.get_interactions():
        scene.play(Create(interaction))
    scene.wait(3)
    scene.play(FadeOut(diagram),
               *[FadeOut(item) for item in diagram.interactions])
    scene.clear()
示例#15
0
def test_graph_add_vertices(using_opengl_renderer):
    G = Graph([1, 2, 3], [(1, 2), (2, 3)])
    G.add_vertices(4)
    assert len(G.vertices) == 4
    assert len(G.edges) == 2
    G.add_vertices(5, labels={5: Text("5")})
    assert len(G.vertices) == 5
    assert len(G.edges) == 2
    assert 5 in G._labels
    assert 5 in G._vertex_config
    G.add_vertices(6, 7, 8)
    assert len(G.vertices) == 8
    assert len(G._graph.nodes()) == 8
示例#16
0
    def animate_code_comments(
        self,
        path: str,
        title: str = None,
        keep_comments: bool = False,
        start_line: int = 1,
        end_line: Optional[int] = None,
        reset_at_end: bool = True,
    ) -> Code:
        """
        Parses a code file, displays it or a section of it, and animates comments

        Args:
            path: The source code file path
            title: The title or file path if not provided
            keep_comments: Whether to keep comments or strip them when displaying
            start_line: The start line number, used for displaying only a partial file
            end_line: The end line number, defaults to the end of the file
            reset_at_end: Whether to reset the code to full screen at the end or not
        """
        code, comments = comment_parser.parse(path,
                                              keep_comments=keep_comments,
                                              start_line=start_line,
                                              end_line=end_line)

        tex = AutoScaled(
            PartialCode(code=code,
                        start_line=start_line,
                        style=self.code_theme))
        if title is None:
            title = path

        title = Text(title, color=WHITE).to_edge(edge=UP)
        self.add(title)
        tex.next_to(title, DOWN)

        self.play(ShowCreation(tex))
        self.wait()

        for comment in comments:
            self.highlight_lines(tex, comment.start, comment.end,
                                 comment.caption)

        if self.caption:
            self.play(FadeOut(self.caption))
            self.caption = None

        if reset_at_end:
            self.play(HighlightNone(tex))
            self.play(ApplyMethod(tex.full_size))
        return tex
示例#17
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)
示例#18
0
    def __init__(self,
                 source: Mobject,
                 target: Mobject,
                 label: Optional[str] = None,
                 font=DEFAULT_FONT,
                 **kwargs):
        """
        Args:
            source: The source object
            target: The target object
            label: The optional label text to put over the arrow
        """
        super().__init__(**kwargs)
        self.font = font
        label_direction = UP
        label_buff = 0

        arrow: Optional[Arrow] = None
        if source.get_x(RIGHT) <= target.get_x(LEFT):
            arrow = Arrow(start=source.get_edge_center(RIGHT),
                          end=target.get_edge_center(LEFT),
                          buff=0)
            label_direction = UP
        elif source.get_x(LEFT) >= target.get_x(RIGHT):
            arrow = Arrow(start=source.get_edge_center(LEFT),
                          end=target.get_edge_center(RIGHT),
                          buff=0)
            label_direction = UP
        elif source.get_y(DOWN) >= target.get_y(UP):
            arrow = Arrow(start=source.get_edge_center(DOWN),
                          end=target.get_edge_center(UP),
                          buff=0)
            label_direction = RIGHT
            label_buff = VERTICAL_ARROW_LABEL_BUFF
        elif source.get_y(UP) <= target.get_y(DOWN):
            arrow = Arrow(start=source.get_edge_center(UP),
                          end=target.get_edge_center(DOWN),
                          buff=0)
            label_direction = RIGHT
            label_buff = VERTICAL_ARROW_LABEL_BUFF

        if not arrow:
            raise ValueError("Unable to connect")

        self.add(arrow)
        if label:
            text = Text(label, font=self.font, size=0.5, slant=ITALIC)
            text.next_to(arrow, direction=label_direction, buff=label_buff)
            self.add(text)
    def __init__(self, source: Actor, target: Actor, label: str = "", font=DEFAULT_FONT, **kwargs):
        super().__init__(source, font=font, **kwargs)
        self.target = target
        self.label = label
        self.font = font

        line = Arrow(
            start=[self.source.get_center()[0], 0, 0],
            end=[self.target.get_center()[0], 0, 0],
            buff=0,
            stroke_width=ARROW_STROKE_WIDTH,
        )
        text = Text(self.label, font=self.font, size=0.5, slant=ITALIC)
        text.next_to(line, direction=UP, buff=0)
        self.add(line, text)
示例#20
0
def goodbye(scene: CodeScene):
    title = Text(
        """
        Try Code Video Generator today at:

          https://github.com/sleuth-io/code-video-generator

        Thanks for watching!""",
        font="Helvetica",
        line_spacing=0.5,
    ).scale(0.7)
    title.to_edge(LEFT)
    scene.play(Create(title, run_time=4, rate_func=linear))
    scene.wait(5)
    scene.play(FadeOut(title))
示例#21
0
def test_t2s() -> None:
    size = 1
    temp_pango_text = Text("Helloworld", t2s={"world": ITALIC})
    surface = cairocffi.SVGSurface(filename, WIDTH, HEIGTH)
    context = cairocffi.Context(surface)
    context.move_to(START_X, START_Y)
    layout = pangocairocffi.create_layout(context)
    layout.set_width(pangocffi.units_from_double(WIDTH))
    fontdesc = pangocffi.FontDescription()
    fontdesc.set_size(pangocffi.units_from_double(size * 10))
    layout.set_font_description(fontdesc)
    layout.set_markup(
        'Hello<span style="italic">world</span>')  # yay, pango markup
    pangocairocffi.show_layout(context, layout)
    surface.finish()
    assert compare_SVGObject_with_PangoText(temp_pango_text, filename)
示例#22
0
    def finish(self, target: Actor):
        self.target = target

        line = Arrow(
            start=[self.source.get_center()[0], 0, 0],
            end=[self.target.get_center()[0], 0, 0],
            buff=0,
            stroke_width=ARROW_STROKE_WIDTH,
        )
        text = Text(self.label,
                    font=self.source.diagram.lib.text_font,
                    size=0.7,
                    slant=ITALIC)
        text.next_to(line, direction=UP, buff=0)
        self.add(line, text)
        return self
示例#23
0
def test_rtl_text_to_svgobject() -> None:
    """Checks number of submobjects generated when directly
    called using ``SVGMobject``"""
    size = 1
    text = RTL_TEXT.replace("\n", "")
    temp_pango_text = Text(text, size=1)
    surface = cairocffi.SVGSurface(filename, WIDTH, HEIGTH)
    context = cairocffi.Context(surface)
    context.move_to(START_X, START_Y)
    layout = pangocairocffi.create_layout(context)
    layout.set_width(pangocffi.units_from_double(WIDTH))
    fontdesc = pangocffi.FontDescription()
    fontdesc.set_size(pangocffi.units_from_double(size * 10))
    layout.set_font_description(fontdesc)
    layout.set_text(text)
    pangocairocffi.show_layout(context, layout)
    surface.finish()
    assert compare_SVGObject_with_PangoText(temp_pango_text, filename)
示例#24
0
def test_tabs_replace() -> None:
    """Checks whether are there in end svg image.
    Pango should handle tabs and line breaks."""
    size = 1
    temp_pango_text = Text("hello\thi\nf")
    assert temp_pango_text.text == "hellohif"
    surface = cairocffi.SVGSurface(filename, WIDTH, HEIGTH)
    context = cairocffi.Context(surface)
    context.move_to(START_X, START_Y)
    layout = pangocairocffi.create_layout(context)
    layout.set_width(pangocffi.units_from_double(WIDTH))
    fontdesc = pangocffi.FontDescription()
    fontdesc.set_size(pangocffi.units_from_double(size * 10))
    layout.set_font_description(fontdesc)
    layout.set_text("hellohif")
    pangocairocffi.show_layout(context, layout)
    surface.finish()
    assert compare_SVGObject_with_PangoText(temp_pango_text, filename)
示例#25
0
def test_font_face() -> None:
    """Checks font face using submobject len"""
    size = 1
    text = RTL_TEXT.replace("\n", "")
    font_face = "sans"
    temp_pango_text = Text(text, size=1, font=font_face)
    surface = cairocffi.SVGSurface(filename, WIDTH, HEIGTH)
    context = cairocffi.Context(surface)
    context.move_to(START_X, START_Y)
    layout = pangocairocffi.create_layout(context)
    layout.set_width(pangocffi.units_from_double(WIDTH))
    fontdesc = pangocffi.FontDescription()
    fontdesc.set_family(font_face)
    fontdesc.set_size(pangocffi.units_from_double(size * 10))
    layout.set_font_description(fontdesc)
    layout.set_text(text)
    pangocairocffi.show_layout(context, layout)
    surface.finish()
    assert compare_SVGObject_with_PangoText(temp_pango_text, filename)
    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)
示例#27
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)
示例#28
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
    def construct(self):
        example_dir = dirname(__file__)
        self.add_background(f"{example_dir}/resources/blackboard.jpg")
        diagram = AutoScaled(SequenceDiagram())
        browser, web, app = diagram.add_objects("Browser", "Web", "App")

        browser.to(web, "Make a request")
        web.to(app, "Do a quick thing")
        web.to(app, "Retrieve a json object")
        app.to(app, "Call itself")
        app.note("Do lots and lots and lots of thinking")
        app.to(web, "Value from db")
        web.to(browser, "HTML response")

        title = Text("Sequence Diagram", font=DEFAULT_FONT, size=0.8)
        title.to_edge(UP)
        self.add(title)
        diagram.next_to(title, DOWN)

        self.play(ShowCreation(diagram))
        for interaction in diagram.get_interactions():
            self.play(ShowCreation(interaction))

        self.wait(5)
    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)