From d2de8a2dcaaa6539224e906baeeca554f7183d76 Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Tue, 22 Sep 2020 13:57:56 -0500 Subject: [PATCH 1/4] REORG: JoinStyle and CapStyle classes Breaking API, use these class instances everywhere that we used the strings internally before. --- doc/api/_types.rst | 9 + doc/api/index.rst | 1 + examples/lines_bars_and_markers/joinstyle.py | 90 ---------- lib/matplotlib/_types.py | 170 +++++++++++++++++++ lib/matplotlib/backend_bases.py | 34 ++-- lib/matplotlib/backends/backend_cairo.py | 21 +-- lib/matplotlib/backends/backend_pdf.py | 20 ++- lib/matplotlib/backends/backend_pgf.py | 13 +- lib/matplotlib/backends/backend_ps.py | 12 +- lib/matplotlib/backends/backend_svg.py | 9 +- lib/matplotlib/backends/backend_wx.py | 13 +- lib/matplotlib/collections.py | 23 +-- lib/matplotlib/lines.py | 56 +++--- lib/matplotlib/markers.py | 34 ++-- lib/matplotlib/patches.py | 25 +-- lib/matplotlib/rcsetup.py | 48 +----- lib/matplotlib/tests/test_collections.py | 13 +- lib/matplotlib/tests/test_patches.py | 7 +- src/py_converters.cpp | 23 ++- 19 files changed, 359 insertions(+), 262 deletions(-) create mode 100644 doc/api/_types.rst delete mode 100644 examples/lines_bars_and_markers/joinstyle.py create mode 100644 lib/matplotlib/_types.py diff --git a/doc/api/_types.rst b/doc/api/_types.rst new file mode 100644 index 000000000000..de1be5cb95b4 --- /dev/null +++ b/doc/api/_types.rst @@ -0,0 +1,9 @@ +********************** +``matplotlib._types`` +********************** + +.. automodule:: matplotlib._types + :members: + :undoc-members: + :show-inheritance: + diff --git a/doc/api/index.rst b/doc/api/index.rst index dba86c35ad0a..10d6d600137d 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -124,6 +124,7 @@ Matplotlib consists of the following submodules: transformations.rst tri_api.rst type1font.rst + _types.rst units_api.rst widgets_api.rst _api_api.rst diff --git a/examples/lines_bars_and_markers/joinstyle.py b/examples/lines_bars_and_markers/joinstyle.py deleted file mode 100644 index dcc47105d12f..000000000000 --- a/examples/lines_bars_and_markers/joinstyle.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -========================== -Join styles and cap styles -========================== - -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. -""" - -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=(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() -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/_types.py b/lib/matplotlib/_types.py new file mode 100644 index 000000000000..f32cde398d50 --- /dev/null +++ b/lib/matplotlib/_types.py @@ -0,0 +1,170 @@ +""" +Style description information that is shared across unrelated classses. +""" + +from enum import Enum, auto +from matplotlib import cbook + + +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 _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`: + + .. plot:: + :alt: Demo of possible JoinStyle's + + from matplotlib._types import JoinStyle + 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. + + Matplotlib provides three options for drawing the corners between adjacent + segments. In short: + + - *miter* is 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* is 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:: + + The *miter* option can be controlled further by specifying a "miter + limit", which specifies how long a miter tip can get before it is + automatically "bevel"ed off. Matplotlib does not currently expose a + ``miterlimit`` parameter to the user, and most backends simply use the + upstream default value. For example, the PDF backend assumes 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. + + A more detailed description of the effect of a miter limit can be found + in the `Mozilla Developer Docs + `_ + """ + + miter = auto() + round = auto() + bevel = auto() + + def __init__(self, s): + s = _deprecate_case_insensitive_join_cap(s) + Enum.__init__(self) + + @staticmethod + def demo(): + 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=(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.set_xlim(-1.5, 2.75) + ax.set_ylim(-.5, 5.5) + ax.set_axis_off() + fig.show() + + +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`: + + .. plot:: + :alt: Demo of possible CapStyle's + + from matplotlib._types import CapStyle + CapStyle.demo() + + Available options: + + - *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``. + """ + butt = 'butt' + projecting = 'projecting' + round = 'round' + + def __init__(self, s): + s = _deprecate_case_insensitive_join_cap(s) + Enum.__init__(self) + + @staticmethod + def demo(): + import matplotlib.pyplot as plt + + 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() + fig.show() diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 8c11f841cb42..ec42aa991069 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -50,8 +50,8 @@ 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._types 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,9 +820,7 @@ def get_antialiased(self): return self._antialiased def get_capstyle(self): - """ - Return the capstyle as a string in ('butt', 'round', 'projecting'). - """ + """Return the `.CapStyle`.""" return self._capstyle def get_clip_rectangle(self): @@ -867,7 +865,7 @@ 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 the `.JoinStyle`.""" return self._joinstyle def get_linewidth(self): @@ -920,9 +918,14 @@ def set_antialiased(self, b): self._antialiased = int(bool(b)) 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 {'butt', 'round', 'projecting'} + """ + self._capstyle = CapStyle(cs) def set_clip_rectangle(self, rectangle): """Set the clip rectangle to a `.Bbox` or None.""" @@ -980,9 +983,14 @@ def set_foreground(self, fg, isRGBA=False): self._rgb = colors.to_rgba(fg) 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 {'miter', 'round', 'bevel'}. + """ + self._joinstyle = JoinStyle(js) def set_linewidth(self, w): """Set the linewidth in points.""" diff --git a/lib/matplotlib/backends/backend_cairo.py b/lib/matplotlib/backends/backend_cairo.py index b05a5fc0967a..abefdebd4944 100644 --- a/lib/matplotlib/backends/backend_cairo.py +++ b/lib/matplotlib/backends/backend_cairo.py @@ -32,6 +32,7 @@ from matplotlib.mathtext import MathTextParser from matplotlib.path import Path from matplotlib.transforms import Affine2D +from matplotlib._types import JoinStyle, CapStyle backend_version = cairo.version @@ -321,15 +322,15 @@ def points_to_pixels(self, points): class GraphicsContextCairo(GraphicsContextBase): _joind = { - 'bevel': cairo.LINE_JOIN_BEVEL, - 'miter': cairo.LINE_JOIN_MITER, - 'round': cairo.LINE_JOIN_ROUND, + JoinStyle.bevel: cairo.LINE_JOIN_BEVEL, + JoinStyle.miter: cairo.LINE_JOIN_MITER, + JoinStyle.round: cairo.LINE_JOIN_ROUND, } _capd = { - 'butt': cairo.LINE_CAP_BUTT, - 'projecting': cairo.LINE_CAP_SQUARE, - 'round': cairo.LINE_CAP_ROUND, + CapStyle.butt: cairo.LINE_CAP_BUTT, + CapStyle.projecting: cairo.LINE_CAP_SQUARE, + CapStyle.round: cairo.LINE_CAP_ROUND, } def __init__(self, renderer): @@ -353,8 +354,8 @@ def set_alpha(self, alpha): # one for False. def set_capstyle(self, cs): - self.ctx.set_line_cap(_api.check_getitem(self._capd, capstyle=cs)) - self._capstyle = cs + super().set_capstyle(cs) + self.ctx.set_line_cap(self._capd[self.get_capstyle()]) def set_clip_rectangle(self, rectangle): if not rectangle: @@ -396,8 +397,8 @@ def get_rgb(self): return self.ctx.get_source().get_rgba()[:3] def set_joinstyle(self, js): - self.ctx.set_line_join(_api.check_getitem(self._joind, joinstyle=js)) - self._joinstyle = js + super().set_joinstyle(js) + self.ctx.set_line_join(self._joind[self.get_joinstyle()]) def set_linewidth(self, w): self._linewidth = float(w) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 636f87c3d97b..d0a390cff86b 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -26,22 +26,23 @@ import matplotlib as mpl from matplotlib import _api, _text_layout, cbook from matplotlib._pylab_helpers import Gcf +from matplotlib.afm import AFM from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer +from matplotlib.dates import UTC +import matplotlib.dviread as dviread from matplotlib.figure import Figure from matplotlib.font_manager import findfont, get_font -from matplotlib.afm import AFM -import matplotlib.type1font as type1font -import matplotlib.dviread as dviread from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, LOAD_NO_HINTING, KERNING_UNFITTED) from matplotlib.mathtext import MathTextParser -from matplotlib.transforms import Affine2D, BboxBase -from matplotlib.path import Path -from matplotlib.dates import UTC from matplotlib import _path +from matplotlib.path import Path +from matplotlib._types import JoinStyle, CapStyle +import matplotlib.type1font as type1font +from matplotlib.transforms import Affine2D, BboxBase from . import _backend_pdf_ps _log = logging.getLogger(__name__) @@ -746,7 +747,8 @@ def newPage(self, width, height): self.reserveObject('length of content stream')) # Initialize the pdf graphics state to match the default mpl # graphics context: currently only the join style needs to be set - self.output(GraphicsContextPdf.joinstyles['round'], Op.setlinejoin) + self.output(GraphicsContextPdf.joinstyles[JoinStyle.round], + Op.setlinejoin) # Clear the list of annotations for the next page self.pageAnnotations = [] @@ -2414,8 +2416,8 @@ def paint(self): """ return Op.paint_path(self.fill(), self.stroke()) - capstyles = {'butt': 0, 'round': 1, 'projecting': 2} - joinstyles = {'miter': 0, 'round': 1, 'bevel': 2} + capstyles = {CapStyle.butt: 0, CapStyle.round: 1, CapStyle.projecting: 2} + joinstyles = {JoinStyle.miter: 0, JoinStyle.round: 1, JoinStyle.bevel: 2} def capstyle_cmd(self, style): return [self.capstyles[style], Op.setlinecap] diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 6cc27928733a..57d22dfe5de1 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -26,6 +26,7 @@ from matplotlib.path import Path from matplotlib.figure import Figure from matplotlib._pylab_helpers import Gcf +from matplotlib._types import JoinStyle, CapStyle _log = logging.getLogger(__name__) @@ -515,15 +516,15 @@ def _print_pgf_clip(self, gc): def _print_pgf_path_styles(self, gc, rgbFace): # cap style - capstyles = {"butt": r"\pgfsetbuttcap", - "round": r"\pgfsetroundcap", - "projecting": r"\pgfsetrectcap"} + capstyles = {CapStyle.butt: r"\pgfsetbuttcap", + CapStyle.round: r"\pgfsetroundcap", + CapStyle.projecting: r"\pgfsetrectcap"} writeln(self.fh, capstyles[gc.get_capstyle()]) # join style - joinstyles = {"miter": r"\pgfsetmiterjoin", - "round": r"\pgfsetroundjoin", - "bevel": r"\pgfsetbeveljoin"} + joinstyles = {JoinStyle.miter: r"\pgfsetmiterjoin", + JoinStyle.round: r"\pgfsetroundjoin", + JoinStyle.bevel: r"\pgfsetbeveljoin"} writeln(self.fh, joinstyles[gc.get_joinstyle()]) # filling diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 7b4acb3ad29a..3e8467adf5c6 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -24,6 +24,7 @@ from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) +from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.cbook import is_writable_file_like, file_requires_unicode from matplotlib.font_manager import get_font from matplotlib.ft2font import LOAD_NO_HINTING, LOAD_NO_SCALE @@ -31,9 +32,9 @@ from matplotlib.mathtext import MathTextParser from matplotlib._mathtext_data import uni2type1 from matplotlib.path import Path +from matplotlib._types import JoinStyle, CapStyle from matplotlib.texmanager import TexManager from matplotlib.transforms import Affine2D -from matplotlib.backends.backend_mixed import MixedModeRenderer from . import _backend_pdf_ps _log = logging.getLogger(__name__) @@ -801,11 +802,16 @@ def _is_transparent(rgb_or_rgba): @_api.deprecated("3.4", alternative="GraphicsContextBase") class GraphicsContextPS(GraphicsContextBase): + + _capstyles = {CapStyle.butt: 0, CapStyle.round: 1, CapStyle.projecting: 2} + def get_capstyle(self): - return {'butt': 0, 'round': 1, 'projecting': 2}[super().get_capstyle()] + return self._capstyles[super().get_capstyle()] + + _joinstyles = {JoinStyle.miter: 0, JoinStyle.round: 1, JoinStyle.bevel: 2} def get_joinstyle(self): - return {'miter': 0, 'round': 1, 'bevel': 2}[super().get_joinstyle()] + return self._joinstyles[super().get_joinstyle()] class _Orientation(Enum): diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index ce6ad0c115f8..7b31170729c4 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -27,6 +27,7 @@ from matplotlib.path import Path from matplotlib import _path from matplotlib.transforms import Affine2D, Affine2DBase +from matplotlib._types import JoinStyle, CapStyle _log = logging.getLogger(__name__) @@ -571,10 +572,10 @@ def _get_style_dict(self, gc, rgbFace): attrib['stroke-opacity'] = short_float_fmt(rgb[3]) if linewidth != 1.0: attrib['stroke-width'] = short_float_fmt(linewidth) - if gc.get_joinstyle() != 'round': - attrib['stroke-linejoin'] = gc.get_joinstyle() - if gc.get_capstyle() != 'butt': - attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle()] + if gc.get_joinstyle() != JoinStyle.round: + attrib['stroke-linejoin'] = gc.get_joinstyle().name + if gc.get_capstyle() != CapStyle.butt: + attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle().name] return attrib diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index a5f335cbbc32..b431234bb212 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -28,6 +28,7 @@ from matplotlib.figure import Figure from matplotlib.path import Path from matplotlib.transforms import Affine2D +from matplotlib._types import JoinStyle, CapStyle from matplotlib.widgets import SubplotTool import wx @@ -326,13 +327,13 @@ class GraphicsContextWx(GraphicsContextBase): since wxPython colour management is rather simple, I have not chosen to implement a separate colour manager class. """ - _capd = {'butt': wx.CAP_BUTT, - 'projecting': wx.CAP_PROJECTING, - 'round': wx.CAP_ROUND} + _capd = {CapStyle.butt: wx.CAP_BUTT, + CapStyle.projecting: wx.CAP_PROJECTING, + CapStyle.round: wx.CAP_ROUND} - _joind = {'bevel': wx.JOIN_BEVEL, - 'miter': wx.JOIN_MITER, - 'round': wx.JOIN_ROUND} + _joind = {JoinStyle.bevel: wx.JOIN_BEVEL, + JoinStyle.miter: wx.JOIN_MITER, + JoinStyle.round: wx.JOIN_ROUND} _cache = weakref.WeakKeyDictionary() diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 3b8a0a5e1fc1..4db9615d1328 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 ._types import JoinStyle, CapStyle import warnings @@ -110,14 +111,10 @@ 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` + 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. antialiaseds : bool or list of bool, default: :rc:`patch.antialiased` Whether each patch in the collection should be drawn with antialiasing. @@ -657,30 +654,28 @@ def set_linestyle(self, ls): 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'} + cs : `.CapStyle` or {'butt', 'round', 'projecting'} The capstyle. """ - mpl.rcsetup.validate_capstyle(cs) - self._capstyle = cs + self._capstyle = CapStyle(cs) def get_capstyle(self): return self._capstyle 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'} + js : `.JoinStyle` or {'miter', 'round', 'bevel'} The joinstyle. """ - mpl.rcsetup.validate_joinstyle(js) - self._joinstyle = js + self._joinstyle = JoinStyle(js) def get_joinstyle(self): return self._joinstyle diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 6d2d196a3e32..d846e2e20eac 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 ._types import JoinStyle, CapStyle # Imported here for backward compatibility, even though they don't # really belong. @@ -1313,35 +1314,33 @@ def update_from(self, other): 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 {'miter', 'round', 'bevel'} """ - 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 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 {'miter', 'round', 'bevel'} """ - 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`. """ @@ -1349,7 +1348,7 @@ def get_dash_joinstyle(self): def get_solid_joinstyle(self): """ - Return the join style for solid lines. + Return the `.JoinStyle` for solid lines. See also `~.Line2D.set_solid_joinstyle`. """ @@ -1357,35 +1356,33 @@ def get_solid_joinstyle(self): 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 {'butt', 'round', 'projecting'} """ - 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 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 {'butt', 'round', 'projecting'} """ - 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`. """ @@ -1393,7 +1390,7 @@ def get_dash_capstyle(self): def get_solid_capstyle(self): """ - Return the cap style for solid lines. + Return the `.CapStyle` for solid lines. See also `~.Line2D.set_solid_capstyle`. """ @@ -1401,7 +1398,8 @@ def get_solid_capstyle(self): def is_dashed(self): """ - Return whether line has a dashed linestyle. + 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`. """ diff --git a/lib/matplotlib/markers.py b/lib/matplotlib/markers.py index 7118051acdd3..30733e8faf0b 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 ._types 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..86edaf667b72 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 ._types 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'] @@ -473,14 +474,14 @@ def get_fill(self): def set_capstyle(self, s): """ - Set the capstyle. + Set the `.CapStyle`. Parameters ---------- - s : {'butt', 'round', 'projecting'} + s : `.CapStyle` or {'butt', 'round', 'projecting'} """ - mpl.rcsetup.validate_capstyle(s) - self._capstyle = s + cs = CapStyle(s) + self._capstyle = cs self.stale = True def get_capstyle(self): @@ -489,14 +490,14 @@ def get_capstyle(self): def set_joinstyle(self, s): """ - Set the joinstyle. + Set the `.JoinStyle`. Parameters ---------- - s : {'miter', 'round', 'bevel'} + s : `.JoinStyle` or {'miter', 'round', 'bevel'} """ - mpl.rcsetup.validate_joinstyle(s) - self._joinstyle = s + js = JoinStyle(s) + self._joinstyle = js self.stale = True def get_joinstyle(self): @@ -3973,8 +3974,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..d31f4ba50106 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -22,10 +22,11 @@ import numpy as np -from matplotlib import _api, animation, cbook +from matplotlib import 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._types 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..db75a094ac62 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -6,12 +6,13 @@ import pytest 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.pyplot as plt +from matplotlib._types import JoinStyle, CapStyle from matplotlib.testing.decorators import check_figures_equal, image_comparison +import matplotlib.transforms as mtransforms def generate_EventCollection_plot(): @@ -529,17 +530,17 @@ def test_lslw_bcast(): @pytest.mark.style('default') def test_capstyle(): col = mcollections.PathCollection([], capstyle='round') - assert col.get_capstyle() == 'round' + assert col.get_capstyle() == CapStyle.round col.set_capstyle('butt') - assert col.get_capstyle() == 'butt' + assert col.get_capstyle() == CapStyle.butt @pytest.mark.style('default') def test_joinstyle(): col = mcollections.PathCollection([], joinstyle='round') - assert col.get_joinstyle() == 'round' + assert col.get_joinstyle() == JoinStyle.round col.set_joinstyle('miter') - assert col.get_joinstyle() == 'miter' + assert col.get_joinstyle() == JoinStyle.miter @image_comparison(['cap_and_joinstyle.png']) diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index 2a908098364e..a6001752bfe5 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -6,9 +6,10 @@ import pytest from matplotlib.patches import Patch, Polygon, Rectangle, FancyArrowPatch +import matplotlib.pyplot as plt from matplotlib.testing.decorators import image_comparison, check_figures_equal from matplotlib.transforms import Bbox -import matplotlib.pyplot as plt +from matplotlib._types import JoinStyle, CapStyle from matplotlib import ( collections as mcollections, colors as mcolors, patches as mpatches, path as mpath, style as mstyle, transforms as mtransforms, rcParams) @@ -620,9 +621,9 @@ def test_default_linestyle(): def test_default_capstyle(): patch = Patch() - assert patch.get_capstyle() == 'butt' + assert patch.get_capstyle() == CapStyle.butt def test_default_joinstyle(): patch = Patch() - assert patch.get_joinstyle() == 'miter' + assert patch.get_joinstyle() == JoinStyle.miter diff --git a/src/py_converters.cpp b/src/py_converters.cpp index a4c0b1909940..208809328809 100644 --- a/src/py_converters.cpp +++ b/src/py_converters.cpp @@ -123,7 +123,17 @@ int convert_cap(PyObject *capobj, void *capp) int values[] = {agg::butt_cap, agg::round_cap, agg::square_cap}; int result = agg::butt_cap; - if (!convert_string_enum(capobj, "capstyle", names, values, &result)) { + PyObject *name = PyUnicode_FromString("name"); + PyObject *cap_string = PyObject_GetAttr(capobj, name); + if (cap_string == NULL) { + Py_DECREF(name); + PyErr_SetString(PyExc_TypeError, "capstyle must be a CapStyle (Enum) object."); + } + + int success = convert_string_enum(cap_string, "capstyle", names, values, &result); + Py_DECREF(name); + Py_DECREF(cap_string); + if (!success) { return 0; } @@ -136,8 +146,17 @@ int convert_join(PyObject *joinobj, void *joinp) const char *names[] = {"miter", "round", "bevel", NULL}; int values[] = {agg::miter_join_revert, agg::round_join, agg::bevel_join}; int result = agg::miter_join_revert; + PyObject *name = PyUnicode_FromString("name"); + PyObject *join_string = PyObject_GetAttr(joinobj, name); + if (join_string == NULL) { + Py_DECREF(name); + PyErr_SetString(PyExc_TypeError, "joinstyle must be a JoinStyle (Enum) object."); + } - if (!convert_string_enum(joinobj, "joinstyle", names, values, &result)) { + int success = convert_string_enum(join_string, "joinstyle", names, values, &result); + Py_DECREF(name); + Py_DECREF(join_string); + if (!success) { return 0; } From 56f37c3f0f9246ef8108799ac117c43eb42a274b Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Tue, 20 Oct 2020 10:32:34 -0700 Subject: [PATCH 2/4] REVERT: Do not expose JoinStyle/CapStyle at all --- lib/matplotlib/backend_bases.py | 4 ++-- lib/matplotlib/backends/backend_cairo.py | 21 ++++++++++----------- lib/matplotlib/backends/backend_pdf.py | 20 +++++++++----------- lib/matplotlib/backends/backend_pgf.py | 13 ++++++------- lib/matplotlib/backends/backend_ps.py | 12 +++--------- lib/matplotlib/backends/backend_svg.py | 9 ++++----- lib/matplotlib/backends/backend_wx.py | 13 ++++++------- lib/matplotlib/collections.py | 6 ++---- lib/matplotlib/lines.py | 12 ++++++------ lib/matplotlib/tests/test_collections.py | 11 ++++++----- lib/matplotlib/tests/test_patches.py | 7 +++---- src/py_converters.cpp | 23 ++--------------------- 12 files changed, 59 insertions(+), 92 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index ec42aa991069..0f5a72bd4015 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -821,7 +821,7 @@ def get_antialiased(self): def get_capstyle(self): """Return the `.CapStyle`.""" - return self._capstyle + return self._capstyle.name def get_clip_rectangle(self): """ @@ -866,7 +866,7 @@ def get_forced_alpha(self): def get_joinstyle(self): """Return the `.JoinStyle`.""" - return self._joinstyle + return self._joinstyle.name def get_linewidth(self): """Return the line width in points.""" diff --git a/lib/matplotlib/backends/backend_cairo.py b/lib/matplotlib/backends/backend_cairo.py index abefdebd4944..b05a5fc0967a 100644 --- a/lib/matplotlib/backends/backend_cairo.py +++ b/lib/matplotlib/backends/backend_cairo.py @@ -32,7 +32,6 @@ from matplotlib.mathtext import MathTextParser from matplotlib.path import Path from matplotlib.transforms import Affine2D -from matplotlib._types import JoinStyle, CapStyle backend_version = cairo.version @@ -322,15 +321,15 @@ def points_to_pixels(self, points): class GraphicsContextCairo(GraphicsContextBase): _joind = { - JoinStyle.bevel: cairo.LINE_JOIN_BEVEL, - JoinStyle.miter: cairo.LINE_JOIN_MITER, - JoinStyle.round: cairo.LINE_JOIN_ROUND, + 'bevel': cairo.LINE_JOIN_BEVEL, + 'miter': cairo.LINE_JOIN_MITER, + 'round': cairo.LINE_JOIN_ROUND, } _capd = { - CapStyle.butt: cairo.LINE_CAP_BUTT, - CapStyle.projecting: cairo.LINE_CAP_SQUARE, - CapStyle.round: cairo.LINE_CAP_ROUND, + 'butt': cairo.LINE_CAP_BUTT, + 'projecting': cairo.LINE_CAP_SQUARE, + 'round': cairo.LINE_CAP_ROUND, } def __init__(self, renderer): @@ -354,8 +353,8 @@ def set_alpha(self, alpha): # one for False. def set_capstyle(self, cs): - super().set_capstyle(cs) - self.ctx.set_line_cap(self._capd[self.get_capstyle()]) + self.ctx.set_line_cap(_api.check_getitem(self._capd, capstyle=cs)) + self._capstyle = cs def set_clip_rectangle(self, rectangle): if not rectangle: @@ -397,8 +396,8 @@ def get_rgb(self): return self.ctx.get_source().get_rgba()[:3] def set_joinstyle(self, js): - super().set_joinstyle(js) - self.ctx.set_line_join(self._joind[self.get_joinstyle()]) + self.ctx.set_line_join(_api.check_getitem(self._joind, joinstyle=js)) + self._joinstyle = js def set_linewidth(self, w): self._linewidth = float(w) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index d0a390cff86b..636f87c3d97b 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -26,23 +26,22 @@ import matplotlib as mpl from matplotlib import _api, _text_layout, cbook from matplotlib._pylab_helpers import Gcf -from matplotlib.afm import AFM from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) from matplotlib.backends.backend_mixed import MixedModeRenderer -from matplotlib.dates import UTC -import matplotlib.dviread as dviread from matplotlib.figure import Figure from matplotlib.font_manager import findfont, get_font +from matplotlib.afm import AFM +import matplotlib.type1font as type1font +import matplotlib.dviread as dviread from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, LOAD_NO_HINTING, KERNING_UNFITTED) from matplotlib.mathtext import MathTextParser -from matplotlib import _path -from matplotlib.path import Path -from matplotlib._types import JoinStyle, CapStyle -import matplotlib.type1font as type1font from matplotlib.transforms import Affine2D, BboxBase +from matplotlib.path import Path +from matplotlib.dates import UTC +from matplotlib import _path from . import _backend_pdf_ps _log = logging.getLogger(__name__) @@ -747,8 +746,7 @@ def newPage(self, width, height): self.reserveObject('length of content stream')) # Initialize the pdf graphics state to match the default mpl # graphics context: currently only the join style needs to be set - self.output(GraphicsContextPdf.joinstyles[JoinStyle.round], - Op.setlinejoin) + self.output(GraphicsContextPdf.joinstyles['round'], Op.setlinejoin) # Clear the list of annotations for the next page self.pageAnnotations = [] @@ -2416,8 +2414,8 @@ def paint(self): """ return Op.paint_path(self.fill(), self.stroke()) - capstyles = {CapStyle.butt: 0, CapStyle.round: 1, CapStyle.projecting: 2} - joinstyles = {JoinStyle.miter: 0, JoinStyle.round: 1, JoinStyle.bevel: 2} + capstyles = {'butt': 0, 'round': 1, 'projecting': 2} + joinstyles = {'miter': 0, 'round': 1, 'bevel': 2} def capstyle_cmd(self, style): return [self.capstyles[style], Op.setlinecap] diff --git a/lib/matplotlib/backends/backend_pgf.py b/lib/matplotlib/backends/backend_pgf.py index 57d22dfe5de1..6cc27928733a 100644 --- a/lib/matplotlib/backends/backend_pgf.py +++ b/lib/matplotlib/backends/backend_pgf.py @@ -26,7 +26,6 @@ from matplotlib.path import Path from matplotlib.figure import Figure from matplotlib._pylab_helpers import Gcf -from matplotlib._types import JoinStyle, CapStyle _log = logging.getLogger(__name__) @@ -516,15 +515,15 @@ def _print_pgf_clip(self, gc): def _print_pgf_path_styles(self, gc, rgbFace): # cap style - capstyles = {CapStyle.butt: r"\pgfsetbuttcap", - CapStyle.round: r"\pgfsetroundcap", - CapStyle.projecting: r"\pgfsetrectcap"} + capstyles = {"butt": r"\pgfsetbuttcap", + "round": r"\pgfsetroundcap", + "projecting": r"\pgfsetrectcap"} writeln(self.fh, capstyles[gc.get_capstyle()]) # join style - joinstyles = {JoinStyle.miter: r"\pgfsetmiterjoin", - JoinStyle.round: r"\pgfsetroundjoin", - JoinStyle.bevel: r"\pgfsetbeveljoin"} + joinstyles = {"miter": r"\pgfsetmiterjoin", + "round": r"\pgfsetroundjoin", + "bevel": r"\pgfsetbeveljoin"} writeln(self.fh, joinstyles[gc.get_joinstyle()]) # filling diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index 3e8467adf5c6..7b4acb3ad29a 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -24,7 +24,6 @@ from matplotlib.backend_bases import ( _Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) -from matplotlib.backends.backend_mixed import MixedModeRenderer from matplotlib.cbook import is_writable_file_like, file_requires_unicode from matplotlib.font_manager import get_font from matplotlib.ft2font import LOAD_NO_HINTING, LOAD_NO_SCALE @@ -32,9 +31,9 @@ from matplotlib.mathtext import MathTextParser from matplotlib._mathtext_data import uni2type1 from matplotlib.path import Path -from matplotlib._types import JoinStyle, CapStyle from matplotlib.texmanager import TexManager from matplotlib.transforms import Affine2D +from matplotlib.backends.backend_mixed import MixedModeRenderer from . import _backend_pdf_ps _log = logging.getLogger(__name__) @@ -802,16 +801,11 @@ def _is_transparent(rgb_or_rgba): @_api.deprecated("3.4", alternative="GraphicsContextBase") class GraphicsContextPS(GraphicsContextBase): - - _capstyles = {CapStyle.butt: 0, CapStyle.round: 1, CapStyle.projecting: 2} - def get_capstyle(self): - return self._capstyles[super().get_capstyle()] - - _joinstyles = {JoinStyle.miter: 0, JoinStyle.round: 1, JoinStyle.bevel: 2} + return {'butt': 0, 'round': 1, 'projecting': 2}[super().get_capstyle()] def get_joinstyle(self): - return self._joinstyles[super().get_joinstyle()] + return {'miter': 0, 'round': 1, 'bevel': 2}[super().get_joinstyle()] class _Orientation(Enum): diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 7b31170729c4..ce6ad0c115f8 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -27,7 +27,6 @@ from matplotlib.path import Path from matplotlib import _path from matplotlib.transforms import Affine2D, Affine2DBase -from matplotlib._types import JoinStyle, CapStyle _log = logging.getLogger(__name__) @@ -572,10 +571,10 @@ def _get_style_dict(self, gc, rgbFace): attrib['stroke-opacity'] = short_float_fmt(rgb[3]) if linewidth != 1.0: attrib['stroke-width'] = short_float_fmt(linewidth) - if gc.get_joinstyle() != JoinStyle.round: - attrib['stroke-linejoin'] = gc.get_joinstyle().name - if gc.get_capstyle() != CapStyle.butt: - attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle().name] + if gc.get_joinstyle() != 'round': + attrib['stroke-linejoin'] = gc.get_joinstyle() + if gc.get_capstyle() != 'butt': + attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle()] return attrib diff --git a/lib/matplotlib/backends/backend_wx.py b/lib/matplotlib/backends/backend_wx.py index b431234bb212..a5f335cbbc32 100644 --- a/lib/matplotlib/backends/backend_wx.py +++ b/lib/matplotlib/backends/backend_wx.py @@ -28,7 +28,6 @@ from matplotlib.figure import Figure from matplotlib.path import Path from matplotlib.transforms import Affine2D -from matplotlib._types import JoinStyle, CapStyle from matplotlib.widgets import SubplotTool import wx @@ -327,13 +326,13 @@ class GraphicsContextWx(GraphicsContextBase): since wxPython colour management is rather simple, I have not chosen to implement a separate colour manager class. """ - _capd = {CapStyle.butt: wx.CAP_BUTT, - CapStyle.projecting: wx.CAP_PROJECTING, - CapStyle.round: wx.CAP_ROUND} + _capd = {'butt': wx.CAP_BUTT, + 'projecting': wx.CAP_PROJECTING, + 'round': wx.CAP_ROUND} - _joind = {JoinStyle.bevel: wx.JOIN_BEVEL, - JoinStyle.miter: wx.JOIN_MITER, - JoinStyle.round: wx.JOIN_ROUND} + _joind = {'bevel': wx.JOIN_BEVEL, + 'miter': wx.JOIN_MITER, + 'round': wx.JOIN_ROUND} _cache = weakref.WeakKeyDictionary() diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 4db9615d1328..6463cab6ff2c 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -659,12 +659,11 @@ def set_capstyle(self, cs): Parameters ---------- cs : `.CapStyle` or {'butt', 'round', 'projecting'} - The capstyle. """ self._capstyle = CapStyle(cs) def get_capstyle(self): - return self._capstyle + return self._capstyle.name def set_joinstyle(self, js): """ @@ -673,12 +672,11 @@ def set_joinstyle(self, js): Parameters ---------- js : `.JoinStyle` or {'miter', 'round', 'bevel'} - The joinstyle. """ 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 d846e2e20eac..0d288b84dd94 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -254,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 != "": @@ -1344,7 +1344,7 @@ def get_dash_joinstyle(self): See also `~.Line2D.set_dash_joinstyle`. """ - return self._dashjoinstyle + return self._dashjoinstyle.name def get_solid_joinstyle(self): """ @@ -1352,7 +1352,7 @@ def get_solid_joinstyle(self): See also `~.Line2D.set_solid_joinstyle`. """ - return self._solidjoinstyle + return self._solidjoinstyle.name def set_dash_capstyle(self, s): """ @@ -1386,7 +1386,7 @@ def get_dash_capstyle(self): See also `~.Line2D.set_dash_capstyle`. """ - return self._dashcapstyle + return self._dashcapstyle.name def get_solid_capstyle(self): """ @@ -1394,7 +1394,7 @@ def get_solid_capstyle(self): See also `~.Line2D.set_solid_capstyle`. """ - return self._solidcapstyle + return self._solidcapstyle.name def is_dashed(self): """ diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index db75a094ac62..64a58cbc289b 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -6,11 +6,12 @@ import pytest 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.pyplot as plt -from matplotlib._types import JoinStyle, CapStyle from matplotlib.testing.decorators import check_figures_equal, image_comparison import matplotlib.transforms as mtransforms @@ -530,17 +531,17 @@ def test_lslw_bcast(): @pytest.mark.style('default') def test_capstyle(): col = mcollections.PathCollection([], capstyle='round') - assert col.get_capstyle() == CapStyle.round + assert col.get_capstyle() == 'round' col.set_capstyle('butt') - assert col.get_capstyle() == CapStyle.butt + assert col.get_capstyle() == 'butt' @pytest.mark.style('default') def test_joinstyle(): col = mcollections.PathCollection([], joinstyle='round') - assert col.get_joinstyle() == JoinStyle.round + assert col.get_joinstyle() == 'round' col.set_joinstyle('miter') - assert col.get_joinstyle() == JoinStyle.miter + assert col.get_joinstyle() == 'miter' @image_comparison(['cap_and_joinstyle.png']) diff --git a/lib/matplotlib/tests/test_patches.py b/lib/matplotlib/tests/test_patches.py index a6001752bfe5..2a908098364e 100644 --- a/lib/matplotlib/tests/test_patches.py +++ b/lib/matplotlib/tests/test_patches.py @@ -6,10 +6,9 @@ import pytest from matplotlib.patches import Patch, Polygon, Rectangle, FancyArrowPatch -import matplotlib.pyplot as plt from matplotlib.testing.decorators import image_comparison, check_figures_equal from matplotlib.transforms import Bbox -from matplotlib._types import JoinStyle, CapStyle +import matplotlib.pyplot as plt from matplotlib import ( collections as mcollections, colors as mcolors, patches as mpatches, path as mpath, style as mstyle, transforms as mtransforms, rcParams) @@ -621,9 +620,9 @@ def test_default_linestyle(): def test_default_capstyle(): patch = Patch() - assert patch.get_capstyle() == CapStyle.butt + assert patch.get_capstyle() == 'butt' def test_default_joinstyle(): patch = Patch() - assert patch.get_joinstyle() == JoinStyle.miter + assert patch.get_joinstyle() == 'miter' diff --git a/src/py_converters.cpp b/src/py_converters.cpp index 208809328809..a4c0b1909940 100644 --- a/src/py_converters.cpp +++ b/src/py_converters.cpp @@ -123,17 +123,7 @@ int convert_cap(PyObject *capobj, void *capp) int values[] = {agg::butt_cap, agg::round_cap, agg::square_cap}; int result = agg::butt_cap; - PyObject *name = PyUnicode_FromString("name"); - PyObject *cap_string = PyObject_GetAttr(capobj, name); - if (cap_string == NULL) { - Py_DECREF(name); - PyErr_SetString(PyExc_TypeError, "capstyle must be a CapStyle (Enum) object."); - } - - int success = convert_string_enum(cap_string, "capstyle", names, values, &result); - Py_DECREF(name); - Py_DECREF(cap_string); - if (!success) { + if (!convert_string_enum(capobj, "capstyle", names, values, &result)) { return 0; } @@ -146,17 +136,8 @@ int convert_join(PyObject *joinobj, void *joinp) const char *names[] = {"miter", "round", "bevel", NULL}; int values[] = {agg::miter_join_revert, agg::round_join, agg::bevel_join}; int result = agg::miter_join_revert; - PyObject *name = PyUnicode_FromString("name"); - PyObject *join_string = PyObject_GetAttr(joinobj, name); - if (join_string == NULL) { - Py_DECREF(name); - PyErr_SetString(PyExc_TypeError, "joinstyle must be a JoinStyle (Enum) object."); - } - int success = convert_string_enum(join_string, "joinstyle", names, values, &result); - Py_DECREF(name); - Py_DECREF(join_string); - if (!success) { + if (!convert_string_enum(joinobj, "joinstyle", names, values, &result)) { return 0; } From 3c19b1f5fa5937531cd54a267a2fa6e4f52a6bdc Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Tue, 20 Oct 2020 12:02:43 -0700 Subject: [PATCH 3/4] DOCS: Centralize JoinStyle/CapStyle docs --- doc/_static/mpl.css | 21 +++- doc/api/_types.rst | 12 +- examples/lines_bars_and_markers/capstyle.py | 15 +++ examples/lines_bars_and_markers/joinstyle.py | 15 +++ lib/matplotlib/_types.py | 116 ++++++++++++------- lib/matplotlib/backend_bases.py | 10 +- lib/matplotlib/collections.py | 11 +- lib/matplotlib/lines.py | 24 ++-- lib/matplotlib/patches.py | 6 +- lib/matplotlib/rcsetup.py | 2 +- 10 files changed, 168 insertions(+), 64 deletions(-) create mode 100644 examples/lines_bars_and_markers/capstyle.py create mode 100644 examples/lines_bars_and_markers/joinstyle.py 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/_types.rst b/doc/api/_types.rst index de1be5cb95b4..88ded801768c 100644 --- a/doc/api/_types.rst +++ b/doc/api/_types.rst @@ -3,7 +3,13 @@ ********************** .. automodule:: matplotlib._types - :members: - :undoc-members: - :show-inheritance: + :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/examples/lines_bars_and_markers/capstyle.py b/examples/lines_bars_and_markers/capstyle.py new file mode 100644 index 000000000000..6fda277a9a46 --- /dev/null +++ b/examples/lines_bars_and_markers/capstyle.py @@ -0,0 +1,15 @@ +""" +========= +CapStyle +========= + +The `matplotlib._types.CapStyle` controls how Matplotlib draws the corners +where two different line segments meet. For more details, see the +`~matplotlib._types.CapStyle` docs. +""" + +import matplotlib.pyplot as plt +from matplotlib._types import CapStyle + +CapStyle.demo() +plt.show() diff --git a/examples/lines_bars_and_markers/joinstyle.py b/examples/lines_bars_and_markers/joinstyle.py new file mode 100644 index 000000000000..7d2605db3798 --- /dev/null +++ b/examples/lines_bars_and_markers/joinstyle.py @@ -0,0 +1,15 @@ +""" +========= +JoinStyle +========= + +The `matplotlib._types.JoinStyle` controls how Matplotlib draws the corners +where two different line segments meet. For more details, see the +`~matplotlib._types.JoinStyle` docs. +""" + +import matplotlib.pyplot as plt +from matplotlib._types import JoinStyle + +JoinStyle.demo() +plt.show() diff --git a/lib/matplotlib/_types.py b/lib/matplotlib/_types.py index f32cde398d50..dd7a8126a35b 100644 --- a/lib/matplotlib/_types.py +++ b/lib/matplotlib/_types.py @@ -1,16 +1,28 @@ """ -Style description information that is shared across unrelated classses. +Matplotlib API concepts that would not otherwise merit a dedicated class. + +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 +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() @@ -34,13 +46,7 @@ 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`: - - .. plot:: - :alt: Demo of possible JoinStyle's - - from matplotlib._types import JoinStyle - JoinStyle.demo() + `, 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 @@ -52,32 +58,42 @@ class JoinStyle(str, _AutoStringNameEnum): results in corners appearing "rounded", which may not be the desired behavior if you are drawing, for example, a polygon or pointed star. - Matplotlib provides three options for drawing the corners between adjacent - segments. In short: + **Supported values:** + + .. rst-class:: value-list - - *miter* is 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* is the "squared-off" style. It can be thought of as a rounded - corner where the "circular" part of the corner has been cut off. + '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:: - The *miter* option can be controlled further by specifying a "miter - limit", which specifies how long a miter tip can get before it is - automatically "bevel"ed off. Matplotlib does not currently expose a - ``miterlimit`` parameter to the user, and most backends simply use the - upstream default value. For example, the PDF backend assumes 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. + 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._types import JoinStyle + JoinStyle.demo() + """ miter = auto() @@ -90,6 +106,7 @@ def __init__(self, s): @staticmethod def demo(): + """Demonstrate how each JoinStyle looks for various join angles.""" import numpy as np import matplotlib.pyplot as plt @@ -101,7 +118,7 @@ def plot_angle(ax, x, y, angle, 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)) + 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) @@ -115,6 +132,11 @@ def plot_angle(ax, x, y, angle, style): 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. @@ -125,7 +147,20 @@ class CapStyle(str, _AutoStringNameEnum): controlled by the *CapStyle*. For a visual impression of each *CapStyle*, `view these docs online - ` or run `CapStyle.demo`: + ` 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 @@ -133,13 +168,6 @@ class CapStyle(str, _AutoStringNameEnum): from matplotlib._types import CapStyle CapStyle.demo() - Available options: - - - *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``. """ butt = 'butt' projecting = 'projecting' @@ -151,20 +179,30 @@ def __init__(self, s): @staticmethod def demo(): + """Demonstrate how each CapStyle looks for a thick line segment.""" import matplotlib.pyplot as plt - fig, ax = plt.subplots(figsize=(8, 2)) + 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, 1, style, ha='center') + 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.7, '(default)', ha='center') + 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 0f5a72bd4015..b46f78296b45 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -44,8 +44,8 @@ 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 @@ -917,13 +917,14 @@ 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 how to draw endpoints of lines. Parameters ---------- - cs : `.CapStyle` or {'butt', 'round', 'projecting'} + cs : `.CapStyle` or %(CapStyle)s """ self._capstyle = CapStyle(cs) @@ -982,13 +983,14 @@ def set_foreground(self, fg, isRGBA=False): else: self._rgb = colors.to_rgba(fg) + @docstring.interpd def set_joinstyle(self, js): """ Set how to draw connections between line segments. Parameters ---------- - js : `.JoinStyle` or {'miter', 'round', 'bevel'}. + js : `.JoinStyle` or %(JoinStyle)s """ self._joinstyle = JoinStyle(js) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 6463cab6ff2c..0b445d66b070 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -72,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, @@ -113,8 +114,10 @@ def __init__(self, :doc:`/gallery/lines_bars_and_markers/linestyles`. capstyle : `.CapStyle`-like, default: :rc:`patch.capstyle` Style to use for capping lines for all paths in the collection. + Allowed values are %(CapStyle)s. joinstyle : `.JoinStyle`-like, default: :rc:`patch.joinstyle` Style to use for joining lines for all paths in the collection. + 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. @@ -125,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 @@ -652,26 +655,28 @@ 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). Parameters ---------- - cs : `.CapStyle` or {'butt', 'round', 'projecting'} + cs : `.CapStyle` or %(CapStyle)s """ self._capstyle = CapStyle(cs) def get_capstyle(self): return self._capstyle.name + @docstring.interpd def set_joinstyle(self, js): """ Set the `.JoinStyle` for the collection (for all its elements). Parameters ---------- - js : `.JoinStyle` or {'miter', 'round', 'bevel'} + js : `.JoinStyle` or %(JoinStyle)s """ self._joinstyle = JoinStyle(js) diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 0d288b84dd94..121e9b7e15f6 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -1157,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. @@ -1312,26 +1312,28 @@ def update_from(self, other): self._marker = MarkerStyle(marker=other._marker) self._drawstyle = other._drawstyle + @docstring.interpd def set_dash_joinstyle(self, s): """ - How to join segments of the line if it `~Line2D.is dashed`. + How to join segments of the line if it `~Line2D.is_dashed`. Parameters ---------- - s : `.JoinStyle` or {'miter', 'round', 'bevel'} + s : `.JoinStyle` or %(JoinStyle)s """ js = JoinStyle(s) if self._dashjoinstyle != js: self.stale = True self._dashjoinstyle = js + @docstring.interpd def set_solid_joinstyle(self, s): """ How to join segments if the line is solid (not `~Line2D.is_dashed`). Parameters ---------- - s : `.JoinStyle` or {'miter', 'round', 'bevel'} + s : `.JoinStyle` or %(JoinStyle)s """ js = JoinStyle(s) if self._solidjoinstyle != js: @@ -1354,26 +1356,28 @@ def get_solid_joinstyle(self): """ return self._solidjoinstyle.name + @docstring.interpd def set_dash_capstyle(self, s): """ How to draw the end caps if the line is `~Line2D.is_dashed`. Parameters ---------- - s : `.CapStyle` or {'butt', 'round', 'projecting'} + s : `.CapStyle` or %(CapStyle)s """ cs = CapStyle(s) if self._dashcapstyle != cs: self.stale = True self._dashcapstyle = cs + @docstring.interpd def set_solid_capstyle(self, s): """ How to draw the end caps if the line is solid (not `~Line2D.is_dashed`) Parameters ---------- - s : `.CapStyle` or {'butt', 'round', 'projecting'} + s : `.CapStyle` or %(CapStyle)s """ cs = CapStyle(s) if self._solidcapstyle != cs: @@ -1398,8 +1402,10 @@ def get_solid_capstyle(self): 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. + 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`. """ @@ -1554,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/patches.py b/lib/matplotlib/patches.py index 86edaf667b72..8b4e1d34fac3 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -472,13 +472,14 @@ def get_fill(self): # attribute. fill = property(get_fill, set_fill) + @docstring.interpd def set_capstyle(self, s): """ Set the `.CapStyle`. Parameters ---------- - s : `.CapStyle` or {'butt', 'round', 'projecting'} + s : `.CapStyle` or %(CapStyle)s """ cs = CapStyle(s) self._capstyle = cs @@ -488,13 +489,14 @@ def get_capstyle(self): """Return the capstyle.""" return self._capstyle + @docstring.interpd def set_joinstyle(self, s): """ Set the `.JoinStyle`. Parameters ---------- - s : `.JoinStyle` or {'miter', 'round', 'bevel'} + s : `.JoinStyle` or %(JoinStyle)s """ js = JoinStyle(s) self._joinstyle = js diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index d31f4ba50106..20d6216efc28 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -22,7 +22,7 @@ import numpy as np -from matplotlib import animation, cbook +from matplotlib import _api, animation, cbook from matplotlib.cbook import ls_mapper from matplotlib.colors import Colormap, is_color_like from matplotlib.fontconfig_pattern import parse_fontconfig_pattern From 4894099e025f0006462d0b1392701ef49876a84d Mon Sep 17 00:00:00 2001 From: Bruno Beltran Date: Mon, 14 Dec 2020 12:51:15 -0800 Subject: [PATCH 4/4] REORG: _types -> _enums --- doc/api/{_types.rst => _enums.rst} | 4 ++-- doc/api/index.rst | 2 +- examples/lines_bars_and_markers/capstyle.py | 6 +++--- examples/lines_bars_and_markers/joinstyle.py | 6 +++--- lib/matplotlib/{_types.py => _enums.py} | 6 +++--- lib/matplotlib/backend_bases.py | 2 +- lib/matplotlib/collections.py | 2 +- lib/matplotlib/lines.py | 2 +- lib/matplotlib/markers.py | 2 +- lib/matplotlib/patches.py | 2 +- lib/matplotlib/rcsetup.py | 2 +- lib/matplotlib/tests/test_collections.py | 4 +--- 12 files changed, 19 insertions(+), 21 deletions(-) rename doc/api/{_types.rst => _enums.rst} (83%) rename lib/matplotlib/{_types.py => _enums.py} (97%) diff --git a/doc/api/_types.rst b/doc/api/_enums.rst similarity index 83% rename from doc/api/_types.rst rename to doc/api/_enums.rst index 88ded801768c..c9e283305967 100644 --- a/doc/api/_types.rst +++ b/doc/api/_enums.rst @@ -1,8 +1,8 @@ ********************** -``matplotlib._types`` +``matplotlib._enums`` ********************** -.. automodule:: matplotlib._types +.. automodule:: matplotlib._enums :no-members: .. autoclass:: JoinStyle diff --git a/doc/api/index.rst b/doc/api/index.rst index 10d6d600137d..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 @@ -124,7 +125,6 @@ Matplotlib consists of the following submodules: transformations.rst tri_api.rst type1font.rst - _types.rst units_api.rst widgets_api.rst _api_api.rst diff --git a/examples/lines_bars_and_markers/capstyle.py b/examples/lines_bars_and_markers/capstyle.py index 6fda277a9a46..05bb1e96585c 100644 --- a/examples/lines_bars_and_markers/capstyle.py +++ b/examples/lines_bars_and_markers/capstyle.py @@ -3,13 +3,13 @@ CapStyle ========= -The `matplotlib._types.CapStyle` controls how Matplotlib draws the corners +The `matplotlib._enums.CapStyle` controls how Matplotlib draws the corners where two different line segments meet. For more details, see the -`~matplotlib._types.CapStyle` docs. +`~matplotlib._enums.CapStyle` docs. """ import matplotlib.pyplot as plt -from matplotlib._types import CapStyle +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 7d2605db3798..0f289d7ac393 100644 --- a/examples/lines_bars_and_markers/joinstyle.py +++ b/examples/lines_bars_and_markers/joinstyle.py @@ -3,13 +3,13 @@ JoinStyle ========= -The `matplotlib._types.JoinStyle` controls how Matplotlib draws the corners +The `matplotlib._enums.JoinStyle` controls how Matplotlib draws the corners where two different line segments meet. For more details, see the -`~matplotlib._types.JoinStyle` docs. +`~matplotlib._enums.JoinStyle` docs. """ import matplotlib.pyplot as plt -from matplotlib._types import JoinStyle +from matplotlib._enums import JoinStyle JoinStyle.demo() plt.show() diff --git a/lib/matplotlib/_types.py b/lib/matplotlib/_enums.py similarity index 97% rename from lib/matplotlib/_types.py rename to lib/matplotlib/_enums.py index dd7a8126a35b..35fe82482869 100644 --- a/lib/matplotlib/_types.py +++ b/lib/matplotlib/_enums.py @@ -1,5 +1,5 @@ """ -Matplotlib API concepts that would not otherwise merit a dedicated class. +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', @@ -91,7 +91,7 @@ class JoinStyle(str, _AutoStringNameEnum): .. plot:: :alt: Demo of possible JoinStyle's - from matplotlib._types import JoinStyle + from matplotlib._enums import JoinStyle JoinStyle.demo() """ @@ -165,7 +165,7 @@ class CapStyle(str, _AutoStringNameEnum): .. plot:: :alt: Demo of possible CapStyle's - from matplotlib._types import CapStyle + from matplotlib._enums import CapStyle CapStyle.demo() """ diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index b46f78296b45..b249f5e2a85a 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -51,7 +51,7 @@ from matplotlib.cbook import _setattr_cm from matplotlib.path import Path from matplotlib.transforms import Affine2D -from matplotlib._types import JoinStyle, CapStyle +from matplotlib._enums import JoinStyle, CapStyle _log = logging.getLogger(__name__) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 0b445d66b070..148665f5a410 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -16,7 +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 ._types import JoinStyle, CapStyle +from ._enums import JoinStyle, CapStyle import warnings diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 121e9b7e15f6..41b2f98c314e 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -17,7 +17,7 @@ from .markers import MarkerStyle from .path import Path from .transforms import Bbox, BboxTransformTo, TransformedPath -from ._types import JoinStyle, CapStyle +from ._enums import JoinStyle, CapStyle # Imported here for backward compatibility, even though they don't # really belong. diff --git a/lib/matplotlib/markers.py b/lib/matplotlib/markers.py index 30733e8faf0b..ae2bb3da83ac 100644 --- a/lib/matplotlib/markers.py +++ b/lib/matplotlib/markers.py @@ -134,7 +134,7 @@ from . import _api, cbook, rcParams from .path import Path from .transforms import IdentityTransform, Affine2D -from ._types import JoinStyle, CapStyle +from ._enums import JoinStyle, CapStyle # special-purpose marker identifiers: (TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN, diff --git a/lib/matplotlib/patches.py b/lib/matplotlib/patches.py index 8b4e1d34fac3..81df8568f243 100644 --- a/lib/matplotlib/patches.py +++ b/lib/matplotlib/patches.py @@ -16,7 +16,7 @@ get_parallels, inside_circle, make_wedged_bezier2, split_bezier_intersecting_with_closedpath, split_path_inout) from .path import Path -from ._types import JoinStyle, CapStyle +from ._enums import JoinStyle, CapStyle @cbook._define_aliases({ diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 20d6216efc28..535649b03f9f 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -26,7 +26,7 @@ from matplotlib.cbook import ls_mapper from matplotlib.colors import Colormap, is_color_like from matplotlib.fontconfig_pattern import parse_fontconfig_pattern -from matplotlib._types import JoinStyle, CapStyle +from matplotlib._enums import JoinStyle, CapStyle # Don't let the original cycler collide with our validating cycler from cycler import Cycler, cycler as ccycler diff --git a/lib/matplotlib/tests/test_collections.py b/lib/matplotlib/tests/test_collections.py index 64a58cbc289b..9fe73e3b892a 100644 --- a/lib/matplotlib/tests/test_collections.py +++ b/lib/matplotlib/tests/test_collections.py @@ -8,12 +8,10 @@ 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.pyplot as plt -from matplotlib.testing.decorators import check_figures_equal, image_comparison import matplotlib.transforms as mtransforms +from matplotlib.testing.decorators import check_figures_equal, image_comparison def generate_EventCollection_plot(): 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