Skip to content

Commit 51aa01a

Browse files
jakekinchenpre-commit-ci[bot]behackl
authored
Fix duplicated arrow tips in DashedVMobject (issue #3220) (#4484)
* Fix duplicated arrow tips in DashedVMobject and add tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
1 parent 3bf384a commit 51aa01a

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

manim/mobject/types/vectorized_mobject.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2832,6 +2832,21 @@ def __init__(
28322832
self.dashed_ratio = dashed_ratio
28332833
self.num_dashes = num_dashes
28342834
super().__init__(color=color, **kwargs)
2835+
2836+
# Work on a copy to avoid mutating the caller's mobject (e.g. removing tips).
2837+
base_vmobject = vmobject
2838+
vmobject = base_vmobject.copy()
2839+
2840+
# TipableVMobject instances (Arrow, Vector, etc.) carry tips as submobjects.
2841+
# When dashing such objects, each subcurve would otherwise include its own
2842+
# tip, leading to many overlapping arrowheads. Pop tips from the working
2843+
# copy and re-attach them only once after the dashes are created.
2844+
tips = None
2845+
if hasattr(vmobject, "pop_tips"):
2846+
popped_tips = vmobject.pop_tips()
2847+
if len(popped_tips.submobjects) > 0:
2848+
tips = popped_tips
2849+
28352850
r = self.dashed_ratio
28362851
n = self.num_dashes
28372852
if n > 0:
@@ -2917,6 +2932,9 @@ def __init__(
29172932
# Family is already taken care of by get_subcurve
29182933
# implementation
29192934
if config.renderer == RendererType.OPENGL:
2920-
self.match_style(vmobject, recurse=False)
2935+
self.match_style(base_vmobject, recurse=False)
29212936
else:
2922-
self.match_style(vmobject, family=False)
2937+
self.match_style(base_vmobject, family=False)
2938+
2939+
if tips is not None:
2940+
self.add(*tips.submobjects)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from manim import ORIGIN, UR, Arrow, DashedVMobject, VGroup
2+
from manim.mobject.geometry.tips import ArrowTip, StealthTip
3+
4+
5+
def _collect_tips(mobject):
6+
return [mob for mob in mobject.get_family() if isinstance(mob, ArrowTip)]
7+
8+
9+
def test_dashed_arrow_has_single_tip():
10+
dashed = DashedVMobject(Arrow(ORIGIN, 2 * UR))
11+
tips = _collect_tips(dashed)
12+
13+
assert len(tips) == 1
14+
15+
16+
def test_dashed_arrow_tip_not_duplicated_in_group_opacity():
17+
base_arrow = Arrow(ORIGIN, 2 * UR)
18+
faded_arrow = base_arrow.copy().set_fill(opacity=0.4).set_stroke(opacity=0.4)
19+
20+
dashed_group = (
21+
VGroup(DashedVMobject(faded_arrow))
22+
.set_fill(opacity=0.4, family=True)
23+
.set_stroke(opacity=0.4, family=True)
24+
)
25+
26+
tips = _collect_tips(dashed_group)
27+
28+
assert len(tips) == 1
29+
30+
31+
def test_dashed_arrow_custom_tip_shape_has_single_tip():
32+
dashed = DashedVMobject(Arrow(ORIGIN, 2 * UR, tip_shape=StealthTip))
33+
tips = _collect_tips(dashed)
34+
35+
assert len(tips) == 1
36+
assert isinstance(tips[0], StealthTip)
37+
38+
39+
def test_dashed_arrow_with_start_tip_has_two_tips():
40+
dashed = DashedVMobject(Arrow(ORIGIN, 2 * UR).add_tip(at_start=True))
41+
tips = _collect_tips(dashed)
42+
43+
assert len(tips) == 2

0 commit comments

Comments
 (0)