Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions manim/mobject/types/vectorized_mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -2832,6 +2832,21 @@ def __init__(
self.dashed_ratio = dashed_ratio
self.num_dashes = num_dashes
super().__init__(color=color, **kwargs)

# Work on a copy to avoid mutating the caller's mobject (e.g. removing tips).
base_vmobject = vmobject
vmobject = base_vmobject.copy()

# TipableVMobject instances (Arrow, Vector, etc.) carry tips as submobjects.
# When dashing such objects, each subcurve would otherwise include its own
# tip, leading to many overlapping arrowheads. Pop tips from the working
# copy and re-attach them only once after the dashes are created.
tips = None
if hasattr(vmobject, "pop_tips"):
popped_tips = vmobject.pop_tips()
if len(popped_tips.submobjects) > 0:
tips = popped_tips

r = self.dashed_ratio
n = self.num_dashes
if n > 0:
Expand Down Expand Up @@ -2917,6 +2932,9 @@ def __init__(
# Family is already taken care of by get_subcurve
# implementation
if config.renderer == RendererType.OPENGL:
self.match_style(vmobject, recurse=False)
self.match_style(base_vmobject, recurse=False)
else:
self.match_style(vmobject, family=False)
self.match_style(base_vmobject, family=False)

if tips is not None:
self.add(*tips.submobjects)
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from manim import ORIGIN, UR, Arrow, DashedVMobject, VGroup
from manim.mobject.geometry.tips import ArrowTip, StealthTip


def _collect_tips(mobject):
return [mob for mob in mobject.get_family() if isinstance(mob, ArrowTip)]


def test_dashed_arrow_has_single_tip():
dashed = DashedVMobject(Arrow(ORIGIN, 2 * UR))
tips = _collect_tips(dashed)

assert len(tips) == 1


def test_dashed_arrow_tip_not_duplicated_in_group_opacity():
base_arrow = Arrow(ORIGIN, 2 * UR)
faded_arrow = base_arrow.copy().set_fill(opacity=0.4).set_stroke(opacity=0.4)

dashed_group = (
VGroup(DashedVMobject(faded_arrow))
.set_fill(opacity=0.4, family=True)
.set_stroke(opacity=0.4, family=True)
)

tips = _collect_tips(dashed_group)

assert len(tips) == 1


def test_dashed_arrow_custom_tip_shape_has_single_tip():
dashed = DashedVMobject(Arrow(ORIGIN, 2 * UR, tip_shape=StealthTip))
tips = _collect_tips(dashed)

assert len(tips) == 1
assert isinstance(tips[0], StealthTip)


def test_dashed_arrow_with_start_tip_has_two_tips():
dashed = DashedVMobject(Arrow(ORIGIN, 2 * UR).add_tip(at_start=True))
tips = _collect_tips(dashed)

assert len(tips) == 2
Loading