Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added cap_style feature to VMobject #3516

Merged
merged 8 commits into from
Dec 10, 2023
13 changes: 12 additions & 1 deletion manim/camera/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
}


CAP_STYLE_MAP = {
CapStyleType.AUTO: None, # TODO: this could be improved
CapStyleType.ROUND: cairo.LineCap.ROUND,
CapStyleType.BUTT: cairo.LineCap.BUTT,
CapStyleType.SQUARE: cairo.LineCap.SQUARE,
}


class Camera:
"""Base camera class.

Expand Down Expand Up @@ -778,11 +786,14 @@ def apply_stroke(
ctx.set_line_width(
width
* self.cairo_line_width_multiple
*
# This ensures lines have constant width as you zoom in on them.
* (self.frame_width / self.frame_width),
(self.frame_width / self.frame_width),
MathItYT marked this conversation as resolved.
Show resolved Hide resolved
)
if vmobject.joint_type != LineJointType.AUTO:
ctx.set_line_join(LINE_JOIN_MAP[vmobject.joint_type])
if vmobject.cap_style != CapStyleType.AUTO:
ctx.set_line_cap(CAP_STYLE_MAP[vmobject.cap_style])
ctx.stroke_preserve()
return self

Expand Down
39 changes: 39 additions & 0 deletions manim/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"CTRL_VALUE",
"RendererType",
"LineJointType",
"CapStyleType",
]
# Messages

Expand Down Expand Up @@ -305,3 +306,41 @@ def construct(self):
ROUND = 1
BEVEL = 2
MITER = 3


class CapStyleType(Enum):
"""Collection of available cap styles.

See the example below for a visual illustration of the different
cap styles.

Examples
--------

.. manim:: CapStyleVariants
:save_last_frame:

class CapStyleVariants(Scene):
def construct(self):
arcs = VGroup(*[
Arc(
radius=1,
start_angle=0,
angle=TAU / 4,
stroke_width=20,
color=GREEN,
cap_style=cap_style,
)
for cap_style in CapStyleType
])
arcs.arrange(RIGHT, buff=1)
self.add(arcs)
for arc in arcs:
label = Text(arc.cap_style.name, font_size=24).next_to(arc, DOWN)
self.add(label)
"""

AUTO = 0
ROUND = 1
BUTT = 2
SQUARE = 3
34 changes: 32 additions & 2 deletions manim/mobject/types/vectorized_mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def __init__(
# TODO, do we care about accounting for varying zoom levels?
tolerance_for_point_equality: float = 1e-6,
n_points_per_cubic_curve: int = 4,
cap_style: CapStyleType = CapStyleType.AUTO,
**kwargs,
):
self.fill_opacity = fill_opacity
Expand Down Expand Up @@ -150,6 +151,7 @@ def __init__(
self.shade_in_3d: bool = shade_in_3d
self.tolerance_for_point_equality: float = tolerance_for_point_equality
self.n_points_per_cubic_curve: int = n_points_per_cubic_curve
self.cap_style: CapStyleType = cap_style
super().__init__(**kwargs)
self.submobjects: list[VMobject]

Expand Down Expand Up @@ -340,6 +342,34 @@ def set_stroke(
self.background_stroke_color = ManimColor(color)
return self

def set_cap_style(self, cap_style: CapStyleType) -> Self:
"""
Sets the cap style of the :class:`VMobject`.

Parameters
----------
cap_style
The cap style to be set. See :class:`.CapStyleType` for options.

Returns
-------
:class:`VMobject`
``self``

Examples
--------
.. manim:: CapStyleExample
:save_last_frame:

class CapStyleExample(Scene):
def construct(self):
line = Line(LEFT, RIGHT, color=YELLOW, stroke_width=20)
line.set_cap_style(CapStyleType.ROUND)
self.add(line)
"""
self.cap_style = cap_style
return self

def set_background_stroke(self, **kwargs) -> Self:
kwargs["background"] = True
self.set_stroke(**kwargs)
Expand Down Expand Up @@ -2458,7 +2488,7 @@ def _throw_error_if_no_submobjects(self):
if len(self.submobjects) == 0:
caller_name = sys._getframe(1).f_code.co_name
raise Exception(
f"Cannot call CurvesAsSubmobjects.{caller_name} for a CurvesAsSubmobject with no submobjects"
f"Cannot call CurvesAsSubmobjects. {caller_name} for a CurvesAsSubmobject with no submobjects" # noqa
)

def _get_submobjects_with_points(self):
Expand All @@ -2468,7 +2498,7 @@ def _get_submobjects_with_points(self):
if len(submobjs_with_pts) == 0:
caller_name = sys._getframe(1).f_code.co_name
raise Exception(
f"Cannot call CurvesAsSubmobjects.{caller_name} for a CurvesAsSubmobject whose submobjects have no points"
f"Cannot call CurvesAsSubmobjects. {caller_name} for a CurvesAsSubmobject whose submobjects have no points" # noqa
)
return submobjs_with_pts

Expand Down
Loading