From cb52bc07c267f20dc3b4ce0d4e3243570fcde414 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 27 Jan 2016 12:03:17 -0500 Subject: [PATCH 1/2] Fix #5917. New dash patterns. Scale dashes by lw --- lib/matplotlib/backend_bases.py | 39 ++++++++----------- lib/matplotlib/collections.py | 13 ++----- lib/matplotlib/lines.py | 18 +++++++++ .../mpl-data/stylelib/classic.mplstyle | 3 ++ lib/matplotlib/rcsetup.py | 15 ++++--- matplotlibrc.template | 5 +++ src/py_converters.cpp | 2 +- 7 files changed, 55 insertions(+), 40 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 62277f73ddc3..3fc97e757124 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -55,6 +55,7 @@ from matplotlib import is_interactive from matplotlib import get_backend from matplotlib._pylab_helpers import Gcf +from matplotlib import lines from matplotlib.transforms import Bbox, TransformedBbox, Affine2D @@ -771,14 +772,6 @@ class GraphicsContextBase(object): An abstract base class that provides color, line styles, etc... """ - # a mapping from dash styles to suggested offset, dash pairs - dashd = { - 'solid': (None, None), - 'dashed': (0, (6.0, 6.0)), - 'dashdot': (0, (3.0, 5.0, 1.0, 5.0)), - 'dotted': (0, (1.0, 3.0)), - } - def __init__(self): self._alpha = 1.0 self._forced_alpha = False # if True, _alpha overrides A from RGBA @@ -870,7 +863,16 @@ def get_dashes(self): Default value is None """ - return self._dashes + if rcParams['_internal.classic_mode']: + return self._dashes + else: + scale = max(1.0, self.get_linewidth()) + offset, dashes = self._dashes + if offset is not None: + offset = offset * scale + if dashes is not None: + dashes = [x * scale for x in dashes] + return offset, dashes def get_forced_alpha(self): """ @@ -1047,21 +1049,12 @@ def set_linewidth(self, w): def set_linestyle(self, style): """ Set the linestyle to be one of ('solid', 'dashed', 'dashdot', - 'dotted'). One may specify customized dash styles by providing - a tuple of (offset, dash pairs). For example, the predefiend - linestyles have following values.: - - 'dashed' : (0, (6.0, 6.0)), - 'dashdot' : (0, (3.0, 5.0, 1.0, 5.0)), - 'dotted' : (0, (1.0, 3.0)), + 'dotted'). These are defined in the rcParams + `lines.dashed_pattern`, `lines.dashdot_pattern` and + `lines.dotted_pattern`. One may also specify customized dash + styles by providing a tuple of (offset, dash pairs). """ - - if style in self.dashd: - offset, dashes = self.dashd[style] - elif isinstance(style, tuple): - offset, dashes = style - else: - raise ValueError('Unrecognized linestyle: %s' % str(style)) + offset, dashes = lines.get_dash_pattern(style) self._linestyle = style self.set_dashes(offset, dashes) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 7032bce22a21..eec52b76578f 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -28,6 +28,7 @@ import matplotlib.path as mpath from matplotlib import _path import matplotlib.mlab as mlab +import matplotlib.lines as mlines CIRCLE_AREA_FACTOR = 1.0 / np.sqrt(np.pi) @@ -531,23 +532,15 @@ def set_linestyle(self, ls): The line style. """ try: - dashd = backend_bases.GraphicsContextBase.dashd if cbook.is_string_like(ls): - ls = cbook.ls_mapper.get(ls, ls) - if ls in dashd: - dashes = [dashd[ls]] - else: - raise ValueError() + dashes = [mlines.get_dash_pattern(ls)] elif cbook.iterable(ls): try: dashes = [] for x in ls: if cbook.is_string_like(x): x = cbook.ls_mapper.get(x, x) - if x in dashd: - dashes.append(dashd[x]) - else: - raise ValueError() + dashes.append(mlines.get_dash_pattern(x)) elif cbook.iterable(x) and len(x) == 2: dashes.append(x) else: diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index e8a032b9c2fa..3a671e2b3c9e 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -36,6 +36,24 @@ from matplotlib import _path +def get_dash_pattern(style): + """ + Given a dash pattern name from 'solid', 'dashed', 'dashdot' or + 'dotted', returns the (offset, dashes) pattern. + """ + if style == 'solid': + offset, dashes = None, None + elif style in ['dashed', 'dashdot', 'dotted']: + offset = 0 + dashes = tuple(rcParams['lines.{}_pattern'.format(style)]) + elif isinstance(style, tuple): + offset, dashes = style + else: + raise ValueError('Unrecognized linestyle: %s' % str(style)) + + return offset, dashes + + def segment_hits(cx, cy, x, y, radius): """ Determine if any line segments are within radius of a diff --git a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle index 62de031f6db6..c5cdb660cd63 100644 --- a/lib/matplotlib/mpl-data/stylelib/classic.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/classic.mplstyle @@ -15,6 +15,9 @@ lines.dash_capstyle : butt # butt|round|projecting lines.solid_joinstyle : round # miter|round|bevel lines.solid_capstyle : projecting # butt|round|projecting lines.antialiased : True # render lines in antialiased (no jaggies) +lines.dashed_pattern : 6, 6 +lines.dashdot_pattern : 3, 5, 1, 5 +lines.dotted_pattern : 1, 3 ### Marker props markers.fillstyle: full diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index f8c239867087..46802d4b3f63 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -275,18 +275,18 @@ def validate_maskedarray(v): class validate_nseq_float(object): - def __init__(self, n): + def __init__(self, n=None): self.n = n def __call__(self, s): """return a seq of n floats or raise""" if isinstance(s, six.string_types): - s = s.split(',') + s = [x.strip() for x in s.split(',')] err_msg = _str_err_msg else: err_msg = _seq_err_msg - if len(s) != self.n: + if self.n is not None and len(s) != self.n: raise ValueError(err_msg.format(n=self.n, num=len(s), s=s)) try: @@ -296,18 +296,18 @@ def __call__(self, s): class validate_nseq_int(object): - def __init__(self, n): + def __init__(self, n=None): self.n = n def __call__(self, s): """return a seq of n ints or raise""" if isinstance(s, six.string_types): - s = s.split(',') + s = [x.strip() for x in s.split(',')] err_msg = _str_err_msg else: err_msg = _seq_err_msg - if len(s) != self.n: + if self.n is not None and len(s) != self.n: raise ValueError(err_msg.format(n=self.n, num=len(s), s=s)) try: @@ -836,6 +836,9 @@ def validate_hist_bins(s): 'lines.solid_joinstyle': ['round', validate_joinstyle], 'lines.dash_capstyle': ['butt', validate_capstyle], 'lines.solid_capstyle': ['projecting', validate_capstyle], + 'lines.dashed_pattern': [[2.8, 1.2], validate_nseq_float()], + 'lines.dashdot_pattern': [[4.8, 1.2, 0.8, 1.2], validate_nseq_float()], + 'lines.dotted_pattern': [[1.2, 0.6], validate_nseq_float()], # marker props 'markers.fillstyle': ['full', validate_fillstyle], diff --git a/matplotlibrc.template b/matplotlibrc.template index f52ea5794772..00a3fccb1979 100644 --- a/matplotlibrc.template +++ b/matplotlibrc.template @@ -90,6 +90,11 @@ backend : $TEMPLATE_BACKEND #lines.solid_capstyle : projecting # butt|round|projecting #lines.antialiased : True # render lines in antialiased (no jaggies) +# The three standard dash patterns. These are scaled by the linewidth. +#lines.dashed_pattern : 2.8, 1.2 +#lines.dashdot_pattern : 4.8, 1.2, 0.8, 1.2 +#lines.dotted_pattern : 1.2, 0.6 + #markers.fillstyle: full # full|left|right|bottom|top|none ### PATCHES diff --git a/src/py_converters.cpp b/src/py_converters.cpp index fee7bf8ad463..631763bbc37a 100644 --- a/src/py_converters.cpp +++ b/src/py_converters.cpp @@ -476,7 +476,7 @@ int convert_gcagg(PyObject *pygc, void *gcp) convert_from_attr(pygc, "_antialiased", &convert_bool, &gc->isaa) && convert_from_attr(pygc, "_capstyle", &convert_cap, &gc->cap) && convert_from_attr(pygc, "_joinstyle", &convert_join, &gc->join) && - convert_from_attr(pygc, "_dashes", &convert_dashes, &gc->dashes) && + convert_from_method(pygc, "get_dashes", &convert_dashes, &gc->dashes) && convert_from_attr(pygc, "_cliprect", &convert_rect, &gc->cliprect) && convert_from_method(pygc, "get_clip_path", &convert_clippath, &gc->clippath) && convert_from_method(pygc, "get_snap", &convert_snap, &gc->snap_mode) && From 104e98eadfe47b186711a968131457e8833ea728 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Wed, 27 Jan 2016 12:52:10 -0500 Subject: [PATCH 2/2] Fix short dash style aliases --- lib/matplotlib/collections.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index eec52b76578f..3bdf84ba1245 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -533,6 +533,7 @@ def set_linestyle(self, ls): """ try: if cbook.is_string_like(ls): + ls = cbook.ls_mapper.get(ls, ls) dashes = [mlines.get_dash_pattern(ls)] elif cbook.iterable(ls): try: