diff --git a/doc/_static/mpl.css b/doc/_static/mpl.css index 0d99cf930ef2..a6499284b64e 100644 --- a/doc/_static/mpl.css +++ b/doc/_static/mpl.css @@ -89,7 +89,7 @@ table.highlighttable td { padding: 0 0.5em 0 0.5em; } -cite, code, tt { +cite, code, tt, dl.value-list dt { font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.95em; letter-spacing: 0.01em; @@ -730,7 +730,6 @@ td.field-body table.property-table tr:last-of-type td { border-bottom-color: #888; } - /* function and class description */ .descclassname { color: #aaa; @@ -806,6 +805,22 @@ dl.class > dd { font-size: 14px; } +/* custom tables for lists of allowed values in "mpl._types" */ +dl.value-list { + display: grid; +} + +dl.value-list dt { + grid-column: 1; + margin: 4px 0; +} + +dl.value-list dd { + grid-column: 2; + margin: 4px 0 4px 20px; + padding: 0; +} + /* parameter section table */ table.docutils.field-list { width: 100%; @@ -1257,4 +1272,4 @@ div.bullet-box li { div#gallery.section .sphx-glr-clear:first-of-type, div#tutorials.section .sphx-glr-clear:first-of-type{ display: none; -} \ No newline at end of file +} diff --git a/doc/api/_enums.rst b/doc/api/_enums.rst new file mode 100644 index 000000000000..c9e283305967 --- /dev/null +++ b/doc/api/_enums.rst @@ -0,0 +1,15 @@ +********************** +``matplotlib._enums`` +********************** + +.. automodule:: matplotlib._enums + :no-members: + + .. autoclass:: JoinStyle + :members: demo + :exclude-members: bevel, miter, round, input_description + + .. autoclass:: CapStyle + :members: demo + :exclude-members: butt, round, projecting, input_description + diff --git a/doc/api/index.rst b/doc/api/index.rst index dba86c35ad0a..e783ea39a35d 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -89,6 +89,7 @@ Matplotlib consists of the following submodules: dates_api.rst docstring_api.rst dviread.rst + _enums.rst figure_api.rst font_manager_api.rst fontconfig_pattern_api.rst diff --git a/examples/lines_bars_and_markers/capstyle.py b/examples/lines_bars_and_markers/capstyle.py new file mode 100644 index 000000000000..05bb1e96585c --- /dev/null +++ b/examples/lines_bars_and_markers/capstyle.py @@ -0,0 +1,15 @@ +""" +========= +CapStyle +========= + +The `matplotlib._enums.CapStyle` controls how Matplotlib draws the corners +where two different line segments meet. For more details, see the +`~matplotlib._enums.CapStyle` docs. +""" + +import matplotlib.pyplot as plt +from matplotlib._enums import CapStyle + +CapStyle.demo() +plt.show() diff --git a/examples/lines_bars_and_markers/joinstyle.py b/examples/lines_bars_and_markers/joinstyle.py index dcc47105d12f..0f289d7ac393 100644 --- a/examples/lines_bars_and_markers/joinstyle.py +++ b/examples/lines_bars_and_markers/joinstyle.py @@ -1,90 +1,15 @@ """ -========================== -Join styles and cap styles -========================== +========= +JoinStyle +========= -This example demonstrates the available join styles and cap styles. - -Both are used in `.Line2D` and various ``Collections`` from -`matplotlib.collections` as well as some functions that create these, e.g. -`~matplotlib.pyplot.plot`. - - -Join styles -=========== - -Join styles define how the connection between two line segments is drawn. - -See the respective ``solid_joinstyle``, ``dash_joinstyle`` or ``joinstyle`` -parameters. +The `matplotlib._enums.JoinStyle` controls how Matplotlib draws the corners +where two different line segments meet. For more details, see the +`~matplotlib._enums.JoinStyle` docs. """ -import numpy as np import matplotlib.pyplot as plt +from matplotlib._enums import JoinStyle - -def plot_angle(ax, x, y, angle, style): - phi = np.radians(angle) - xx = [x + .5, x, x + .5*np.cos(phi)] - yy = [y, y, y + .5*np.sin(phi)] - ax.plot(xx, yy, lw=12, color='tab:blue', solid_joinstyle=style) - ax.plot(xx, yy, lw=1, color='black') - ax.plot(xx[1], yy[1], 'o', color='tab:red', markersize=3) - - -fig, ax = plt.subplots(figsize=(8, 6)) -ax.set_title('Join style') - -for x, style in enumerate(['miter', 'round', 'bevel']): - ax.text(x, 5, style) - for y, angle in enumerate([20, 45, 60, 90, 120]): - plot_angle(ax, x, y, angle, style) - if x == 0: - ax.text(-1.3, y, f'{angle} degrees') -ax.text(1, 4.7, '(default)') - -ax.set_xlim(-1.5, 2.75) -ax.set_ylim(-.5, 5.5) -ax.set_axis_off() +JoinStyle.demo() plt.show() - - -############################################################################# -# -# Cap styles -# ========== -# -# Cap styles define how the the end of a line is drawn. -# -# See the respective ``solid_capstyle``, ``dash_capstyle`` or ``capstyle`` -# parameters. - -fig, ax = plt.subplots(figsize=(8, 2)) -ax.set_title('Cap style') - -for x, style in enumerate(['butt', 'round', 'projecting']): - ax.text(x+0.25, 1, style, ha='center') - xx = [x, x+0.5] - yy = [0, 0] - ax.plot(xx, yy, lw=12, color='tab:blue', solid_capstyle=style) - ax.plot(xx, yy, lw=1, color='black') - ax.plot(xx, yy, 'o', color='tab:red', markersize=3) -ax.text(2.25, 0.7, '(default)', ha='center') - -ax.set_ylim(-.5, 1.5) -ax.set_axis_off() - - -############################################################################# -# -# ------------ -# -# References -# """""""""" -# -# The use of the following functions, methods, classes and modules is shown -# in this example: - -import matplotlib -matplotlib.axes.Axes.plot -matplotlib.pyplot.plot diff --git a/lib/matplotlib/_enums.py b/lib/matplotlib/_enums.py new file mode 100644 index 000000000000..35fe82482869 --- /dev/null +++ b/lib/matplotlib/_enums.py @@ -0,0 +1,208 @@ +""" +Enums representing sets of strings that Matplotlib uses as input parameters. + +Matplotlib often uses simple data types like strings or tuples to define a +concept; e.g. the line capstyle can be specified as one of 'butt', 'round', +or 'projecting'. The classes in this module are used internally and serve to +document these concepts formally. + +As an end-user you will not use these classes directly, but only the values +they define. +""" + +from enum import Enum, auto +from matplotlib import cbook, docstring + + +class _AutoStringNameEnum(Enum): + """Automate the ``name = 'name'`` part of making a (str, Enum).""" + + def _generate_next_value_(name, start, count, last_values): + return name + + def __hash__(self): + return str(self).__hash__() + + +def _deprecate_case_insensitive_join_cap(s): + s_low = s.lower() + if s != s_low: + if s_low in ['miter', 'round', 'bevel']: + cbook.warn_deprecated( + "3.3", message="Case-insensitive capstyles are deprecated " + "since %(since)s and support for them will be removed " + "%(removal)s; please pass them in lowercase.") + elif s_low in ['butt', 'round', 'projecting']: + cbook.warn_deprecated( + "3.3", message="Case-insensitive joinstyles are deprecated " + "since %(since)s and support for them will be removed " + "%(removal)s; please pass them in lowercase.") + # Else, error out at the check_in_list stage. + return s_low + + +class JoinStyle(str, _AutoStringNameEnum): + """ + Define how the connection between two line segments is drawn. + + For a visual impression of each *JoinStyle*, `view these docs online + `, or run `JoinStyle.demo`. + + Lines in Matplotlib are typically defined by a 1D `~.path.Path` and a + finite ``linewidth``, where the underlying 1D `~.path.Path` represents the + center of the stroked line. + + By default, `~.backend_bases.GraphicsContextBase` defines the boundaries of + a stroked line to simply be every point within some radius, + ``linewidth/2``, away from any point of the center line. However, this + results in corners appearing "rounded", which may not be the desired + behavior if you are drawing, for example, a polygon or pointed star. + + **Supported values:** + + .. rst-class:: value-list + + 'miter' + the "arrow-tip" style. Each boundary of the filled-in area will + extend in a straight line parallel to the tangent vector of the + centerline at the point it meets the corner, until they meet in a + sharp point. + 'round' + stokes every point within a radius of ``linewidth/2`` of the center + lines. + 'bevel' + the "squared-off" style. It can be thought of as a rounded corner + where the "circular" part of the corner has been cut off. + + .. note:: + + Very long miter tips are cut off (to form a *bevel*) after a + backend-dependent limit called the "miter limit", which specifies the + maximum allowed ratio of miter length to line width. For example, the + PDF backend uses the default value of 10 specified by the PDF standard, + while the SVG backend does not even specify the miter limit, resulting + in a default value of 4 per the SVG specification. Matplotlib does not + currently allow the user to adjust this parameter. + + A more detailed description of the effect of a miter limit can be found + in the `Mozilla Developer Docs + `_ + + .. plot:: + :alt: Demo of possible JoinStyle's + + from matplotlib._enums import JoinStyle + JoinStyle.demo() + + """ + + miter = auto() + round = auto() + bevel = auto() + + def __init__(self, s): + s = _deprecate_case_insensitive_join_cap(s) + Enum.__init__(self) + + @staticmethod + def demo(): + """Demonstrate how each JoinStyle looks for various join angles.""" + import numpy as np + import matplotlib.pyplot as plt + + def plot_angle(ax, x, y, angle, style): + phi = np.radians(angle) + xx = [x + .5, x, x + .5*np.cos(phi)] + yy = [y, y, y + .5*np.sin(phi)] + ax.plot(xx, yy, lw=12, color='tab:blue', solid_joinstyle=style) + ax.plot(xx, yy, lw=1, color='black') + ax.plot(xx[1], yy[1], 'o', color='tab:red', markersize=3) + + fig, ax = plt.subplots(figsize=(5, 4), constrained_layout=True) + ax.set_title('Join style') + for x, style in enumerate(['miter', 'round', 'bevel']): + ax.text(x, 5, style) + for y, angle in enumerate([20, 45, 60, 90, 120]): + plot_angle(ax, x, y, angle, style) + if x == 0: + ax.text(-1.3, y, f'{angle} degrees') + ax.set_xlim(-1.5, 2.75) + ax.set_ylim(-.5, 5.5) + ax.set_axis_off() + fig.show() + + +JoinStyle.input_description = "{" \ + + ", ".join([f"'{js.name}'" for js in JoinStyle]) \ + + "}" + + +class CapStyle(str, _AutoStringNameEnum): + r""" + Define how the two endpoints (caps) of an unclosed line are drawn. + + How to draw the start and end points of lines that represent a closed curve + (i.e. that end in a `~.path.Path.CLOSEPOLY`) is controlled by the line's + `JoinStyle`. For all other lines, how the start and end points are drawn is + controlled by the *CapStyle*. + + For a visual impression of each *CapStyle*, `view these docs online + ` or run `CapStyle.demo`. + + **Supported values:** + + .. rst-class:: value-list + + 'butt' + the line is squared off at its endpoint. + 'projecting' + the line is squared off as in *butt*, but the filled in area + extends beyond the endpoint a distance of ``linewidth/2``. + 'round' + like *butt*, but a semicircular cap is added to the end of the + line, of radius ``linewidth/2``. + + .. plot:: + :alt: Demo of possible CapStyle's + + from matplotlib._enums import CapStyle + CapStyle.demo() + + """ + butt = 'butt' + projecting = 'projecting' + round = 'round' + + def __init__(self, s): + s = _deprecate_case_insensitive_join_cap(s) + Enum.__init__(self) + + @staticmethod + def demo(): + """Demonstrate how each CapStyle looks for a thick line segment.""" + import matplotlib.pyplot as plt + + fig = plt.figure(figsize=(4, 1.2)) + ax = fig.add_axes([0, 0, 1, 0.8]) + ax.set_title('Cap style') + + for x, style in enumerate(['butt', 'round', 'projecting']): + ax.text(x+0.25, 0.85, style, ha='center') + xx = [x, x+0.5] + yy = [0, 0] + ax.plot(xx, yy, lw=12, color='tab:blue', solid_capstyle=style) + ax.plot(xx, yy, lw=1, color='black') + ax.plot(xx, yy, 'o', color='tab:red', markersize=3) + ax.text(2.25, 0.55, '(default)', ha='center') + + ax.set_ylim(-.5, 1.5) + ax.set_axis_off() + fig.show() + + +CapStyle.input_description = "{" \ + + ", ".join([f"'{cs.name}'" for cs in CapStyle]) \ + + "}" + +docstring.interpd.update({'JoinStyle': JoinStyle.input_description, + 'CapStyle': CapStyle.input_description}) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 8c11f841cb42..b249f5e2a85a 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -44,14 +44,14 @@ import matplotlib as mpl from matplotlib import ( - _api, backend_tools as tools, cbook, colors, textpath, tight_bbox, - transforms, widgets, get_backend, is_interactive, rcParams) + _api, backend_tools as tools, cbook, colors, docstring, textpath, + tight_bbox, transforms, widgets, get_backend, is_interactive, rcParams) from matplotlib._pylab_helpers import Gcf from matplotlib.backend_managers import ToolManager from matplotlib.cbook import _setattr_cm from matplotlib.path import Path -from matplotlib.rcsetup import validate_joinstyle, validate_capstyle from matplotlib.transforms import Affine2D +from matplotlib._enums import JoinStyle, CapStyle _log = logging.getLogger(__name__) @@ -765,11 +765,11 @@ def __init__(self): self._alpha = 1.0 self._forced_alpha = False # if True, _alpha overrides A from RGBA self._antialiased = 1 # use 0, 1 not True, False for extension code - self._capstyle = 'butt' + self._capstyle = CapStyle('butt') self._cliprect = None self._clippath = None self._dashes = 0, None - self._joinstyle = 'round' + self._joinstyle = JoinStyle('round') self._linestyle = 'solid' self._linewidth = 1 self._rgb = (0.0, 0.0, 0.0, 1.0) @@ -820,10 +820,8 @@ def get_antialiased(self): return self._antialiased def get_capstyle(self): - """ - Return the capstyle as a string in ('butt', 'round', 'projecting'). - """ - return self._capstyle + """Return the `.CapStyle`.""" + return self._capstyle.name def get_clip_rectangle(self): """ @@ -867,8 +865,8 @@ def get_forced_alpha(self): return self._forced_alpha def get_joinstyle(self): - """Return the line join style as one of ('miter', 'round', 'bevel').""" - return self._joinstyle + """Return the `.JoinStyle`.""" + return self._joinstyle.name def get_linewidth(self): """Return the line width in points.""" @@ -919,10 +917,16 @@ def set_antialiased(self, b): # Use ints to make life easier on extension code trying to read the gc. self._antialiased = int(bool(b)) + @docstring.interpd def set_capstyle(self, cs): - """Set the capstyle to be one of ('butt', 'round', 'projecting').""" - validate_capstyle(cs) - self._capstyle = cs + """ + Set how to draw endpoints of lines. + + Parameters + ---------- + cs : `.CapStyle` or %(CapStyle)s + """ + self._capstyle = CapStyle(cs) def set_clip_rectangle(self, rectangle): """Set the clip rectangle to a `.Bbox` or None.""" @@ -979,10 +983,16 @@ def set_foreground(self, fg, isRGBA=False): else: self._rgb = colors.to_rgba(fg) + @docstring.interpd def set_joinstyle(self, js): - """Set the join style to be one of ('miter', 'round', 'bevel').""" - validate_joinstyle(js) - self._joinstyle = js + """ + Set how to draw connections between line segments. + + Parameters + ---------- + js : `.JoinStyle` or %(JoinStyle)s + """ + self._joinstyle = JoinStyle(js) def set_linewidth(self, w): """Set the linewidth in points.""" diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 3b8a0a5e1fc1..148665f5a410 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -16,6 +16,7 @@ import matplotlib as mpl from . import (_api, _path, artist, cbook, cm, colors as mcolors, docstring, hatch as mhatch, lines as mlines, path as mpath, transforms) +from ._enums import JoinStyle, CapStyle import warnings @@ -71,6 +72,7 @@ class Collection(artist.Artist, cm.ScalarMappable): _edge_default = False @_api.delete_parameter("3.3", "offset_position") + @docstring.interpd def __init__(self, edgecolors=None, facecolors=None, @@ -110,14 +112,12 @@ def __init__(self, where *onoffseq* is an even length tuple of on and off ink lengths in points. For examples, see :doc:`/gallery/lines_bars_and_markers/linestyles`. - capstyle : str, default: :rc:`patch.capstyle` + capstyle : `.CapStyle`-like, default: :rc:`patch.capstyle` Style to use for capping lines for all paths in the collection. - See :doc:`/gallery/lines_bars_and_markers/joinstyle` for - a demonstration of each of the allowed values. - joinstyle : str, default: :rc:`patch.joinstyle` + Allowed values are %(CapStyle)s. + joinstyle : `.JoinStyle`-like, default: :rc:`patch.joinstyle` Style to use for joining lines for all paths in the collection. - See :doc:`/gallery/lines_bars_and_markers/joinstyle` for - a demonstration of each of the allowed values. + Allowed values are %(JoinStyle)s. antialiaseds : bool or list of bool, default: :rc:`patch.antialiased` Whether each patch in the collection should be drawn with antialiasing. @@ -128,7 +128,7 @@ def __init__(self, transOffset : `~.transforms.Transform`, default: `.IdentityTransform` A single transform which will be applied to each *offsets* vector before it is used. - offset_position : {'screen' (default), 'data' (deprecated)} + offset_position : {{'screen' (default), 'data' (deprecated)}} If set to 'data' (deprecated), *offsets* will be treated as if it is in data coordinates instead of in screen coordinates. norm : `~.colors.Normalize`, optional @@ -655,35 +655,33 @@ def set_linestyle(self, ls): self._linewidths, self._linestyles = self._bcast_lwls( self._us_lw, self._us_linestyles) + @docstring.interpd def set_capstyle(self, cs): """ - Set the capstyle for the collection (for all its elements). + Set the `.CapStyle` for the collection (for all its elements). Parameters ---------- - cs : {'butt', 'round', 'projecting'} - The capstyle. + cs : `.CapStyle` or %(CapStyle)s """ - mpl.rcsetup.validate_capstyle(cs) - self._capstyle = cs + self._capstyle = CapStyle(cs) def get_capstyle(self): - return self._capstyle + return self._capstyle.name + @docstring.interpd def set_joinstyle(self, js): """ - Set the joinstyle for the collection (for all its elements). + Set the `.JoinStyle` for the collection (for all its elements). Parameters ---------- - js : {'miter', 'round', 'bevel'} - The joinstyle. + js : `.JoinStyle` or %(JoinStyle)s """ - mpl.rcsetup.validate_joinstyle(js) - self._joinstyle = js + self._joinstyle = JoinStyle(js) def get_joinstyle(self): - return self._joinstyle + return self._joinstyle.name @staticmethod def _bcast_lwls(linewidths, dashes): diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 6d2d196a3e32..41b2f98c314e 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -17,6 +17,7 @@ from .markers import MarkerStyle from .path import Path from .transforms import Bbox, BboxTransformTo, TransformedPath +from ._enums import JoinStyle, CapStyle # Imported here for backward compatibility, even though they don't # really belong. @@ -253,12 +254,12 @@ class Line2D(Artist): @_api.deprecated("3.4") @_api.classproperty def validCap(cls): - return ('butt', 'round', 'projecting') + return tuple(cs.value for cs in CapStyle) @_api.deprecated("3.4") @_api.classproperty def validJoin(cls): - return ('miter', 'round', 'bevel') + return tuple(js.value for js in JoinStyle) def __str__(self): if self._label != "": @@ -1156,7 +1157,7 @@ def set_linestyle(self, ls): self._dashOffset, self._dashSeq = _scale_dashes( self._us_dashOffset, self._us_dashSeq, self._linewidth) - @docstring.dedent_interpd + @docstring.interpd def set_marker(self, marker): """ Set the line marker. @@ -1311,98 +1312,101 @@ def update_from(self, other): self._marker = MarkerStyle(marker=other._marker) self._drawstyle = other._drawstyle + @docstring.interpd def set_dash_joinstyle(self, s): """ - Set the join style for dashed lines. + How to join segments of the line if it `~Line2D.is_dashed`. Parameters ---------- - s : {'miter', 'round', 'bevel'} - For examples see :doc:`/gallery/lines_bars_and_markers/joinstyle`. + s : `.JoinStyle` or %(JoinStyle)s """ - mpl.rcsetup.validate_joinstyle(s) - if self._dashjoinstyle != s: + js = JoinStyle(s) + if self._dashjoinstyle != js: self.stale = True - self._dashjoinstyle = s + self._dashjoinstyle = js + @docstring.interpd def set_solid_joinstyle(self, s): """ - Set the join style for solid lines. + How to join segments if the line is solid (not `~Line2D.is_dashed`). Parameters ---------- - s : {'miter', 'round', 'bevel'} - For examples see :doc:`/gallery/lines_bars_and_markers/joinstyle`. + s : `.JoinStyle` or %(JoinStyle)s """ - mpl.rcsetup.validate_joinstyle(s) - if self._solidjoinstyle != s: + js = JoinStyle(s) + if self._solidjoinstyle != js: self.stale = True - self._solidjoinstyle = s + self._solidjoinstyle = js def get_dash_joinstyle(self): """ - Return the join style for dashed lines. + Return the `.JoinStyle` for dashed lines. See also `~.Line2D.set_dash_joinstyle`. """ - return self._dashjoinstyle + return self._dashjoinstyle.name def get_solid_joinstyle(self): """ - Return the join style for solid lines. + Return the `.JoinStyle` for solid lines. See also `~.Line2D.set_solid_joinstyle`. """ - return self._solidjoinstyle + return self._solidjoinstyle.name + @docstring.interpd def set_dash_capstyle(self, s): """ - Set the cap style for dashed lines. + How to draw the end caps if the line is `~Line2D.is_dashed`. Parameters ---------- - s : {'butt', 'round', 'projecting'} - For examples see :doc:`/gallery/lines_bars_and_markers/joinstyle`. + s : `.CapStyle` or %(CapStyle)s """ - mpl.rcsetup.validate_capstyle(s) - if self._dashcapstyle != s: + cs = CapStyle(s) + if self._dashcapstyle != cs: self.stale = True - self._dashcapstyle = s + self._dashcapstyle = cs + @docstring.interpd def set_solid_capstyle(self, s): """ - Set the cap style for solid lines. + How to draw the end caps if the line is solid (not `~Line2D.is_dashed`) Parameters ---------- - s : {'butt', 'round', 'projecting'} - For examples see :doc:`/gallery/lines_bars_and_markers/joinstyle`. + s : `.CapStyle` or %(CapStyle)s """ - mpl.rcsetup.validate_capstyle(s) - if self._solidcapstyle != s: + cs = CapStyle(s) + if self._solidcapstyle != cs: self.stale = True - self._solidcapstyle = s + self._solidcapstyle = cs def get_dash_capstyle(self): """ - Return the cap style for dashed lines. + Return the `.CapStyle` for dashed lines. See also `~.Line2D.set_dash_capstyle`. """ - return self._dashcapstyle + return self._dashcapstyle.name def get_solid_capstyle(self): """ - Return the cap style for solid lines. + Return the `.CapStyle` for solid lines. See also `~.Line2D.set_solid_capstyle`. """ - return self._solidcapstyle + return self._solidcapstyle.name def is_dashed(self): """ Return whether line has a dashed linestyle. + A custom linestyle is assumed to be dashed, we do not inspect the + ``onoffseq`` directly. + See also `~.Line2D.set_linestyle`. """ return self._linestyle in ('--', '-.', ':') @@ -1556,4 +1560,4 @@ def onpick(self, event): # You can not set the docstring of an instancemethod, # but you can on the underlying function. Go figure. -docstring.dedent_interpd(Line2D.__init__) +docstring.interpd(Line2D.__init__) diff --git a/lib/matplotlib/markers.py b/lib/matplotlib/markers.py index 7118051acdd3..ae2bb3da83ac 100644 --- a/lib/matplotlib/markers.py +++ b/lib/matplotlib/markers.py @@ -134,6 +134,7 @@ from . import _api, cbook, rcParams from .path import Path from .transforms import IdentityTransform, Affine2D +from ._enums import JoinStyle, CapStyle # special-purpose marker identifiers: (TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN, @@ -242,8 +243,8 @@ def _recache(self): self._alt_path = None self._alt_transform = None self._snap_threshold = None - self._joinstyle = 'round' - self._capstyle = 'butt' + self._joinstyle = JoinStyle.round + self._capstyle = CapStyle.butt # Initial guess: Assume the marker is filled unless the fillstyle is # set to 'none'. The marker function will override this for unfilled # markers. @@ -391,14 +392,14 @@ def _set_tuple_marker(self): symstyle = marker[1] if symstyle == 0: self._path = Path.unit_regular_polygon(numsides) - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter elif symstyle == 1: self._path = Path.unit_regular_star(numsides) - self._joinstyle = 'bevel' + self._joinstyle = JoinStyle.bevel elif symstyle == 2: self._path = Path.unit_regular_asterisk(numsides) self._filled = False - self._joinstyle = 'bevel' + self._joinstyle = JoinStyle.bevel else: raise ValueError(f"Unexpected tuple marker: {marker}") self._transform = Affine2D().scale(0.5).rotate_deg(rotation) @@ -499,7 +500,7 @@ def _set_triangle(self, rot, skip): self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_triangle_up(self): return self._set_triangle(0.0, 0) @@ -529,7 +530,7 @@ def _set_square(self): self._transform.rotate_deg(rotate) self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_diamond(self): self._transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45) @@ -543,7 +544,7 @@ def _set_diamond(self): rotate = {'right': 0, 'top': 90, 'left': 180, 'bottom': 270}[fs] self._transform.rotate_deg(rotate) self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_thin_diamond(self): self._set_diamond() @@ -570,7 +571,7 @@ def _set_pentagon(self): }[self.get_fillstyle()] self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_star(self): self._transform = Affine2D().scale(0.5) @@ -592,7 +593,7 @@ def _set_star(self): }[self.get_fillstyle()] self._alt_transform = self._transform - self._joinstyle = 'bevel' + self._joinstyle = JoinStyle.bevel def _set_hexagon1(self): self._transform = Affine2D().scale(0.5) @@ -616,7 +617,7 @@ def _set_hexagon1(self): }[self.get_fillstyle()] self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_hexagon2(self): self._transform = Affine2D().scale(0.5).rotate_deg(30) @@ -642,7 +643,7 @@ def _set_hexagon2(self): }[self.get_fillstyle()] self._alt_transform = self._transform - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_octagon(self): self._transform = Affine2D().scale(0.5) @@ -663,7 +664,7 @@ def _set_octagon(self): {'left': 0, 'bottom': 90, 'right': 180, 'top': 270}[fs]) self._alt_transform = self._transform.frozen().rotate_deg(180.0) - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]]) @@ -737,7 +738,7 @@ def _set_caretdown(self): self._snap_threshold = 3.0 self._filled = False self._path = self._caret_path - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter def _set_caretup(self): self._set_caretdown() @@ -803,7 +804,8 @@ def _set_x(self): def _set_plus_filled(self): self._transform = Affine2D() self._snap_threshold = 5.0 - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter + fs = self.get_fillstyle() if not self._half_fill(): self._path = self._plus_filled_path else: @@ -827,7 +829,7 @@ def _set_plus_filled(self): def _set_x_filled(self): self._transform = Affine2D() self._snap_threshold = 5.0 - self._joinstyle = 'miter' + self._joinstyle = JoinStyle.miter if not self._half_fill(): self._path = self._x_filled_path else: diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 98eada4c7d5e..81df8568f243 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -16,6 +16,7 @@ get_parallels, inside_circle, make_wedged_bezier2, split_bezier_intersecting_with_closedpath, split_path_inout) from .path import Path +from ._enums import JoinStyle, CapStyle @cbook._define_aliases({ @@ -74,9 +75,9 @@ def __init__(self, if linestyle is None: linestyle = "solid" if capstyle is None: - capstyle = 'butt' + capstyle = CapStyle.butt if joinstyle is None: - joinstyle = 'miter' + joinstyle = JoinStyle.miter if antialiased is None: antialiased = mpl.rcParams['patch.antialiased'] @@ -471,32 +472,34 @@ def get_fill(self): # attribute. fill = property(get_fill, set_fill) + @docstring.interpd def set_capstyle(self, s): """ - Set the capstyle. + Set the `.CapStyle`. Parameters ---------- - s : {'butt', 'round', 'projecting'} + s : `.CapStyle` or %(CapStyle)s """ - mpl.rcsetup.validate_capstyle(s) - self._capstyle = s + cs = CapStyle(s) + self._capstyle = cs self.stale = True def get_capstyle(self): """Return the capstyle.""" return self._capstyle + @docstring.interpd def set_joinstyle(self, s): """ - Set the joinstyle. + Set the `.JoinStyle`. Parameters ---------- - s : {'miter', 'round', 'bevel'} + s : `.JoinStyle` or %(JoinStyle)s """ - mpl.rcsetup.validate_joinstyle(s) - self._joinstyle = s + js = JoinStyle(s) + self._joinstyle = js self.stale = True def get_joinstyle(self): @@ -3973,8 +3976,8 @@ def __init__(self, posA=None, posB=None, path=None, ``joinstyle`` for `FancyArrowPatch` are set to ``"round"``. """ # Traditionally, the cap- and joinstyle for FancyArrowPatch are round - kwargs.setdefault("joinstyle", "round") - kwargs.setdefault("capstyle", "round") + kwargs.setdefault("joinstyle", JoinStyle.round) + kwargs.setdefault("capstyle", CapStyle.round) super().__init__(**kwargs) diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index f9b68e0924af..535649b03f9f 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -24,8 +24,9 @@ from matplotlib import _api, animation, cbook from matplotlib.cbook import ls_mapper -from matplotlib.fontconfig_pattern import parse_fontconfig_pattern from matplotlib.colors import Colormap, is_color_like +from matplotlib.fontconfig_pattern import parse_fontconfig_pattern +from matplotlib._enums import JoinStyle, CapStyle # Don't let the original cycler collide with our validating cycler from cycler import Cycler, cycler as ccycler @@ -577,41 +578,10 @@ def _is_iterable_not_string_like(x): raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.") -def _deprecate_case_insensitive_join_cap(s): - s_low = s.lower() - if s != s_low: - if s_low in ['miter', 'round', 'bevel']: - _api.warn_deprecated( - "3.3", message="Case-insensitive capstyles are deprecated " - "since %(since)s and support for them will be removed " - "%(removal)s; please pass them in lowercase.") - elif s_low in ['butt', 'round', 'projecting']: - _api.warn_deprecated( - "3.3", message="Case-insensitive joinstyles are deprecated " - "since %(since)s and support for them will be removed " - "%(removal)s; please pass them in lowercase.") - # Else, error out at the check_in_list stage. - return s_low - - -def validate_joinstyle(s): - s = _deprecate_case_insensitive_join_cap(s) - _api.check_in_list(['miter', 'round', 'bevel'], joinstyle=s) - return s - - -def validate_capstyle(s): - s = _deprecate_case_insensitive_join_cap(s) - _api.check_in_list(['butt', 'round', 'projecting'], capstyle=s) - return s - - validate_fillstyle = ValidateInStrings( 'markers.fillstyle', ['full', 'left', 'right', 'bottom', 'top', 'none']) -validate_joinstylelist = _listify_validator(validate_joinstyle) -validate_capstylelist = _listify_validator(validate_capstyle) validate_fillstylelist = _listify_validator(validate_fillstyle) @@ -788,8 +758,8 @@ def validate_hatch(s): 'linestyle': _listify_validator(_validate_linestyle), 'facecolor': validate_colorlist, 'edgecolor': validate_colorlist, - 'joinstyle': validate_joinstylelist, - 'capstyle': validate_capstylelist, + 'joinstyle': _listify_validator(JoinStyle), + 'capstyle': _listify_validator(CapStyle), 'fillstyle': validate_fillstylelist, 'markerfacecolor': validate_colorlist, 'markersize': validate_floatlist, @@ -1033,10 +1003,10 @@ def _convert_validator_spec(key, conv): "lines.markeredgewidth": validate_float, "lines.markersize": validate_float, # markersize, in points "lines.antialiased": validate_bool, # antialiased (no jaggies) - "lines.dash_joinstyle": validate_joinstyle, - "lines.solid_joinstyle": validate_joinstyle, - "lines.dash_capstyle": validate_capstyle, - "lines.solid_capstyle": validate_capstyle, + "lines.dash_joinstyle": JoinStyle, + "lines.solid_joinstyle": JoinStyle, + "lines.dash_capstyle": CapStyle, + "lines.solid_capstyle": CapStyle, "lines.dashed_pattern": validate_floatlist, "lines.dashdot_pattern": validate_floatlist, "lines.dotted_pattern": validate_floatlist, diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index 1f5deddda1ff..9fe73e3b892a 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -8,9 +8,9 @@ import matplotlib as mpl import matplotlib.pyplot as plt import matplotlib.collections as mcollections -import matplotlib.transforms as mtransforms from matplotlib.collections import (Collection, LineCollection, EventCollection, PolyCollection) +import matplotlib.transforms as mtransforms from matplotlib.testing.decorators import check_figures_equal, image_comparison pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy