diff --git a/manim/__init__.py b/manim/__init__.py index a697122752..cae45fcc42 100644 --- a/manim/__init__.py +++ b/manim/__init__.py @@ -46,6 +46,7 @@ from .mobject.frame import * from .mobject.geometry.arc import * from .mobject.geometry.boolean_ops import * +from .mobject.geometry.labeled import * from .mobject.geometry.line import * from .mobject.geometry.polygram import * from .mobject.geometry.shape_matchers import * diff --git a/manim/mobject/geometry/__init__.py b/manim/mobject/geometry/__init__.py index abae9c0da0..cf3283bc69 100644 --- a/manim/mobject/geometry/__init__.py +++ b/manim/mobject/geometry/__init__.py @@ -8,6 +8,7 @@ ~arc ~boolean_ops + ~labeled ~line ~polygram ~shape_matchers diff --git a/manim/mobject/geometry/labeled.py b/manim/mobject/geometry/labeled.py new file mode 100644 index 0000000000..91f8cb0912 --- /dev/null +++ b/manim/mobject/geometry/labeled.py @@ -0,0 +1,153 @@ +r"""Mobjects that inherit from lines and contain a label along the length.""" + +from __future__ import annotations + +__all__ = ["LabeledLine", "LabeledArrow"] + +from manim.constants import * +from manim.mobject.geometry.line import Arrow, Line +from manim.mobject.geometry.shape_matchers import ( + BackgroundRectangle, + SurroundingRectangle, +) +from manim.mobject.text.tex_mobject import MathTex, Tex +from manim.mobject.text.text_mobject import Text +from manim.utils.color import WHITE, Color + + +class LabeledLine(Line): + """Constructs a line containing a label box somewhere along its length. + + Parameters + ---------- + label : str | Tex | MathTex | Text + Label that will be displayed on the line. + label_position : float | optional + A ratio in the range [0-1] to indicate the position of the label with respect to the length of the line. Default value is 0.5. + font_size : float | optional + Control font size for the label. This parameter is only used when `label` is of type `str`. + label_color: numpy.ndarray | optional + The color of the label's text. This parameter is only used when `label` is of type `str`. + label_frame : Bool | optional + Add a `SurroundingRectangle` frame to the label box. + frame_fill_color : numpy.ndarray | optional + Background color to fill the label box. If no value is provided, the background color of the canvas will be used. + frame_fill_opacity : float | optional + Determine the opacity of the label box by passing a value in the range [0-1], where 0 indicates complete transparency and 1 means full opacity. + + .. seealso:: + :class:`LabeledArrow` + + Examples + -------- + .. manim:: LabeledLineExample + :save_last_frame: + + class LabeledLineExample(Scene): + def construct(self): + line = LabeledLine( + label = '0.5', + label_position = 0.8, + font_size = 20, + label_color = WHITE, + label_frame = True, + + start=LEFT+DOWN, + end=RIGHT+UP) + + + line.set_length(line.get_length() * 2) + self.add(line) + """ + + def __init__( + self, + label: str | Tex | MathTex | Text, + label_position: float = 0.5, + font_size: float = DEFAULT_FONT_SIZE, + label_color: Color | str | None = WHITE, + label_frame: bool = True, + frame_fill_color: Color | str | None = None, + frame_fill_opacity: float = 1, + *args, + **kwargs, + ) -> None: + if isinstance(label, str): + from manim import MathTex + + rendered_label = MathTex(label, color=label_color, font_size=font_size) + else: + rendered_label = label + + super().__init__(*args, **kwargs) + + # calculating the vector for the label position + line_start, line_end = self.get_start_and_end() + new_vec = (line_end - line_start) * label_position + label_coords = line_start + new_vec + + # rendered_label.move_to(self.get_vector() * label_position) + rendered_label.move_to(label_coords) + + box = BackgroundRectangle( + rendered_label, + buff=0.05, + color=frame_fill_color, + fill_opacity=frame_fill_opacity, + stroke_width=0.5, + ) + self.add(box) + + if label_frame: + box_frame = SurroundingRectangle( + rendered_label, buff=0.05, color=label_color, stroke_width=0.5 + ) + + self.add(box_frame) + + self.add(rendered_label) + + +class LabeledArrow(LabeledLine, Arrow): + """Constructs an arrow containing a label box somewhere along its length. + This class inherits its label properties from `LabeledLine`, so the main parameters controlling it are the same. + + Parameters + ---------- + label : str | Tex | MathTex | Text + Label that will be displayed on the line. + label_position : float | optional + A ratio in the range [0-1] to indicate the position of the label with respect to the length of the line. Default value is 0.5. + font_size : float | optional + Control font size for the label. This parameter is only used when `label` is of type `str`. + label_color: numpy.ndarray | optional + The color of the label's text. This parameter is only used when `label` is of type `str`. + label_frame : Bool | optional + Add a `SurroundingRectangle` frame to the label box. + frame_fill_color : numpy.ndarray | optional + Background color to fill the label box. If no value is provided, the background color of the canvas will be used. + frame_fill_opacity : float | optional + Determine the opacity of the label box by passing a value in the range [0-1], where 0 indicates complete transparency and 1 means full opacity. + + + .. seealso:: + :class:`LabeledLine` + + Examples + -------- + .. manim:: LabeledArrowExample + :save_last_frame: + + class LabeledArrowExample(Scene): + def construct(self): + l_arrow = LabeledArrow("0.5", start=LEFT*3, end=RIGHT*3 + UP*2, label_position=0.5) + + self.add(l_arrow) + """ + + def __init__( + self, + *args, + **kwargs, + ) -> None: + super().__init__(*args, **kwargs) diff --git a/tests/test_graphical_units/control_data/geometry/LabeledArrow.npz b/tests/test_graphical_units/control_data/geometry/LabeledArrow.npz new file mode 100644 index 0000000000..43baefcbc5 Binary files /dev/null and b/tests/test_graphical_units/control_data/geometry/LabeledArrow.npz differ diff --git a/tests/test_graphical_units/control_data/geometry/LabeledLine.npz b/tests/test_graphical_units/control_data/geometry/LabeledLine.npz new file mode 100644 index 0000000000..b431dde19b Binary files /dev/null and b/tests/test_graphical_units/control_data/geometry/LabeledLine.npz differ diff --git a/tests/test_graphical_units/test_geometry.py b/tests/test_graphical_units/test_geometry.py index 6f6ab72e13..10ea6094e9 100644 --- a/tests/test_graphical_units/test_geometry.py +++ b/tests/test_graphical_units/test_geometry.py @@ -247,3 +247,25 @@ def test_CurvedArrowCustomTip(scene): tip_shape_end=ArrowSquareFilledTip, ) scene.add(arrow, double_arrow) + + +@frames_comparison +def test_LabeledLine(scene): + line = LabeledLine( + label="0.5", + label_position=0.8, + font_size=20, + label_color=WHITE, + label_frame=True, + start=LEFT + DOWN, + end=RIGHT + UP, + ) + scene.add(line) + + +@frames_comparison +def test_LabeledArrow(scene): + l_arrow = LabeledArrow( + "0.5", start=LEFT * 3, end=RIGHT * 3 + UP * 2, label_position=0.5, font_size=15 + ) + scene.add(l_arrow)