Ejemplo n.º 1
0
    def __init__(
        self,
        reactants: List[ChemObject] = [],
        products: List[ChemObject] = [],
        **arrow_kwargs,
    ):
        raise DeprecationWarning(
            "ReactionVGroup doesn't work, and will probably be removed in the future"
        )
        digest_config(self, arrow_kwargs)
        arrow = ChemArrow(
            self._type,
            self.length,
            self.angle,
            self.style,
            self.text_up,
            self.text_down,
        )

        r = self.get_side_of_equation(reactants)
        p = self.get_side_of_equation(products)

        self.set_same_height_for_all_mobjects(r)
        self.set_same_height_for_all_mobjects(p)
        self.set_same_height_for_all_mobjects([VGroup(*r), arrow, VGroup(*p)])

        VGroup.__init__(*[*r, arrow, *p])
Ejemplo n.º 2
0
    def get_subdivision_braces_and_labels(
        self,
        parts,
        labels,
        direction,
        buff=SMALL_BUFF,
        min_num_quads=1,
    ):
        label_mobs = VGroup()
        braces = VGroup()
        for label, part in zip(labels, parts):
            brace = Brace(part, direction, min_num_quads=min_num_quads, buff=buff)
            if isinstance(label, (Mobject, OpenGLMobject)):
                label_mob = label
            else:
                label_mob = MathTex(label)
                label_mob.scale(self.default_label_scale_val)
            label_mob.next_to(brace, direction, buff)

            braces.add(brace)
            label_mobs.add(label_mob)
        parts.braces = braces
        parts.labels = label_mobs
        parts.label_kwargs = {
            "labels": label_mobs.copy(),
            "direction": direction,
            "buff": buff,
        }
        return VGroup(parts.braces, parts.labels)
Ejemplo n.º 3
0
    def __init__(self, tabledict,
                 **kwargs):  #__init__ is called everytime Table() is called.

        for item in kwargs:  #Add everything that has been given in kwargs to the config.
            self.CONFIG[item] = kwargs[
                item]  #Using digest config gave AttributeError: 'dict' object has no attribute '__dict__'

        if not self.CONFIG["tabledict"]:
            self.tabledict: dict = tabledict
        self.buff_length: Union[float, int] = self.CONFIG["buff_length"]
        self.vbuff_length = self.buff_length if self.CONFIG[
            "vbuff_length"] is None else self.CONFIG["vbuff_length"]
        self.hbuff_length = self.buff_length if self.CONFIG[
            "hbuff_length"] is None else self.CONFIG["hbuff_length"]
        self.line_color: Union[str, hex] = self.CONFIG["line_color"]
        self.raw_string_color: Union[str,
                                     hex] = self.CONFIG["raw_string_color"]
        self.unchanged: bool = True
        # self.cell_height
        # self.cell_length

        VGroup.__init__(self)  #Initialise Table as VGroup

        self.make_table()  #Make the table with the parameters in CONFIG
        self.move_to(ORIGIN)
Ejemplo n.º 4
0
    def _add_x_axis_labels(self):
        """Essentially ``:meth:~.NumberLine.add_labels``, but differs in that
        the direction of the label with respect to the x_axis changes to UP or DOWN
        depending on the value.

        UP for negative values and DOWN for positive values.
        """

        val_range = np.arange(
            0.5, len(self.bar_names), 1
        )  # 0.5 shifted so that labels are centered, not on ticks

        labels = VGroup()

        for i, (value, bar_name) in enumerate(zip(val_range, self.bar_names)):
            # to accommodate negative bars, the label may need to be
            # below or above the x_axis depending on the value of the bar
            if self.values[i] < 0:
                direction = UP
            else:
                direction = DOWN
            bar_name_label = self.x_axis.label_constructor(bar_name)

            bar_name_label.font_size = self.x_axis.font_size
            bar_name_label.next_to(
                self.x_axis.number_to_point(value),
                direction=direction,
                buff=self.x_axis.line_to_number_buff,
            )

            labels.add(bar_name_label)

        self.x_axis.labels = labels
        self.x_axis.add(labels)
Ejemplo n.º 5
0
 def __init__(self, shell_config=None, cloud_radius=None):
     VGroup.__init__(self)
     if not shell_config:
         self.shell_config = self.CONFIG["shell_config"]
     if not cloud_radius:
         self.cloud_radius = self.CONFIG["cloud_radius"]
     self.make_atom()
     self.move_to(ORIGIN)
Ejemplo n.º 6
0
 def create_faces(
     self,
     face_coords: list[list[list | np.ndarray]],
 ) -> VGroup:
     """Creates VGroup of faces from a list of face coordinates."""
     face_group = VGroup()
     for face in face_coords:
         face_group.add(Polygon(*face, **self.faces_config))
     return face_group
Ejemplo n.º 7
0
 def add_ticks(self):
     """Adds ticks to the number line. Ticks can be accessed after creation
     via ``self.ticks``."""
     ticks = VGroup()
     elongated_tick_size = self.tick_size * self.longer_tick_multiple
     for x in self.get_tick_range():
         size = self.tick_size
         if x in self.numbers_with_elongated_ticks:
             size = elongated_tick_size
         ticks.add(self.get_tick(x, size))
     self.add(ticks)
     self.ticks = ticks
Ejemplo n.º 8
0
    def add_numbers(
        self,
        x_values: Iterable[float] | None = None,
        excluding: Iterable[float] | None = None,
        font_size: float | None = None,
        label_constructor: VMobject | None = None,
        **kwargs,
    ):
        """Adds :class:`~.DecimalNumber` mobjects representing their position
        at each tick of the number line. The numbers can be accessed after creation
        via ``self.numbers``.

        Parameters
        ----------
        x_values
            An iterable of the values used to position and create the labels.
            Defaults to the output produced by :meth:`~.NumberLine.get_tick_range`
        excluding
            A list of values to exclude from :attr:`x_values`.
        font_size
            The font size of the labels. Defaults to the ``font_size`` attribute
            of the number line.
        label_constructor
            The :class:`~.VMobject` class that will be used to construct the label.
            Defaults to the ``label_constructor`` attribute of the number line
            if not specified.
        """
        if x_values is None:
            x_values = self.get_tick_range()

        if excluding is None:
            excluding = self.numbers_to_exclude

        if font_size is None:
            font_size = self.font_size

        if label_constructor is None:
            label_constructor = self.label_constructor

        numbers = VGroup()
        for x in x_values:
            if x in excluding:
                continue
            numbers.add(
                self.get_number_mobject(
                    x,
                    font_size=font_size,
                    label_constructor=label_constructor,
                    **kwargs,
                ))
        self.add(numbers)
        self.numbers = numbers
        return self
Ejemplo n.º 9
0
    def get_bar_labels(
        self,
        color: Color | None = None,
        font_size: float = 24,
        buff: float = MED_SMALL_BUFF,
        label_constructor: VMobject = Tex,
    ):
        """Annotates each bar with its corresponding value. Use ``self.bar_labels`` to access the
        labels after creation.

        Parameters
        ----------
        color
            The color of each label. By default ``None`` and is based on the parent's bar color.
        font_size
            The font size of each label.
        buff
            The distance from each label to its bar. By default 0.4.
        label_constructor
            The Mobject class to construct the labels, by default :class:`~.Tex`.

        Examples
        --------
        .. manim:: GetBarLabelsExample
            :save_last_frame:

            class GetBarLabelsExample(Scene):
                def construct(self):
                    chart = BarChart(values=[10, 9, 8, 7, 6, 5, 4, 3, 2, 1], y_range=[0, 10, 1])

                    c_bar_lbls = chart.get_bar_labels(
                        color=WHITE, label_constructor=MathTex, font_size=36
                    )

                    self.add(chart, c_bar_lbls)
        """

        bar_labels = VGroup()
        for bar, value in zip(self.bars, self.values):
            bar_lbl = label_constructor(str(value))

            if color is None:
                bar_lbl.set_color(bar.get_fill_color())
            else:
                bar_lbl.set_color(color)

            bar_lbl.font_size = font_size

            pos = UP if (value >= 0) else DOWN
            bar_lbl.next_to(bar, pos, buff=buff)
            bar_labels.add(bar_lbl)

        return bar_labels
Ejemplo n.º 10
0
 def _setup_in_uv_space(self):
     u_values, v_values = self._get_u_values_and_v_values()
     faces = VGroup()
     for i in range(len(u_values) - 1):
         for j in range(len(v_values) - 1):
             u1, u2 = u_values[i : i + 2]
             v1, v2 = v_values[j : j + 2]
             face = ThreeDVMobject()
             face.set_points_as_corners(
                 [
                     [u1, v1, 0],
                     [u2, v1, 0],
                     [u2, v2, 0],
                     [u1, v2, 0],
                     [u1, v1, 0],
                 ],
             )
             faces.add(face)
             face.u_index = i
             face.v_index = j
             face.u1 = u1
             face.u2 = u2
             face.v1 = v1
             face.v2 = v2
     faces.set_fill(color=self.fill_color, opacity=self.fill_opacity)
     faces.set_stroke(
         color=self.stroke_color,
         width=self.stroke_width,
         opacity=self.stroke_opacity,
     )
     self.add(*faces)
     if self.checkerboard_colors:
         self.set_fill_by_checkerboard(*self.checkerboard_colors)
Ejemplo n.º 11
0
    def get_division_along_dimension(self, p_list, dim, colors, vect):
        p_list = self.complete_p_list(p_list)
        colors = color_gradient(colors, len(p_list))

        last_point = self.get_edge_center(-vect)
        parts = VGroup()
        for factor, color in zip(p_list, colors):
            part = SampleSpace()
            part.set_fill(color, 1)
            part.replace(self, stretch=True)
            part.stretch(factor, dim)
            part.move_to(last_point, -vect)
            last_point = part.get_edge_center(vect)
            parts.add(part)
        return parts
Ejemplo n.º 12
0
    def create_creatures(self):
        self.teacher = Alex(color=self.teacher_color).scale(SCALE_FACTOR)
        self.teacher.to_corner(DR)
        self.students = VGroup(*[
            Alex(color=c).scale(SCALE_FACTOR)
            for c in self.student_colors
        ])
        self.students.arrange(RIGHT, buff=0.5)
        # self.students.arrange(RIGHT)
        self.students.scale(self.student_scale_factor)
        self.students.to_corner(DL)
        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)
Ejemplo n.º 13
0
 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 pencil in target:
             pencil.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,
     )
Ejemplo n.º 14
0
    def make_atom(self):

        electron_dict = {
        }  #This holds all the electrons in the format {"shell_n":electron}

        for shell in self.shell_config:
            electron_dict[shell] = VGroup()
            tempVGroup = VGroup()
            if self.shell_config[shell]["electron_count"] == 0:
                continue
            else:
                for electron_coordinate in self.get_electron_coordinates_list(
                        self.shell_config[shell]["electron_count"]
                ):  #for each electron coordinate in get_electron_coordinates where the radius of the inner circle is 1 and electron_count electrons are needed:
                    electron = shading.get_surface(
                        surface=Sphere(radius=0.08),
                        fill_color=self.shell_config[shell]["colour"],
                        fill_opacity=1)
                    electron.move_to(electron_coordinate)
                    electron_dict[shell].add(electron)
            tempVGroup = VGroup(*electron_dict[shell])
            tempVGroup.space_out_submobjects(
                self.shell_config[shell]["radius"])

        positivecloud = shading.get_surface(surface=Sphere(radius=2),
                                            fill_color=DARK_BLUE)

        # atom=VGroup(*electron_dict["shell_1"],*electron_dict["shell_2"],positivecloud)
        self.add(positivecloud)
        for shell in electron_dict:
            self.add(electron_dict[shell])
Ejemplo n.º 15
0
    def __init__(
        self,
        color: Color = WHITE,
        height: float = 2.0,
        width: float = 4.0,
        grid_xstep: float | None = None,
        grid_ystep: float | None = None,
        mark_paths_closed=True,
        close_new_points=True,
        **kwargs,
    ):
        super().__init__(UR, UL, DL, DR, color=color, **kwargs)
        self.stretch_to_fit_width(width)
        self.stretch_to_fit_height(height)
        v = self.get_vertices()
        if grid_xstep is not None:
            from manim.mobject.geometry.line import Line

            grid_xstep = abs(grid_xstep)
            count = int(width / grid_xstep)
            grid = VGroup(
                *(
                    Line(
                        v[1] + i * grid_xstep * RIGHT,
                        v[1] + i * grid_xstep * RIGHT + height * DOWN,
                        color=color,
                    )
                    for i in range(1, count)
                )
            )
            self.add(grid)
        if grid_ystep is not None:
            grid_ystep = abs(grid_ystep)
            count = int(height / grid_ystep)
            grid = VGroup(
                *(
                    Line(
                        v[1] + i * grid_ystep * DOWN,
                        v[1] + i * grid_ystep * DOWN + width * RIGHT,
                        color=color,
                    )
                    for i in range(1, count)
                )
            )
            self.add(grid)
Ejemplo n.º 16
0
    def get_parts_by_tex(self, tex, substring=True, case_sensitive=True):
        def test(tex1, tex2):
            if not case_sensitive:
                tex1 = tex1.lower()
                tex2 = tex2.lower()
            if substring:
                return tex1 in tex2
            else:
                return tex1 == tex2

        return VGroup(*(m for m in self.submobjects if test(tex, m.get_tex_string())))
Ejemplo n.º 17
0
    def _add_bars(self):
        self.bars = VGroup()

        for i, value in enumerate(self.values):
            bar_h = abs(self.c2p(0, value)[1] - self.c2p(0, 0)[1])
            bar_w = self.c2p(self.bar_width, 0)[0] - self.c2p(0, 0)[0]
            bar = Rectangle(
                height=bar_h,
                width=bar_w,
                stroke_width=self.bar_stroke_width,
                fill_opacity=self.bar_fill_opacity,
            )

            pos = UP if (value >= 0) else DOWN
            bar.next_to(self.c2p(i + 0.5, 0), pos, buff=0)
            self.bars.add(bar)
        if isinstance(self.bar_colors, str):
            self.bars.set_color_by_gradient(self.bar_colors)
        else:
            self.bars.set_color_by_gradient(*self.bar_colors)

        self.add_to_back(self.bars)
Ejemplo n.º 18
0
    def get_lines(self) -> VGroup:
        """Get the lines forming an angle of the :class:`Angle` class.

        Returns
        -------
        :class:`~.VGroup`
            A :class:`~.VGroup` containing the lines that form the angle of the :class:`Angle` class.

        Examples
        --------
        ::

            >>> line_1, line_2 = Line(ORIGIN, RIGHT), Line(ORIGIN, UR)
            >>> angle = Angle(line_1, line_2)
            >>> angle.get_lines()
            VGroup(Line, Line)
        """

        return VGroup(*self.lines)
Ejemplo n.º 19
0
    def creation_anim(
        self,
        text_anim: Animation = Write,
        arrow_anim: Animation = FadeInFrom,
        reactant_product_simultaneity=False,
        **kwargs,
    ) -> AnimationGroup:
        """Workaround and shortcut method to overcome the bugs in `Write`.

        Args:
            text_anim (Animation, optional): The animation on the reactants and products. Defaults to Write.
            arrow_anim (Animation, optional): The animation on the arrow. Defaults to FadeInFrom.
            reactant_product_simultaneity (bool, optional): Whether to animate the reactants and products together or not.
        Returns:
            AnimationGroup: The group of animations on the text and arrow.
        """
        text = VGroup(self[0:2 * len(self.reactants) - 1],
                      self[2 * len(self.reactants):])
        arrow = self[2 * len(self.reactants) - 1]

        if "text_kwargs" not in kwargs.keys():
            kwargs["text_kwargs"] = dict()

        if "arrow_kwargs" not in kwargs.keys():
            kwargs["arrow_kwargs"] = dict()

        if "group_kwargs" not in kwargs.keys():
            kwargs["group_kwargs"] = dict()

        print(kwargs["group_kwargs"])

        anim_group = (AnimationGroup(text_anim(text[0]), text_anim(text[1]),
                                     arrow_anim(arrow), **kwargs)
                      if reactant_product_simultaneity else AnimationGroup(
                          text_anim(text), arrow_anim(arrow), **kwargs))

        try:
            print(anim_group.run_time)
        except Exception:
            pass
        return anim_group
Ejemplo n.º 20
0
    def setup(
        self,
        creatures_start_on_screen = True,
        default_creature_start_corner = DR,
        default_creature_kwargs= {
            "color": GREY_BROWN,
            "flip_at_start": True,
        },
        total_wait_time  = 0,
        seconds_to_blink  = 3,
    ):
        self.creatures_start_on_screen = creatures_start_on_screen
        self.default_creature_start_corner = default_creature_start_corner
        self.default_creature_kwargs = default_creature_kwargs
        self.total_wait_time = total_wait_time
        self.seconds_to_blink = seconds_to_blink

        self.creatures = VGroup(*self.create_creatures())
        self.creature = self.get_primary_creature()
        if self.creatures_start_on_screen:
            self.add(*self.creatures)
Ejemplo n.º 21
0
class BarChart(Axes):
    """Creates a bar chart. Inherits from :class:`~.Axes`, so it shares its methods
    and attributes. Each axis inherits from :class:`~.NumberLine`, so pass in ``x_axis_config``/``y_axis_config``
    to control their attributes.

    Parameters
    ----------
    values
        An iterable of values that determines the height of each bar. Accepts negative values.
    bar_names
        An iterable of names for each bar. Does not have to match the length of ``values``.
    y_range
        The y_axis range of values. If ``None``, the range will be calculated based on the
        min/max of ``values`` and the step will be calculated based on ``y_length``.
    x_length
        The length of the x-axis. If ``None``, it is automatically calculated based on
        the number of values and the width of the screen.
    y_length
        The length of the y-axis.
    bar_colors
        The color for the bars. Accepts a single color or an iterable of colors.
        If the length of``bar_colors`` does not match that of ``values``,
        intermediate colors will be automatically determined.
    bar_width
        The length of a bar. Must be between 0 and 1.
    bar_fill_opacity
        The fill opacity of the bars.
    bar_stroke_width
        The stroke width of the bars.

    Examples
    --------
    .. manim:: BarChartExample
        :save_last_frame:

        class BarChartExample(Scene):
            def construct(self):
                chart = BarChart(
                    values=[-5, 40, -10, 20, -3],
                    bar_names=["one", "two", "three", "four", "five"],
                    y_range=[-20, 50, 10],
                    y_length=6,
                    x_length=10,
                    x_axis_config={"font_size": 36},
                )

                c_bar_lbls = chart.get_bar_labels(font_size=48)

                self.add(chart, c_bar_lbls)
    """

    def __init__(
        self,
        values: Iterable[float],
        bar_names: Iterable[str] | None = None,
        y_range: Sequence[float] | None = None,
        x_length: float | None = None,
        y_length: float | None = config.frame_height - 4,
        bar_colors: str
        | Iterable[str]
        | None = [
            "#003f5c",
            "#58508d",
            "#bc5090",
            "#ff6361",
            "#ffa600",
        ],
        bar_width: float = 0.6,
        bar_fill_opacity: float = 0.7,
        bar_stroke_width: float = 3,
        **kwargs,
    ):

        self.values = values
        self.bar_names = bar_names
        self.bar_colors = bar_colors
        self.bar_width = bar_width
        self.bar_fill_opacity = bar_fill_opacity
        self.bar_stroke_width = bar_stroke_width

        x_range = [0, len(self.values), 1]

        if y_range is None:
            y_range = [
                min(0, min(self.values)),
                max(0, max(self.values)),
                round(max(self.values) / y_length, 2),
            ]
        elif len(y_range) == 2:
            y_range = [*y_range, round(max(self.values) / y_length, 2)]

        if x_length is None:
            x_length = min(len(self.values), config.frame_width - 2)

        x_axis_config = {"font_size": 24, "label_constructor": Tex}
        self._update_default_configs(
            (x_axis_config,), (kwargs.pop("x_axis_config", None),)
        )

        self.bars = None
        self.x_labels = None
        self.bar_labels = None

        super().__init__(
            x_range=x_range,
            y_range=y_range,
            x_length=x_length,
            y_length=y_length,
            x_axis_config=x_axis_config,
            tips=kwargs.pop("tips", False),
            **kwargs,
        )

        self._add_bars()

        if self.bar_names is not None:
            self._add_x_axis_labels()

        self.y_axis.add_numbers()

    def _add_x_axis_labels(self):
        """Essentially ``:meth:~.NumberLine.add_labels``, but differs in that
        the direction of the label with respect to the x_axis changes to UP or DOWN
        depending on the value.

        UP for negative values and DOWN for positive values.
        """

        val_range = np.arange(
            0.5, len(self.bar_names), 1
        )  # 0.5 shifted so that labels are centered, not on ticks

        labels = VGroup()

        for i, (value, bar_name) in enumerate(zip(val_range, self.bar_names)):
            # to accommodate negative bars, the label may need to be
            # below or above the x_axis depending on the value of the bar
            if self.values[i] < 0:
                direction = UP
            else:
                direction = DOWN
            bar_name_label = self.x_axis.label_constructor(bar_name)

            bar_name_label.font_size = self.x_axis.font_size
            bar_name_label.next_to(
                self.x_axis.number_to_point(value),
                direction=direction,
                buff=self.x_axis.line_to_number_buff,
            )

            labels.add(bar_name_label)

        self.x_axis.labels = labels
        self.x_axis.add(labels)

    def _add_bars(self):
        self.bars = VGroup()

        for i, value in enumerate(self.values):
            bar_h = abs(self.c2p(0, value)[1] - self.c2p(0, 0)[1])
            bar_w = self.c2p(self.bar_width, 0)[0] - self.c2p(0, 0)[0]
            bar = Rectangle(
                height=bar_h,
                width=bar_w,
                stroke_width=self.bar_stroke_width,
                fill_opacity=self.bar_fill_opacity,
            )

            pos = UP if (value >= 0) else DOWN
            bar.next_to(self.c2p(i + 0.5, 0), pos, buff=0)
            self.bars.add(bar)
        if isinstance(self.bar_colors, str):
            self.bars.set_color_by_gradient(self.bar_colors)
        else:
            self.bars.set_color_by_gradient(*self.bar_colors)

        self.add_to_back(self.bars)

    def get_bar_labels(
        self,
        color: Color | None = None,
        font_size: float = 24,
        buff: float = MED_SMALL_BUFF,
        label_constructor: VMobject = Tex,
    ):
        """Annotates each bar with its corresponding value. Use ``self.bar_labels`` to access the
        labels after creation.

        Parameters
        ----------
        color
            The color of each label. By default ``None`` and is based on the parent's bar color.
        font_size
            The font size of each label.
        buff
            The distance from each label to its bar. By default 0.4.
        label_constructor
            The Mobject class to construct the labels, by default :class:`~.Tex`.

        Examples
        --------
        .. manim:: GetBarLabelsExample
            :save_last_frame:

            class GetBarLabelsExample(Scene):
                def construct(self):
                    chart = BarChart(values=[10, 9, 8, 7, 6, 5, 4, 3, 2, 1], y_range=[0, 10, 1])

                    c_bar_lbls = chart.get_bar_labels(
                        color=WHITE, label_constructor=MathTex, font_size=36
                    )

                    self.add(chart, c_bar_lbls)
        """

        bar_labels = VGroup()
        for bar, value in zip(self.bars, self.values):
            bar_lbl = label_constructor(str(value))

            if color is None:
                bar_lbl.set_color(bar.get_fill_color())
            else:
                bar_lbl.set_color(color)

            bar_lbl.font_size = font_size

            pos = UP if (value >= 0) else DOWN
            bar_lbl.next_to(bar, pos, buff=buff)
            bar_labels.add(bar_lbl)

        return bar_labels

    def change_bar_values(self, values: Iterable[float]):
        """Updates the height of the bars of the chart.

        Parameters
        ----------
        values
            The values that will be used to update the height of the bars.
            Does not have to match the number of bars.

        Examples
        --------
        .. manim:: ChangeBarValuesExample
            :save_last_frame:

            class ChangeBarValuesExample(Scene):
                def construct(self):
                    values=[-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10]

                    chart = BarChart(
                        values,
                        y_range=[-10, 10, 2],
                        y_axis_config={"font_size": 24},
                    )
                    self.add(chart)

                    chart.change_bar_values(list(reversed(values)))
                    self.add(chart.get_bar_labels(font_size=24))
        """

        for i, (bar, value) in enumerate(zip(self.bars, values)):
            chart_val = self.values[i]

            if chart_val > 0:
                bar_lim = bar.get_bottom()
                aligned_edge = DOWN
            else:
                bar_lim = bar.get_top()
                aligned_edge = UP

            try:
                quotient = value / chart_val
                if quotient < 0:

                    aligned_edge = UP if chart_val > 0 else DOWN

                    # if the bar is already positive, then we now want to move it
                    # so that it is negative. So, we move the top edge of the bar
                    # to the location of the previous bottom

                    # if already negative, then we move the bottom edge of the bar
                    # to the location of the previous top

                bar.stretch_to_fit_height(quotient * bar.height)

            except ZeroDivisionError:
                bar.height = 0

            bar.move_to(bar_lim, aligned_edge)

        self.values[: len(values)] = values
Ejemplo n.º 22
0
class PencilCreature(SVGMobject):
    def __init__(
        self,
        # File
        type="pencil",
        mode="normal",
        # Style
        color=BLUE_E,
        background_color="#e5d4c3",
        pupil_to_eye_width_ratio=0.4,
        pupil_dot_to_pupil_width_ratio=0.3,
        # Position
        start_corner=None,
        flip_at_start=False,
        # Size
        corner_scale_factor=0.75,
        **kwargs,
    ):
        # File
        self.mode = "ask"
        self.type = "pencil"
        self.file_name = str(SVG_DIR / f"{type}_{mode}.svg")
        # Style
        self.color = color
        self.background_color = background_color
        self.pupil_to_eye_width_ratio = pupil_to_eye_width_ratio
        self.pupil_dot_to_pupil_width_ratio = pupil_dot_to_pupil_width_ratio
        # Position
        self.start_corner = start_corner
        self.flip_at_start = flip_at_start
        # Size
        self.corner_scale_factor = corner_scale_factor

        SVGMobject.__init__(self,
                            file_name=self.file_name,
                            color=self.color,
                            **kwargs)

        if self.flip_at_start:
            self.flip()
        if self.start_corner is not None:
            self.to_corner(self.start_corner)

    def name_parts(self):
        # body
        self.body = VGroup(*[
            self.submobjects[BODY_INDEX],
            self.submobjects[SPITE_INDEX],
        ])
        self.spite = self.submobjects[SPITE_INDEX]
        self.background_body = VGroup(*[
            self.submobjects[LEFT_CIRCLE], self.submobjects[MIDDLE_CIRCLE],
            self.submobjects[RIGHT_CIRCLE], self.submobjects[BCKGRD_BODY_INDEX]
        ])
        # arms
        self.arms = VGroup(*[
            self.submobjects[LEFT_ARM_INDEX], self.submobjects[RIGHT_ARM_INDEX]
        ])
        # mouth
        self.mouth = self.submobjects[MOUTH_INDEX]
        # eyes and around
        self.eyebrow = VGroup(*[
            self.submobjects[LEFT_EYEBROW_INDEX],
            self.submobjects[RIGHT_EYEBROW_INDEX]
        ])
        self.pupils = VGroup(*[
            self.submobjects[LEFT_PUPIL_INDEX],
            self.submobjects[RIGHT_PUPIL_INDEX]
        ])
        self.eyes = VGroup(*[
            self.submobjects[LEFT_EYE_INDEX], self.submobjects[RIGHT_EYE_INDEX]
        ])
        self.eye_parts = VGroup(self.eyes, self.pupils)
        # garbage draws
        self.garbage = self.submobjects[GARBAGE]

    def init_colors(self):
        SVGMobject.init_colors(self)
        self.name_parts()

        self.garbage.set_color(RED)
        self.garbage.set_stroke(width=0)

        self.body.set_fill(self.color, opacity=1)
        self.body.set_stroke(width=0)

        self.background_body.set_fill(self.background_color, opacity=1)
        self.background_body.set_stroke(width=0)

        self.arms.set_fill(opacity=0)
        self.arms.set_stroke(color=WHITE, opacity=1, width=2)

        self.mouth.set_fill(BLACK, opacity=1)
        self.mouth.set_stroke(width=0)

        self.eyes.set_fill(WHITE, opacity=1)
        self.eyes.set_stroke(width=0)

        self.eyebrow.set_fill(opacity=0)
        self.eyebrow.set_stroke(color=WHITE, opacity=1, width=2)
        self.init_pupils()

        return self

    def init_pupils(self):
        # Instead of what is drawn, make new circles.
        # This is mostly because the paths associated
        # with the eyes in all the drawings got slightly
        # messed up.
        for eye, pupil in zip(self.eyes, self.pupils):
            pupil_r = eye.get_width() / 2
            pupil_r *= self.pupil_to_eye_width_ratio
            dot_r = pupil_r
            dot_r *= self.pupil_dot_to_pupil_width_ratio

            new_pupil = Circle(
                radius=pupil_r,
                color=BLACK,
                fill_opacity=1,
                stroke_width=0,
            )
            dot = Circle(
                radius=dot_r,
                color=WHITE,
                fill_opacity=1,
                stroke_width=0,
            )
            new_pupil.move_to(pupil)
            pupil.become(new_pupil)
            dot.shift(
                new_pupil.get_boundary_point(UL) - dot.get_boundary_point(UL))
            pupil.add(dot)

    def copy(self):
        copy_mobject = SVGMobject.copy(self)
        copy_mobject.name_parts()
        return copy_mobject

    def set_color(self, color):
        self.body.set_fill(color)
        self.color = color
        return self

    def change_mode(self, mode):
        new_self = self.__class__(mode=mode)
        new_self.match_style(self)
        new_self.match_height(self)
        if self.is_flipped() != new_self.is_flipped():
            new_self.flip()
        new_self.shift(self.eyes.get_center() - new_self.eyes.get_center())
        if hasattr(self, "purposeful_looking_direction"):
            new_self.look(self.purposeful_looking_direction)
        self.become(new_self)
        self.mode = mode
        return self

    def get_mode(self):
        return self.mode

    def look(self, direction):
        norm = get_norm(direction)
        if norm == 0:
            return
        direction /= norm
        self.purposeful_looking_direction = direction
        for pupil, eye in zip(self.pupils.split(), self.eyes.split()):
            c = eye.get_center()
            right = eye.get_right() - c
            up = eye.get_top() - c
            vect = direction[0] * right + direction[1] * up
            v_norm = get_norm(vect)
            p_radius = 0.5 * pupil.get_width()
            vect *= (v_norm - 0.75 * p_radius) / v_norm
            pupil.move_to(c + vect)
        self.pupils[1].align_to(self.pupils[0], DOWN)
        return self

    def look_at(self, point_or_mobject):
        if isinstance(point_or_mobject, Mobject):
            point = point_or_mobject.get_center()
        else:
            point = point_or_mobject
        self.look(point - self.eyes.get_center())
        return self

    def change(self, new_mode, look_at_arg=None):
        self.change_mode(new_mode)
        if look_at_arg is not None:
            self.look_at(look_at_arg)
        return self

    def get_looking_direction(self):
        vect = self.pupils.get_center() - self.eyes.get_center()
        return normalize(vect)

    def get_look_at_spot(self):
        return self.eyes.get_center() + self.get_looking_direction()

    def is_flipped(self):
        return self.eyes.submobjects[0].get_center()[0] > \
            self.eyes.submobjects[1].get_center()[0]

    def blink(self):
        eye_parts = self.eye_parts
        eye_bottom_y = eye_parts.get_bottom()[1]
        eye_parts.apply_function(lambda p: [p[0], eye_bottom_y, p[2]])
        return self

    def look_at_u(self):
        for eye, pupil in zip(self.eyes, self.pupils):
            pupil.move_to(eye.get_center())

    def to_corner(self, vect=None, **kwargs):
        if vect is not None:
            SVGMobject.to_corner(self, vect, **kwargs)
        else:
            self.scale(self.corner_scale_factor)
            self.to_corner(DOWN + LEFT, **kwargs)
        return self

    def get_bubble(self, content, **kwargs):
        bubble = Bubble(**kwargs)
        if content:
            if isinstance(content, str):
                bubble.write(content)
            else:
                bubble.add_content(content)
            bubble.resize_to_content()
        bubble.pin_to(self)
        self.bubble = bubble
        return bubble

    def make_eye_contact(self, creature):
        self.look_at(creature.eyes)
        creature.look_at(self.eyes)
        return self

    def get_all_creature_modes(self):
        result = []
        prefix = "{}_".format(self.type)
        suffix = ".svg"
        for file in os.listdir(SVG_DIR):
            if file.startswith(prefix) and file.endswith(suffix):
                result.append(file[len(prefix):-len(suffix)])
        return result
Ejemplo n.º 23
0
 def name_parts(self):
     # body
     self.body = VGroup(*[
         self.submobjects[BODY_INDEX],
         self.submobjects[SPITE_INDEX],
     ])
     self.spite = self.submobjects[SPITE_INDEX]
     self.background_body = VGroup(*[
         self.submobjects[LEFT_CIRCLE], self.submobjects[MIDDLE_CIRCLE],
         self.submobjects[RIGHT_CIRCLE], self.submobjects[BCKGRD_BODY_INDEX]
     ])
     # arms
     self.arms = VGroup(*[
         self.submobjects[LEFT_ARM_INDEX], self.submobjects[RIGHT_ARM_INDEX]
     ])
     # mouth
     self.mouth = self.submobjects[MOUTH_INDEX]
     # eyes and around
     self.eyebrow = VGroup(*[
         self.submobjects[LEFT_EYEBROW_INDEX],
         self.submobjects[RIGHT_EYEBROW_INDEX]
     ])
     self.pupils = VGroup(*[
         self.submobjects[LEFT_PUPIL_INDEX],
         self.submobjects[RIGHT_PUPIL_INDEX]
     ])
     self.eyes = VGroup(*[
         self.submobjects[LEFT_EYE_INDEX], self.submobjects[RIGHT_EYE_INDEX]
     ])
     self.eye_parts = VGroup(self.eyes, self.pupils)
     # garbage draws
     self.garbage = self.submobjects[GARBAGE]
Ejemplo n.º 24
0
 def create_creatures(self):
     """
     Likely updated for subclasses
     """
     return VGroup(self.create_creature())
Ejemplo n.º 25
0
    def interpolate_mobject(self, alpha):
        self.mobject.become(self.starting_mobject)

        VGroup(*self.mobject.get_face(self.face[0])).rotate(
            alpha * self.angle, self.axis)
Ejemplo n.º 26
0
    def __init__(
        self,
        file_name=None,
        code=None,
        tab_width=3,
        line_spacing=0.3,
        font_size=24,
        font="Monospac821 BT",
        stroke_width=0,
        margin=0.3,
        indentation_chars="    ",
        background="rectangle",  # or window
        background_stroke_width=1,
        background_stroke_color=WHITE,
        corner_radius=0.2,
        insert_line_no=True,
        line_no_from=1,
        line_no_buff=0.4,
        style="vim",
        language=None,
        generate_html_file=False,
        **kwargs,
    ):
        super().__init__(
            stroke_width=stroke_width,
            **kwargs,
        )
        self.background_stroke_color = background_stroke_color
        self.background_stroke_width = background_stroke_width
        self.tab_width = tab_width
        self.line_spacing = line_spacing
        self.font = font
        self.font_size = font_size
        self.margin = margin
        self.indentation_chars = indentation_chars
        self.background = background
        self.corner_radius = corner_radius
        self.insert_line_no = insert_line_no
        self.line_no_from = line_no_from
        self.line_no_buff = line_no_buff
        self.style = style
        self.language = language
        self.generate_html_file = generate_html_file

        self.file_path = None
        self.file_name = file_name
        if self.file_name:
            self._ensure_valid_file()
            with open(self.file_path, encoding="utf-8") as f:
                self.code_string = f.read()
        elif code:
            self.code_string = code
        else:
            raise ValueError(
                "Neither a code file nor a code string have been specified.",
            )
        if isinstance(self.style, str):
            self.style = self.style.lower()
        self._gen_html_string()
        strati = self.html_string.find("background:")
        self.background_color = self.html_string[strati + 12 : strati + 19]
        self._gen_code_json()

        self.code = self._gen_colored_lines()
        if self.insert_line_no:
            self.line_numbers = self._gen_line_numbers()
            self.line_numbers.next_to(self.code, direction=LEFT, buff=self.line_no_buff)
        if self.background == "rectangle":
            if self.insert_line_no:
                foreground = VGroup(self.code, self.line_numbers)
            else:
                foreground = self.code
            rect = SurroundingRectangle(
                foreground,
                buff=self.margin,
                color=self.background_color,
                fill_color=self.background_color,
                stroke_width=self.background_stroke_width,
                stroke_color=self.background_stroke_color,
                fill_opacity=1,
            )
            rect.round_corners(self.corner_radius)
            self.background_mobject = rect
        else:
            if self.insert_line_no:
                foreground = VGroup(self.code, self.line_numbers)
            else:
                foreground = self.code
            height = foreground.height + 0.1 * 3 + 2 * self.margin
            width = foreground.width + 0.1 * 3 + 2 * self.margin

            rect = RoundedRectangle(
                corner_radius=self.corner_radius,
                height=height,
                width=width,
                stroke_width=self.background_stroke_width,
                stroke_color=self.background_stroke_color,
                color=self.background_color,
                fill_opacity=1,
            )
            red_button = Dot(radius=0.1, stroke_width=0, color="#ff5f56")
            red_button.shift(LEFT * 0.1 * 3)
            yellow_button = Dot(radius=0.1, stroke_width=0, color="#ffbd2e")
            green_button = Dot(radius=0.1, stroke_width=0, color="#27c93f")
            green_button.shift(RIGHT * 0.1 * 3)
            buttons = VGroup(red_button, yellow_button, green_button)
            buttons.shift(
                UP * (height / 2 - 0.1 * 2 - 0.05)
                + LEFT * (width / 2 - 0.1 * 5 - self.corner_radius / 2 - 0.05),
            )

            self.background_mobject = VGroup(rect, buttons)
            x = (height - foreground.height) / 2 - 0.1 * 3
            self.background_mobject.shift(foreground.get_center())
            self.background_mobject.shift(UP * x)
        if self.insert_line_no:
            super().__init__(
                self.background_mobject, self.line_numbers, self.code, **kwargs
            )
        else:
            super().__init__(
                self.background_mobject,
                Dot(fill_opacity=0, stroke_opacity=0),
                self.code,
                **kwargs,
            )
        self.move_to(np.array([0, 0, 0]))
Ejemplo n.º 27
0
 def get_number_mobjects(self, *numbers, **kwargs) -> VGroup:
     if len(numbers) == 0:
         numbers = self.default_numbers_to_display()
     return VGroup(
         [self.get_number_mobject(number, **kwargs) for number in numbers])
Ejemplo n.º 28
0
class Code(VGroup):
    """A highlighted source code listing.

    An object ``listing`` of :class:`.Code` is a :class:`.VGroup` consisting
    of three objects:

    - The background, ``listing.background_mobject``. This is either
      a :class:`.Rectangle` (if the listing has been initialized with
      ``background="rectangle"``, the default option) or a :class:`.VGroup`
      resembling a window (if ``background="window"`` has been passed).

    - The line numbers, ``listing.line_numbers`` (a :class:`.Paragraph`
      object).

    - The highlighted code itself, ``listing.code`` (a :class:`.Paragraph`
      object).

    .. WARNING::

        Using a :class:`.Transform` on text with leading whitespace (and in
        this particular case: code) can look
        `weird <https://github.com/3b1b/manim/issues/1067>`_. Consider using
        :meth:`remove_invisible_chars` to resolve this issue.

    Examples
    --------

    Normal usage::

        listing = Code(
            "helloworldcpp.cpp",
            tab_width=4,
            background_stroke_width=1,
            background_stroke_color=WHITE,
            insert_line_no=True,
            style=Code.styles_list[15],
            background="window",
            language="cpp",
        )

    We can also render code passed as a string (but note that
    the language has to be specified in this case):

    .. manim:: CodeFromString
        :save_last_frame:

        class CodeFromString(Scene):
            def construct(self):
                code = '''from manim import Scene, Square

        class FadeInSquare(Scene):
            def construct(self):
                s = Square()
                self.play(FadeIn(s))
                self.play(s.animate.scale(2))
                self.wait()
        '''
                rendered_code = Code(code=code, tab_width=4, background="window",
                                    language="Python", font="Monospace")
                self.add(rendered_code)

    Parameters
    ----------
    file_name : :class:`str`
        Name of the code file to display.
    code : :class:`str`
        If ``file_name`` is not specified, a code string can be
        passed directly.
    tab_width : :class:`int`, optional
        Number of space characters corresponding to a tab character. Defaults to 3.
    line_spacing : :class:`float`, optional
        Amount of space between lines in relation to font size. Defaults to 0.3, which means 30% of font size.
    font_size : class:`float`, optional
        A number which scales displayed code. Defaults to 24.
    font : :class:`str`, optional
         The name of the text font to be used. Defaults to ``"Monospac821 BT"``.
    stroke_width : class:`float`, optional
        Stroke width for text. 0 is recommended, and the default.
    margin: class :`float`, optional
        Inner margin of text from the background. Defaults to 0.3.
    indentation_chars : :class:`str`, optional
        "Indentation chars" refers to the spaces/tabs at the beginning of a given code line. Defaults to ``"    "`` (spaces).
    background : :class:`str`, optional
        Defines the background's type. Currently supports only ``"rectangle"`` (default) and ``"window"``.
    background_stroke_width : class:`float`, optional
        Defines the stroke width of the background. Defaults to 1.
    background_stroke_color : class:`str`, optional
        Defines the stroke color for the background. Defaults to ``WHITE``.
    corner_radius : :class:`float`, optional
        Defines the corner radius for the background. Defaults to 0.2.
    insert_line_no : :class:`bool`, optional
        Defines whether line numbers should be inserted in displayed code. Defaults to ``True``.
    line_no_from : :class:`int`, optional
        Defines the first line's number in the line count. Defaults to 1.
    line_no_buff : :class:`float`, optional
        Defines the spacing between line numbers and displayed code. Defaults to 0.4.
    style : :class:`str`, optional
        Defines the style type of displayed code. You can see possible names of styles in with :attr:`styles_list`. Defaults to ``"vim"``.
    language : Optional[:class:`str`], optional
        Specifies the programming language the given code was written in. If ``None``
        (the default), the language will be automatically detected. For the list of
        possible options, visit https://pygments.org/docs/lexers/ and look for
        'aliases or short names'.
    generate_html_file : :class:`bool`, optional
        Defines whether to generate highlighted html code to the folder `assets/codes/generated_html_files`. Defaults to `False`.

    Attributes
    ----------
    background_mobject : :class:`~.VGroup`
        The background of the code listing.
    line_numbers : :class:`~.Paragraph`
        The line numbers for the code listing. Empty, if
        ``insert_line_no=False`` has been specified.
    code : :class:`~.Paragraph`
        The highlighted code.

    """

    # tuples in the form (name, aliases, filetypes, mimetypes)
    # 'language' is aliases or short names
    # For more information about pygments.lexers visit https://pygments.org/docs/lexers/
    # from pygments.lexers import get_all_lexers
    # all_lexers = get_all_lexers()
    styles_list = list(get_all_styles())
    # For more information about pygments.styles visit https://pygments.org/docs/styles/

    def __init__(
        self,
        file_name=None,
        code=None,
        tab_width=3,
        line_spacing=0.3,
        font_size=24,
        font="Monospac821 BT",
        stroke_width=0,
        margin=0.3,
        indentation_chars="    ",
        background="rectangle",  # or window
        background_stroke_width=1,
        background_stroke_color=WHITE,
        corner_radius=0.2,
        insert_line_no=True,
        line_no_from=1,
        line_no_buff=0.4,
        style="vim",
        language=None,
        generate_html_file=False,
        **kwargs,
    ):
        super().__init__(
            stroke_width=stroke_width,
            **kwargs,
        )
        self.background_stroke_color = background_stroke_color
        self.background_stroke_width = background_stroke_width
        self.tab_width = tab_width
        self.line_spacing = line_spacing
        self.font = font
        self.font_size = font_size
        self.margin = margin
        self.indentation_chars = indentation_chars
        self.background = background
        self.corner_radius = corner_radius
        self.insert_line_no = insert_line_no
        self.line_no_from = line_no_from
        self.line_no_buff = line_no_buff
        self.style = style
        self.language = language
        self.generate_html_file = generate_html_file

        self.file_path = None
        self.file_name = file_name
        if self.file_name:
            self._ensure_valid_file()
            with open(self.file_path, encoding="utf-8") as f:
                self.code_string = f.read()
        elif code:
            self.code_string = code
        else:
            raise ValueError(
                "Neither a code file nor a code string have been specified.",
            )
        if isinstance(self.style, str):
            self.style = self.style.lower()
        self._gen_html_string()
        strati = self.html_string.find("background:")
        self.background_color = self.html_string[strati + 12 : strati + 19]
        self._gen_code_json()

        self.code = self._gen_colored_lines()
        if self.insert_line_no:
            self.line_numbers = self._gen_line_numbers()
            self.line_numbers.next_to(self.code, direction=LEFT, buff=self.line_no_buff)
        if self.background == "rectangle":
            if self.insert_line_no:
                foreground = VGroup(self.code, self.line_numbers)
            else:
                foreground = self.code
            rect = SurroundingRectangle(
                foreground,
                buff=self.margin,
                color=self.background_color,
                fill_color=self.background_color,
                stroke_width=self.background_stroke_width,
                stroke_color=self.background_stroke_color,
                fill_opacity=1,
            )
            rect.round_corners(self.corner_radius)
            self.background_mobject = rect
        else:
            if self.insert_line_no:
                foreground = VGroup(self.code, self.line_numbers)
            else:
                foreground = self.code
            height = foreground.height + 0.1 * 3 + 2 * self.margin
            width = foreground.width + 0.1 * 3 + 2 * self.margin

            rect = RoundedRectangle(
                corner_radius=self.corner_radius,
                height=height,
                width=width,
                stroke_width=self.background_stroke_width,
                stroke_color=self.background_stroke_color,
                color=self.background_color,
                fill_opacity=1,
            )
            red_button = Dot(radius=0.1, stroke_width=0, color="#ff5f56")
            red_button.shift(LEFT * 0.1 * 3)
            yellow_button = Dot(radius=0.1, stroke_width=0, color="#ffbd2e")
            green_button = Dot(radius=0.1, stroke_width=0, color="#27c93f")
            green_button.shift(RIGHT * 0.1 * 3)
            buttons = VGroup(red_button, yellow_button, green_button)
            buttons.shift(
                UP * (height / 2 - 0.1 * 2 - 0.05)
                + LEFT * (width / 2 - 0.1 * 5 - self.corner_radius / 2 - 0.05),
            )

            self.background_mobject = VGroup(rect, buttons)
            x = (height - foreground.height) / 2 - 0.1 * 3
            self.background_mobject.shift(foreground.get_center())
            self.background_mobject.shift(UP * x)
        if self.insert_line_no:
            super().__init__(
                self.background_mobject, self.line_numbers, self.code, **kwargs
            )
        else:
            super().__init__(
                self.background_mobject,
                Dot(fill_opacity=0, stroke_opacity=0),
                self.code,
                **kwargs,
            )
        self.move_to(np.array([0, 0, 0]))

    def _ensure_valid_file(self):
        """Function to validate file."""
        if self.file_name is None:
            raise Exception("Must specify file for Code")
        possible_paths = [
            os.path.join(os.path.join("assets", "codes"), self.file_name),
            self.file_name,
        ]
        for path in possible_paths:
            if os.path.exists(path):
                self.file_path = path
                return
        error = (
            f"From: {os.getcwd()}, could not find {self.file_name} at either "
            + f"of these locations: {possible_paths}"
        )
        raise OSError(error)

    def _gen_line_numbers(self):
        """Function to generate line_numbers.

        Returns
        -------
        :class:`~.Paragraph`
            The generated line_numbers according to parameters.
        """
        line_numbers_array = []
        for line_no in range(0, self.code_json.__len__()):
            number = str(self.line_no_from + line_no)
            line_numbers_array.append(number)
        line_numbers = Paragraph(
            *list(line_numbers_array),
            line_spacing=self.line_spacing,
            alignment="right",
            font_size=self.font_size,
            font=self.font,
            disable_ligatures=True,
            stroke_width=self.stroke_width,
        )
        for i in line_numbers:
            i.set_color(self.default_color)
        return line_numbers

    def _gen_colored_lines(self):
        """Function to generate code.

        Returns
        -------
        :class:`~.Paragraph`
            The generated code according to parameters.
        """
        lines_text = []
        for line_no in range(0, self.code_json.__len__()):
            line_str = ""
            for word_index in range(self.code_json[line_no].__len__()):
                line_str = line_str + self.code_json[line_no][word_index][0]
            lines_text.append(self.tab_spaces[line_no] * "\t" + line_str)
        code = Paragraph(
            *list(lines_text),
            line_spacing=self.line_spacing,
            tab_width=self.tab_width,
            font_size=self.font_size,
            font=self.font,
            disable_ligatures=True,
            stroke_width=self.stroke_width,
        )
        for line_no in range(code.__len__()):
            line = code.chars[line_no]
            line_char_index = self.tab_spaces[line_no]
            for word_index in range(self.code_json[line_no].__len__()):
                line[
                    line_char_index : line_char_index
                    + self.code_json[line_no][word_index][0].__len__()
                ].set_color(self.code_json[line_no][word_index][1])
                line_char_index += self.code_json[line_no][word_index][0].__len__()
        return code

    def _gen_html_string(self):
        """Function to generate html string with code highlighted and stores in variable html_string."""
        self.html_string = _hilite_me(
            self.code_string,
            self.language,
            self.style,
            self.insert_line_no,
            "border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;",
            self.file_path,
            self.line_no_from,
        )

        if self.generate_html_file:
            os.makedirs(
                os.path.join("assets", "codes", "generated_html_files"),
                exist_ok=True,
            )
            with open(
                os.path.join(
                    "assets",
                    "codes",
                    "generated_html_files",
                    self.file_name + ".html",
                ),
                "w",
            ) as file:
                file.write(self.html_string)

    def _gen_code_json(self):
        """Function to background_color, generate code_json and tab_spaces from html_string.
        background_color is just background color of displayed code.
        code_json is 2d array with rows as line numbers
        and columns as a array with length 2 having text and text's color value.
        tab_spaces is 2d array with rows as line numbers
        and columns as corresponding number of indentation_chars in front of that line in code.
        """
        if (
            self.background_color == "#111111"
            or self.background_color == "#272822"
            or self.background_color == "#202020"
            or self.background_color == "#000000"
        ):
            self.default_color = "#ffffff"
        else:
            self.default_color = "#000000"
        # print(self.default_color,self.background_color)
        for i in range(3, -1, -1):
            self.html_string = self.html_string.replace("</" + " " * i, "</")

        # handle pygments bug
        # https://github.com/pygments/pygments/issues/961
        self.html_string = self.html_string.replace("<span></span>", "")

        for i in range(10, -1, -1):
            self.html_string = self.html_string.replace(
                "</span>" + " " * i,
                " " * i + "</span>",
            )
        self.html_string = self.html_string.replace("background-color:", "background:")

        if self.insert_line_no:
            start_point = self.html_string.find("</td><td><pre")
            start_point = start_point + 9
        else:
            start_point = self.html_string.find("<pre")
        self.html_string = self.html_string[start_point:]
        # print(self.html_string)
        lines = self.html_string.split("\n")
        lines = lines[0 : lines.__len__() - 2]
        start_point = lines[0].find(">")
        lines[0] = lines[0][start_point + 1 :]
        # print(lines)
        self.code_json = []
        self.tab_spaces = []
        code_json_line_index = -1
        for line_index in range(0, lines.__len__()):
            # print(lines[line_index])
            self.code_json.append([])
            code_json_line_index = code_json_line_index + 1
            if lines[line_index].startswith(self.indentation_chars):
                start_point = lines[line_index].find("<")
                starting_string = lines[line_index][:start_point]
                indentation_chars_count = lines[line_index][:start_point].count(
                    self.indentation_chars,
                )
                if (
                    starting_string.__len__()
                    != indentation_chars_count * self.indentation_chars.__len__()
                ):
                    lines[line_index] = (
                        "\t" * indentation_chars_count
                        + starting_string[
                            starting_string.rfind(self.indentation_chars)
                            + self.indentation_chars.__len__() :
                        ]
                        + lines[line_index][start_point:]
                    )
                else:
                    lines[line_index] = (
                        "\t" * indentation_chars_count + lines[line_index][start_point:]
                    )
            indentation_chars_count = 0
            if lines[line_index]:
                while lines[line_index][indentation_chars_count] == "\t":
                    indentation_chars_count = indentation_chars_count + 1
            self.tab_spaces.append(indentation_chars_count)
            # print(lines[line_index])
            lines[line_index] = self._correct_non_span(lines[line_index])
            # print(lines[line_index])
            words = lines[line_index].split("<span")
            for word_index in range(1, words.__len__()):
                color_index = words[word_index].find("color:")
                if color_index == -1:
                    color = self.default_color
                else:
                    starti = words[word_index][color_index:].find("#")
                    color = words[word_index][
                        color_index + starti : color_index + starti + 7
                    ]
                start_point = words[word_index].find(">")
                end_point = words[word_index].find("</span>")
                text = words[word_index][start_point + 1 : end_point]
                text = html.unescape(text)
                if text != "":
                    # print(text, "'" + color + "'")
                    self.code_json[code_json_line_index].append([text, color])
        # print(self.code_json)

    def _correct_non_span(self, line_str):
        """Function put text color to those strings that don't have one according to background_color of displayed code.

        Parameters
        ---------
        line_str : :class:`str`
            Takes a html element's string to put color to it according to background_color of displayed code.

        Returns
        -------
        :class:`str`
            The generated html element's string with having color attributes.
        """
        words = line_str.split("</span>")
        line_str = ""
        for i in range(0, words.__len__()):
            if i != words.__len__() - 1:
                j = words[i].find("<span")
            else:
                j = words[i].__len__()
            temp = ""
            starti = -1
            for k in range(0, j):
                if words[i][k] == "\t" and starti == -1:
                    continue
                else:
                    if starti == -1:
                        starti = k
                    temp = temp + words[i][k]
            if temp != "":
                if i != words.__len__() - 1:
                    temp = (
                        '<span style="color:'
                        + self.default_color
                        + '">'
                        + words[i][starti:j]
                        + "</span>"
                    )
                else:
                    temp = (
                        '<span style="color:'
                        + self.default_color
                        + '">'
                        + words[i][starti:j]
                    )
                temp = temp + words[i][j:]
                words[i] = temp
            if words[i] != "":
                line_str = line_str + words[i] + "</span>"
        return line_str
Ejemplo n.º 29
0
    def add_labels(
        self,
        dict_values: dict[float, str | float | VMobject],
        direction: Sequence[float] = None,
        buff: float | None = None,
        font_size: float | None = None,
        label_constructor: VMobject | None = None,
    ):
        """Adds specifically positioned labels to the :class:`~.NumberLine` using a ``dict``.
        The labels can be accessed after creation via ``self.labels``.

        Parameters
        ----------
        dict_values
            A dictionary consisting of the position along the number line and the mobject to be added:
            ``{1: Tex("Monday"), 3: Tex("Tuesday")}``. :attr:`label_constructor` will be used
            to construct the labels if the value is not a mobject (``str`` or ``float``).
        direction
            Determines the direction at which the label is positioned next to the line.
        buff
            The distance of the label from the line.
        font_size
            The font size of the mobject to be positioned.
        label_constructor
            The :class:`~.VMobject` class that will be used to construct the label.
            Defaults to the ``label_constructor`` attribute of the number line
            if not specified.

        Raises
        ------
        AttributeError
            If the label does not have a ``font_size`` attribute, an ``AttributeError`` is raised.
        """

        direction = self.label_direction if direction is None else direction
        buff = self.line_to_number_buff if buff is None else buff
        font_size = self.font_size if font_size is None else font_size
        label_constructor = (self.label_constructor if
                             label_constructor is None else label_constructor)

        labels = VGroup()
        for x, label in dict_values.items():

            # TODO: remove this check and ability to call
            # this method via CoordinateSystem.add_coordinates()
            # must be explicitly called
            if isinstance(label, str) and self.label_constructor is MathTex:
                label = Tex(label)
            else:
                label = self._create_label_tex(label)

            if hasattr(label, "font_size"):
                label.font_size = font_size
            else:
                raise AttributeError(
                    f"{label} is not compatible with add_labels.")
            label.next_to(self.number_to_point(x),
                          direction=direction,
                          buff=buff)
            labels.add(label)

        self.labels = labels
        self.add(labels)
        return self
Ejemplo n.º 30
0
 def get_on_screen_creatures(self):
     mobjects = self.get_mobject_family_members()
     return VGroup(*[
         pencil for pencil in self.get_creatures()
         if pencil in mobjects
     ])