8000 Merge pull request #23208 from rcomer/stripey-lines · matplotlib/matplotlib@c19ffdf · GitHub
[go: up one dir, main page]

Skip to content

Commit c19ffdf

Browse files
authored
Merge pull request #23208 from rcomer/stripey-lines
ENH: enable stripey lines
2 parents 6e5a541 + 83240b0 commit c19ffdf

File tree

5 files changed

+106
-8
lines changed

5 files changed

+106
-8
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Striped lines (experimental)
2+
----------------------------
3+
4+
New *gapcolor* parameter enables the creation of striped lines.
5+
6+
.. plot::
7+
:include-source: true
8+
9+
import matplotlib.pyplot as plt
10+
import numpy as np
11+
12+
x = np.linspace(1., 3., 10)
13+
y = x**3
14+
15+
fig, ax = plt.subplots()
16+
ax.plot(x, y, linestyle='--', color='orange', gapcolor='blue',
17+
linewidth=3, label='a striped line')
18+
ax.legend()
19+
plt.show()

examples/lines_bars_and_markers/line_demo_dash_control.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,33 @@
1717
:doc:`property_cycle </tutorials/intermediate/color_cycle>`
1818
by passing a list of dash sequences using the keyword *dashes* to the
1919
cycler. This is not shown within this example.
20+
21+
Other attributes of the dash may also be set either with the relevant method
22+
(`~.Line2D.set_dash_capstyle`, `~.Line2D.set_dash_joinstyle`,
23+
`~.Line2D.set_gapcolor`) or by passing the property through a plotting
24+
function.
2025
"""
2126
import numpy as np
2227
import matplotlib.pyplot as plt
2328

2429
x = np.linspace(0, 10, 500)
2530
y = np.sin(x)
2631

32+
plt.rc('lines', linewidth=2.5)
2733
fig, ax = plt.subplots()
2834

29-
# Using set_dashes() to modify dashing of an existing line
30-
line1, = ax.plot(x, y, label='Using set_dashes()')
31-
line1.set_dashes([2, 2, 10, 2]) # 2pt line, 2pt break, 10pt line, 2pt break
35+
# Using set_dashes() and set_capstyle() to modify dashing of an existing line.
36+
line1, = ax.plot(x, y, label='Using set_dashes() and set_dash_capstyle()')
37+
line1.set_dashes([2, 2, 10, 2]) # 2pt line, 2pt break, 10pt line, 2pt break.
38+
line1.set_dash_capstyle('round')
3239

33-
# Using plot(..., dashes=...) to set the dashing when creating a line
40+
# Using plot(..., dashes=...) to set the dashing when creating a line.
3441
line2, = ax.plot(x, y - 0.2, dashes=[6, 2], label='Using the dashes parameter')
3542

36-
ax.legend()
43+
# Using plot(..., dashes=..., gapcolor=...) to set the dashing and
44+
# alternating color when creating a line.
45+
line3, = ax.plot(x, y - 0.4, dashes=[4, 4], gapcolor='tab:pink',
46+
label='Using the dashes and gapcolor parameters')
47+
48+
ax.legend(handlelength=4)
3749
plt.show()

lib/matplotlib/lines.py

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ def __init__(self, xdata, ydata,
273273
linewidth=None, # all Nones default to rc
274274
linestyle=None,
275275
color=None,
276+
gapcolor=None,
276277
marker=None,
277278
markersize=None,
278279
markeredgewidth=None,
@@ -365,6 +366,9 @@ def __init__(self, xdata, ydata,
365366
else:
366367
self._marker = marker
367368

369+
self._gapcolor = None
370+
self.set_gapcolor(gapcolor)
371+
368372
self._markevery = None
369373
self._markersize = None
370374
self._antialiased = None
@@ -755,9 +759,6 @@ def draw(self, renderer):
755759
self._set_gc_clip(gc)
756760
gc.set_url(self.get_url())
757761

758-
lc_rgba = mcolors.to_rgba(self._color, self._alpha)
759-
gc.set_foreground(lc_rgba, isRGBA=True)
760-
761762
gc.set_antialiased(self._antialiased)
762763
gc.set_linewidth(self._linewidth)
763764

@@ -773,6 +774,26 @@ def draw(self, renderer):
773774
if self.get_sketch_params() is not None:
774775
gc.set_sketch_params(*self.get_sketch_params())
775776

777+
# We first draw a path within the gaps if needed.
778+
if self.is_dashed() and self._gapcolor is not None:
779+
lc_rgba = mcolors.to_rgba(self._gapcolor, self._alpha)
780+
gc.set_foreground(lc_rgba, isRGBA=True)
781+
782+
# Define the inverse pattern by moving the last gap to the
783+
# start of the sequence.
784+
dashes = self._dash_pattern[1]
785+
gaps = dashes[-1:] + dashes[:-1]
786+
# Set the offset so that this new first segment is skipped
787+
# (see backend_bases.GraphicsContextBase.set_dashes for
788+
# offset definition).
789+
offset_gaps = self._dash_pattern[0] + dashes[-1]
790+
791+
gc.set_dashes(offset_gaps, gaps)
792+
renderer.draw_path(gc, tpath, affine.frozen())
793+
794+
lc_rgba = mcolors.to_rgba(self._color, self._alpha)
795+
gc.set_foreground(lc_rgba, isRGBA=True)
796+
776797
gc.set_dashes(*self._dash_pattern)
777798
renderer.draw_path(gc, tpath, affine.frozen())
778799
gc.restore()
@@ -877,6 +898,14 @@ def get_drawstyle(self):
877898
"""
878899
return self._drawstyle
879900

901+
def get_gapcolor(self):
902+
"""
903+
Return the line gapcolor.
904+
905+
See also `~.Line2D.set_gapcolor`.
906+
"""
907+
return self._gapcolor
908+
880909
def get_linestyle(self):
881910
"""
882911
Return the linestyle.
@@ -1067,6 +1096,29 @@ def set_drawstyle(self, drawstyle):
10671096
self._invalidx = True
10681097
self._drawstyle = drawstyle
10691098

1099+
def set_gapcolor(self, gapcolor):
1100+
"""
1101+
Set a color to fill the gaps in the dashed line style.
1102+
1103+
.. note::
1104+
1105+
Striped lines are created by drawing two interleaved dashed lines.
1106+
There can be overlaps between those two, which may result in
1107+
artifacts when using transparency.
1108+
1109+
This functionality is experimental and may change.
1110+
1111+
Parameters
1112+
----------
1113+
gapcolor : color or None
1114+
The color with which to fill the gaps. If None, the gaps are
1115+
unfilled.
1116+
"""
1117+
if gapcolor is not None:
1118+
mcolors._check_color_like(color=gapcolor)
1119+
self._gapcolor = gapcolor
1120+
self.stale = True
1121+
10701122
def set_linewidth(self, w):
10711123
"""
10721124
Set the line width in points.
@@ -1248,6 +1300,9 @@ def set_dashes(self, seq):
12481300
For example, (5, 2, 1, 2) describes a sequence of 5 point and 1 point
12491301
dashes separated by 2 point spaces.
12501302
1303+
See also `~.Line2D.set_gapcolor`, which allows those spaces to be
1304+
filled with a color.
1305+
12511306
Parameters
12521307
----------
12531308
seq : sequence of floats (on/off ink in points) or (None, None)
@@ -1265,6 +1320,7 @@ def update_from(self, other):
12651320
self._linestyle = other._linestyle
12661321
self._linewidth = other._linewidth
12671322
self._color = other._color
1323+
self._gapcolor = other._gapcolor
12681324
self._markersize = other._markersize
12691325
self._markerfacecolor = other._markerfacecolor
12701326
self._markerfacecoloralt = other._markerfacecoloralt
Loading

lib/matplotlib/tests/test_lines.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,17 @@ def test_marker_as_markerstyle():
304304
assert_array_equal(line3.get_marker().vertices, triangle1.vertices)
305305

306306

307+
@image_comparison(['striped_line.png'], remove_text=True, style='mpl20')
308+
def test_striped_lines():
309+
rng = np.random.default_rng(19680801)
310+
_, ax = plt.subplots()
311+
ax.plot(rng.uniform(size=12), color='orange', gapcolor='blue',
312+
linestyle='--', lw=5, label=' ')
313+
ax.plot(rng.uniform(size=12), color='red', gapcolor='black',
314+
linestyle=(0, (2, 5, 4, 2)), lw=5, label=' ', alpha=0.5)
315+
ax.legend(handlelength=5)
316+
< 4CE0 /code>317+
307318
@check_figures_equal()
308319
def test_odd_dashes(fig_test, fig_ref):
309320
fig_test.add_subplot().plot([1, 2], dashes=[1, 2, 3])

0 commit comments

Comments
 (0)
0