Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d5ec47d

Browse files
committed
REORG: JoinStyle and CapStyle classes
Centralize docs and validation for JoinStyle and CapStyle in one place.
1 parent c5ab728 commit d5ec47d

16 files changed

+325
-143
lines changed

doc/api/_types.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
**********************
2+
``matplotlib._types``
3+
**********************
4+
5+
.. automodule:: matplotlib._types
6+
:members:
7+
:undoc-members:
8+
:show-inheritance:
9+

doc/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ Matplotlib consists of the following submodules:
123123
transformations.rst
124124
tri_api.rst
125125
type1font.rst
126+
_types.rst
126127
units_api.rst
127128
widgets_api.rst
128129

lib/matplotlib/_types.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
"""
2+
Style desription information that is shared across unrelated classses.
3+
"""
4+
5+
from enum import Enum
6+
from matplotlib import cbook
7+
8+
9+
def _deprecate_case_insensitive_join_cap(s):
10+
s_low = s.lower()
11+
if s != s_low:
12+
if s_low in ['miter', 'round', 'bevel']:
13+
cbook.warn_deprecated(
14+
"3.3", message="Case-insensitive capstyles are deprecated "
15+
"since %(since)s and support for them will be removed "
16+
"%(removal)s; please pass them in lowercase.")
17+
elif s_low in ['butt', 'round', 'projecting']:
18+
cbook.warn_deprecated(
19+
"3.3", message="Case-insensitive joinstyles are deprecated "
20+
"since %(since)s and support for them will be removed "
21+
"%(removal)s; please pass them in lowercase.")
22+
# Else, error out at the check_in_list stage.
23+
return s_low
24+
25+
26+
class JoinStyle(Enum):
27+
"""
28+
Define how the connection between two line segments is drawn.
29+
30+
For a simple visual description of each *JoinStyle*, `view these docs
31+
online <JoinStyle>`, or simply run `JoinStyle.demo`.
32+
33+
.. plot::
34+
:alt: Demo of possible JoinStyle's
35+
36+
from matplotlib._types import JoinStyle
37+
JoinStyle.demo()
38+
39+
For a more precise description, we first recall that lines in Matplotlib
40+
are typically defined by a 1D `~.path.Path` and a finite ``linewidth``,
41+
where the underlying 1D `~.path.Path` represents the center of the stroked
42+
line.
43+
44+
By default, `~.backend_bases.GraphicsContextBase` defines the boundaries of
45+
a stroked line to simply be every point within some radius,
46+
``linewidth/2``, away from any point of the center line. However, this
47+
results in corners appearing "rounded", which may not be the desired
48+
behavior if you are drawing, for example, a polygon or pointed star.
49+
50+
Matplotlib provides three options for defining how the corners where two
51+
segments meet should be drawn. In short:
52+
53+
- *miter* is the "arrow-tip" style. Each boundary of the filled-in area
54+
will extend in a straight line parallel to the tangent vector of the
55+
centerline at the point it meets the corner, until they meet in a
56+
sharp point.
57+
- *round* stokes every point within a radius of ``linewidth/2`` of the
58+
center lines.
59+
- *bevel* is the "squared-off" style. It can be thought of as a rounded
60+
corner where the "circular" part of the corner has been cut off.
61+
62+
.. note::
63+
64+
The *miter* option can be controller further by specifying a
65+
"miterlimit" in most backends, which specifies how long a miter tip can
66+
get before it is automatically "bevel"ed off. Matplotlib does not
67+
currently expose this miterlimit parameter to the user, and most
68+
backends simply use the upstream default value. For example, the PDF
69+
backend assumes the default value of 10 specified by the PDF standard,
70+
while the SVG backend does not even specify the miter limit, resulting
71+
in a default value of 4 per the SVG specification.
72+
73+
See
74+
`https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit`
75+
for a more detailed description of the miter limit.
76+
"""
77+
78+
miter = 'miter'
79+
round = 'round'
80+
bevel = 'bevel'
81+
82+
def __init__(self, s):
83+
s = _deprecate_case_insensitive_join_cap(s)
84+
Enum.__init__(self)
85+
86+
@staticmethod
87+
def demo():
88+
import numpy as np
89+
import matplotlib.pyplot as plt
90+
91+
def plot_angle(ax, x, y, angle, style):
92+
phi = np.radians(angle)
93+
xx = [x + .5, x, x + .5*np.cos(phi)]
94+
yy = [y, y, y + .5*np.sin(phi)]
95+
ax.plot(xx, yy, lw=12, color='tab:blue', solid_joinstyle=style)
96+
ax.plot(xx, yy, lw=1, color='black')
97+
ax.plot(xx[1], yy[1], 'o', color='tab:red', markersize=3)
98+
99+
fig, ax = plt.subplots(figsize=(8, 6))
100+
ax.set_title('Join style')
101+
for x, style in enumerate(['miter', 'round', 'bevel']):
102+
ax.text(x, 5, style)
103+
for y, angle in enumerate([20, 45, 60, 90, 120]):
104+
plot_angle(ax, x, y, angle, style)
105+
if x == 0:
106+
ax.text(-1.3, y, f'{angle} degrees')
107+
ax.set_xlim(-1.5, 2.75)
108+
ax.set_ylim(-.5, 5.5)
109+
ax.set_axis_off()
110+
111+
plt.show()
112+
113+
114+
class CapStyle(Enum):
115+
"""
116+
Define how the the end of a line is drawn.
117+
118+
How to draw the start and end points of lines that represent a closed curve
119+
(i.e. that end in a `~.path.Path.CLOSEPOLY`) is controlled by the line's
120+
`JoinStyle`. For all other lines, how the start and end points are drawn is
121+
controlled by the *CapStyle*.
122+
123+
For a simple visual description of each *CapStyle*, `view these docs
124+
online <CapStyle>` or simply run `CapStyle.demo`.
125+
126+
.. plot::
127+
:alt: Demo of possible CapStyle's
128+
129+
from matplotlib._types import CapStyle
130+
CapStyle.demo()
131+
132+
Briefly, the three options available are:
133+
134+
- *butt*: the line is squared off at its endpoint.
135+
- *projecting*: the line is squared off as in *butt*, but the filled in
136+
area extends beyond the endpoint a distance of ``linewidth/2``.
137+
- *round*: like *butt*, but a semicircular cap is added to the end of
138+
the line, of radius ``linewidth/2``.
139+
"""
140+
butt = 'butt'
141+
projecting = 'projecting'
142+
round = 'round'
143+
144+
def __init__(self, s):
145+
s = _deprecate_case_insensitive_join_cap(s)
146+
Enum.__init__(self)
147+
148+
@staticmethod
149+
def demo():
150+
import matplotlib.pyplot as plt
151+
152+
fig, ax = plt.subplots(figsize=(8, 2))
153+
ax.set_title('Cap style')
154+
155+
for x, style in enumerate(['butt', 'round', 'projecting']):
156+
ax.text(x+0.25, 1, style, ha='center')
157+
xx = [x, x+0.5]
158+
yy = [0, 0]
159+
ax.plot(xx, yy, lw=12, color='tab:blue', solid_capstyle=style)
160+
ax.plot(xx, yy, lw=1, color='black')
161+
ax.plot(xx, yy, 'o', color='tab:red', markersize=3)
162+
ax.text(2.25, 0.7, '(default)', ha='center')
163+
164+
ax.set_ylim(-.5, 1.5)
165+
ax.set_axis_off()

lib/matplotlib/backend_bases.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
from matplotlib.backend_managers import ToolManager
5050
from matplotlib.cbook import _setattr_cm
5151
from matplotlib.path import Path
52-
from matplotlib.rcsetup import validate_joinstyle, validate_capstyle
5352
from matplotlib.transforms import Affine2D
53+
from matplotlib._types import JoinStyle, CapStyle
5454

5555

5656
_log = logging.getLogger(__name__)
@@ -764,11 +764,11 @@ def __init__(self):
764764
self._alpha = 1.0
765765
self._forced_alpha = False # if True, _alpha overrides A from RGBA
766766
self._antialiased = 1 # use 0, 1 not True, False for extension code
767-
self._capstyle = 'butt'
767+
self._capstyle = CapStyle('butt')
768768
self._cliprect = None
769769
self._clippath = None
770770
self._dashes = 0, None
771-
self._joinstyle = 'round'
771+
self._joinstyle = JoinStyle('round')
772772
self._linestyle = 'solid'
773773
self._linewidth = 1
774774
self._rgb = (0.0, 0.0, 0.0, 1.0)
@@ -820,7 +820,7 @@ def get_antialiased(self):
820820

821821
def get_capstyle(self):
822822
"""
823-
Return the capstyle as a string in ('butt', 'round', 'projecting').
823+
Return the default `.CapStyle`.
824824
"""
825825
return self._capstyle
826826

@@ -866,7 +866,7 @@ def get_forced_alpha(self):
866866
return self._forced_alpha
867867

868868
def get_joinstyle(self):
869-
"""Return the line join style as one of ('miter', 'round', 'bevel')."""
869+
"""Return the `.JoinStyle`."""
870870
return self._joinstyle
871871

872872
def get_linewidth(self):
@@ -919,9 +919,14 @@ def set_antialiased(self, b):
919919
self._antialiased = int(bool(b))
920920

921921
def set_capstyle(self, cs):
922-
"""Set the capstyle to be one of ('butt', 'round', 'projecting')."""
923-
validate_capstyle(cs)
924-
self._capstyle = cs
922+
"""Set the `.CapStyle`.
923+
924+
Parameters
925+
----------
926+
cs : `.CapStyle` or {'butt', 'round', 'projecting'}
927+
How to draw end points of lines.
928+
"""
929+
self._capstyle = CapStyle(cs)
925930

926931
def set_clip_rectangle(self, rectangle):
927932
"""
@@ -987,9 +992,12 @@ def set_foreground(self, fg, isRGBA=False):
987992
self._rgb = colors.to_rgba(fg)
988993

989994
def set_joinstyle(self, js):
990-
"""Set the join style to be one of ('miter', 'round', 'bevel')."""
991-
validate_joinstyle(js)
992-
self._joinstyle = js
995+
"""Set the `.JoinStyle`.
996+
997+
Parameters
998+
----------
999+
js : `.JoinStyle` or {'miter', 'round', 'bevel'}."""
1000+
self._joinstyle = JoinStyle(js)
9931001

9941002
def set_linewidth(self, w):
9951003
"""Set the linewidth in points."""

lib/matplotlib/backends/backend_pdf.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,23 @@
2626
import matplotlib as mpl
2727
from matplotlib import _text_layout, cbook
2828
from matplotlib._pylab_helpers import Gcf
29+
from matplotlib.afm import AFM
2930
from matplotlib.backend_bases import (
3031
_Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase,
3132
GraphicsContextBase, RendererBase)
3233
from matplotlib.backends.backend_mixed import MixedModeRenderer
34+
from matplotlib.dates import UTC
35+
import matplotlib.dviread as dviread
3336
from matplotlib.figure import Figure
3437
from matplotlib.font_manager import findfont, is_opentype_cff_font, get_font
35-
from matplotlib.afm import AFM
36-
import matplotlib.type1font as type1font
37-
import matplotlib.dviread as dviread
3838
from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE,
3939
LOAD_NO_HINTING, KERNING_UNFITTED)
4040
from matplotlib.mathtext import MathTextParser
41-
from matplotlib.transforms import Affine2D, BboxBase
42-
from matplotlib.path import Path
43-
from matplotlib.dates import UTC
4441
from matplotlib import _path
42+
from matplotlib.path import Path
43+
from matplotlib._types import JoinStyle, CapStyle
44+
import matplotlib.type1font as type1font
45+
from matplotlib.transforms import Affine2D, BboxBase
4546
from . import _backend_pdf_ps
4647

4748
_log = logging.getLogger(__name__)
@@ -738,7 +739,8 @@ def newPage(self, width, height):
738739
self.reserveObject('length of content stream'))
739740
# Initialize the pdf graphics state to match the default mpl
740741
# graphics context: currently only the join style needs to be set
741-
self.output(GraphicsContextPdf.joinstyles['round'], Op.setlinejoin)
742+
self.output(GraphicsContextPdf.joinstyles[JoinStyle.round],
743+
Op.setlinejoin)
742744

743745
# Clear the list of annotations for the next page
744746
self.pageAnnotations = []
@@ -2377,8 +2379,8 @@ def paint(self):
23772379
"""
23782380
return Op.paint_path(self.fill(), self.stroke())
23792381

2380-
capstyles = {'butt': 0, 'round': 1, 'projecting': 2}
2381-
joinstyles = {'miter': 0, 'round': 1, 'bevel': 2}
2382+
capstyles = {CapStyle.butt: 0, CapStyle.round: 1, CapStyle.projecting: 2}
2383+
joinstyles = {JoinStyle.miter: 0, JoinStyle.round: 1, JoinStyle.bevel: 2}
23822384

23832385
def capstyle_cmd(self, style):
23842386
return [self.capstyles[style], Op.setlinecap]

lib/matplotlib/backends/backend_pgf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -534,13 +534,13 @@ def _print_pgf_path_styles(self, gc, rgbFace):
534534
capstyles = {"butt": r"\pgfsetbuttcap",
535535
"round": r"\pgfsetroundcap",
536536
"projecting": r"\pgfsetrectcap"}
537-
writeln(self.fh, capstyles[gc.get_capstyle()])
537+
writeln(self.fh, capstyles[gc.get_capstyle().name])
538538

539539
# join style
540540
joinstyles = {"miter": r"\pgfsetmiterjoin",
541541
"round": r"\pgfsetroundjoin",
542542
"bevel": r"\pgfsetbeveljoin"}
543-
writeln(self.fh, joinstyles[gc.get_joinstyle()])
543+
writeln(self.fh, joinstyles[gc.get_joinstyle().name])
544544

545545
# filling
546546
has_fill = rgbFace is not None

lib/matplotlib/backends/backend_ps.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,17 @@
2424
from matplotlib.backend_bases import (
2525
_Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase,
2626
GraphicsContextBase, RendererBase)
27+
from matplotlib.backends.backend_mixed import MixedModeRenderer
2728
from matplotlib.cbook import is_writable_file_like, file_requires_unicode
2829
from matplotlib.font_manager import is_opentype_cff_font, get_font
2930
from matplotlib.ft2font import LOAD_NO_HINTING
3031
from matplotlib._ttconv import convert_ttf_to_ps
3132
from matplotlib.mathtext import MathTextParser
3233
from matplotlib._mathtext_data import uni2type1
3334
from matplotlib.path import Path
35+
from matplotlib._types import JoinStyle, CapStyle
3436
from matplotlib.texmanager import TexManager
3537
from matplotlib.transforms import Affine2D
36-
from matplotlib.backends.backend_mixed import MixedModeRenderer
3738
from . import _backend_pdf_ps
3839

3940
_log = logging.getLogger(__name__)
@@ -760,11 +761,16 @@ def _is_transparent(rgb_or_rgba):
760761

761762

762763
class GraphicsContextPS(GraphicsContextBase):
764+
765+
_capstyles = {CapStyle.butt: 0, CapStyle.round: 1, CapStyle.projecting: 2}
766+
763767
def get_capstyle(self):
764-
return {'butt': 0, 'round': 1, 'projecting': 2}[super().get_capstyle()]
768+
return self._capstyles[super().get_capstyle()]
769+
770+
_joinstyles = {JoinStyle.miter: 0, JoinStyle.round: 1, JoinStyle.bevel: 2}
765771

766772
def get_joinstyle(self):
767-
return {'miter': 0, 'round': 1, 'bevel': 2}[super().get_joinstyle()]
773+
return self._joinstyles[super().get_joinstyle()]
768774

769775

770776
class _Orientation(Enum):

lib/matplotlib/backends/backend_svg.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from matplotlib.path import Path
2828
from matplotlib import _path
2929
from matplotlib.transforms import Affine2D, Affine2DBase
30+
from matplotlib._types import JoinStyle, CapStyle
3031

3132
_log = logging.getLogger(__name__)
3233

@@ -571,10 +572,10 @@ def _get_style_dict(self, gc, rgbFace):
571572
attrib['stroke-opacity'] = short_float_fmt(rgb[3])
572573
if linewidth != 1.0:
573574
attrib['stroke-width'] = short_float_fmt(linewidth)
574-
if gc.get_joinstyle() != 'round':
575-
attrib['stroke-linejoin'] = gc.get_joinstyle()
576-
if gc.get_capstyle() != 'butt':
577-
attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle()]
575+
if gc.get_joinstyle() != JoinStyle.round:
576+
attrib['stroke-linejoin'] = gc.get_joinstyle().name
577+
if gc.get_capstyle() != CapStyle.butt:
578+
attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle().name]
578579

579580
return attrib
580581

0 commit comments

Comments
 (0)