Skip to content

Commit 03f9d4b

Browse files
Added cap_style feature to VMobject (#3516)
* Added cap_style feature to VMobject * Added an example to `set_cap_style` method * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Unsplitted line 2501 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added graphical test for cap_style * Added vmobject_cap_styles.npz for testing cap_styles * Removed # noqa comments from vectorized_mobject.py --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 3f118e4 commit 03f9d4b

File tree

5 files changed

+101
-3
lines changed

5 files changed

+101
-3
lines changed

manim/camera/camera.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@
3737
}
3838

3939

40+
CAP_STYLE_MAP = {
41+
CapStyleType.AUTO: None, # TODO: this could be improved
42+
CapStyleType.ROUND: cairo.LineCap.ROUND,
43+
CapStyleType.BUTT: cairo.LineCap.BUTT,
44+
CapStyleType.SQUARE: cairo.LineCap.SQUARE,
45+
}
46+
47+
4048
class Camera:
4149
"""Base camera class.
4250
@@ -778,11 +786,13 @@ def apply_stroke(
778786
ctx.set_line_width(
779787
width
780788
* self.cairo_line_width_multiple
781-
# This ensures lines have constant width as you zoom in on them.
782789
* (self.frame_width / self.frame_width),
790+
# This ensures lines have constant width as you zoom in on them.
783791
)
784792
if vmobject.joint_type != LineJointType.AUTO:
785793
ctx.set_line_join(LINE_JOIN_MAP[vmobject.joint_type])
794+
if vmobject.cap_style != CapStyleType.AUTO:
795+
ctx.set_line_cap(CAP_STYLE_MAP[vmobject.cap_style])
786796
ctx.stroke_preserve()
787797
return self
788798

manim/constants.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"CTRL_VALUE",
7777
"RendererType",
7878
"LineJointType",
79+
"CapStyleType",
7980
]
8081
# Messages
8182

@@ -305,3 +306,41 @@ def construct(self):
305306
ROUND = 1
306307
BEVEL = 2
307308
MITER = 3
309+
310+
311+
class CapStyleType(Enum):
312+
"""Collection of available cap styles.
313+
314+
See the example below for a visual illustration of the different
315+
cap styles.
316+
317+
Examples
318+
--------
319+
320+
.. manim:: CapStyleVariants
321+
:save_last_frame:
322+
323+
class CapStyleVariants(Scene):
324+
def construct(self):
325+
arcs = VGroup(*[
326+
Arc(
327+
radius=1,
328+
start_angle=0,
329+
angle=TAU / 4,
330+
stroke_width=20,
331+
color=GREEN,
332+
cap_style=cap_style,
333+
)
334+
for cap_style in CapStyleType
335+
])
336+
arcs.arrange(RIGHT, buff=1)
337+
self.add(arcs)
338+
for arc in arcs:
339+
label = Text(arc.cap_style.name, font_size=24).next_to(arc, DOWN)
340+
self.add(label)
341+
"""
342+
343+
AUTO = 0
344+
ROUND = 1
345+
BUTT = 2
346+
SQUARE = 3

manim/mobject/types/vectorized_mobject.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ def __init__(
123123
# TODO, do we care about accounting for varying zoom levels?
124124
tolerance_for_point_equality: float = 1e-6,
125125
n_points_per_cubic_curve: int = 4,
126+
cap_style: CapStyleType = CapStyleType.AUTO,
126127
**kwargs,
127128
):
128129
self.fill_opacity = fill_opacity
@@ -150,6 +151,7 @@ def __init__(
150151
self.shade_in_3d: bool = shade_in_3d
151152
self.tolerance_for_point_equality: float = tolerance_for_point_equality
152153
self.n_points_per_cubic_curve: int = n_points_per_cubic_curve
154+
self.cap_style: CapStyleType = cap_style
153155
super().__init__(**kwargs)
154156
self.submobjects: list[VMobject]
155157

@@ -340,6 +342,34 @@ def set_stroke(
340342
self.background_stroke_color = ManimColor(color)
341343
return self
342344

345+
def set_cap_style(self, cap_style: CapStyleType) -> Self:
346+
"""
347+
Sets the cap style of the :class:`VMobject`.
348+
349+
Parameters
350+
----------
351+
cap_style
352+
The cap style to be set. See :class:`.CapStyleType` for options.
353+
354+
Returns
355+
-------
356+
:class:`VMobject`
357+
``self``
358+
359+
Examples
360+
--------
361+
.. manim:: CapStyleExample
362+
:save_last_frame:
363+
364+
class CapStyleExample(Scene):
365+
def construct(self):
366+
line = Line(LEFT, RIGHT, color=YELLOW, stroke_width=20)
367+
line.set_cap_style(CapStyleType.ROUND)
368+
self.add(line)
369+
"""
370+
self.cap_style = cap_style
371+
return self
372+
343373
def set_background_stroke(self, **kwargs) -> Self:
344374
kwargs["background"] = True
345375
self.set_stroke(**kwargs)
@@ -2458,7 +2488,7 @@ def _throw_error_if_no_submobjects(self):
24582488
if len(self.submobjects) == 0:
24592489
caller_name = sys._getframe(1).f_code.co_name
24602490
raise Exception(
2461-
f"Cannot call CurvesAsSubmobjects.{caller_name} for a CurvesAsSubmobject with no submobjects"
2491+
f"Cannot call CurvesAsSubmobjects. {caller_name} for a CurvesAsSubmobject with no submobjects"
24622492
)
24632493

24642494
def _get_submobjects_with_points(self):
@@ -2468,7 +2498,7 @@ def _get_submobjects_with_points(self):
24682498
if len(submobjs_with_pts) == 0:
24692499
caller_name = sys._getframe(1).f_code.co_name
24702500
raise Exception(
2471-
f"Cannot call CurvesAsSubmobjects.{caller_name} for a CurvesAsSubmobject whose submobjects have no points"
2501+
f"Cannot call CurvesAsSubmobjects. {caller_name} for a CurvesAsSubmobject whose submobjects have no points"
24722502
)
24732503
return submobjs_with_pts
24742504

Binary file not shown.

tests/test_graphical_units/test_mobjects.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,22 @@ def test_vmobject_joint_types(scene):
5353

5454
lines.arrange(RIGHT, buff=1)
5555
scene.add(lines)
56+
57+
58+
@frames_comparison
59+
def test_vmobject_cap_styles(scene):
60+
arcs = VGroup(
61+
*[
62+
Arc(
63+
radius=1,
64+
start_angle=0,
65+
angle=TAU / 4,
66+
stroke_width=20,
67+
color=GREEN,
68+
cap_style=cap_style,
69+
)
70+
for cap_style in CapStyleType
71+
]
72+
)
73+
arcs.arrange(RIGHT, buff=1)
74+
scene.add(arcs)

0 commit comments

Comments
 (0)