Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
DOCS: Centralize JoinStyle/CapStyle docs
  • Loading branch information
brunobeltran committed Feb 1, 2021
commit 3c19b1f5fa5937531cd54a267a2fa6e4f52a6bdc
21 changes: 18 additions & 3 deletions doc/_static/mpl.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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%;
Expand Down Expand Up @@ -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;
}
}
12 changes: 9 additions & 3 deletions doc/api/_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
**********************

.. automodule:: matplotlib._types

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok for now.

When we get more entries, every entry should probably get it's own sub-page. But since we might fiddle with the organization/representation anyway (c.f. sphinx-gallery), it's not worth to work on this right now.

:members:
:undoc-members:
:show-inheritance:
:no-members:

.. autoclass:: JoinStyle
:members: demo
:exclude-members: bevel, miter, round, input_description
Comment thread
timhoffm marked this conversation as resolved.

.. autoclass:: CapStyle
:members: demo
:exclude-members: butt, round, projecting, input_description
Comment thread
brunobeltran marked this conversation as resolved.

15 changes: 15 additions & 0 deletions examples/lines_bars_and_markers/capstyle.py
Original file line number Diff line number Diff line change
@@ -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()
15 changes: 15 additions & 0 deletions examples/lines_bars_and_markers/joinstyle.py
Original file line number Diff line number Diff line change
@@ -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()
116 changes: 77 additions & 39 deletions lib/matplotlib/_types.py
Original file line number Diff line number Diff line change
@@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth noting that this will become unneeded (it is the default behavior) in Py3.10 per https://bugs.python.org/issue42385.


def __hash__(self):
return str(self).__hash__()


def _deprecate_case_insensitive_join_cap(s):
s_low = s.lower()
Expand All @@ -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
<JoinStyle>`, or run `JoinStyle.demo`:

.. plot::
:alt: Demo of possible JoinStyle's

from matplotlib._types import JoinStyle
JoinStyle.demo()
<JoinStyle>`, 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
Expand All @@ -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
<https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit>`_

.. plot::
:alt: Demo of possible JoinStyle's

from matplotlib._types import JoinStyle
JoinStyle.demo()

"""

miter = auto()
Expand All @@ -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

Expand All @@ -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)
Expand All @@ -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):

@timhoffm timhoffm Oct 21, 2020 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not particularly happy with the overall appearance of the docs (https://48026-1385122-gh.circle-artifacts.com/0/doc/build/html/api/_types.html#matplotlib._types.CapStyle). It looks a bit cluttered:

  • The supported values should be more prominent, They now blend into the bulk of text and are hard to find.
  • The demo image should be below the list of values.
  • There's a lot of redundancy: Available values are mentioned 4 times: list including descriptions, in the example plot, in input_description, as attributes of the Enum.
  • While I see the use-case demo() still feels a bit out of place. You can keep it for now, but please give it a docstring.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the output looks cluttered. I've:

  • edited the docstrings to emphasize supported values, by making it a section
  • moved the image to the bottom
  • removed all automatic member documentation from the enum to prevent any redundacy

@timhoffm timhoffm Nov 8, 2020 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above are good steps in the right direction, but I'm still somewhat worried that the values are difficult to find. They blend into the paragraphs of the bullet list.

Visibility could be further enhanced with a definition list. E.g.

    **Supported values:**

    .. rst-class:: value-list

       'miter'
          the "arrow-tip" style. Each boundary of the filled-in area
          will extend in a straight line parallel to the tangent vector of the
          centerline at the point it meets the corner, until they meet in a
          sharp point.
       'round'
          stokes every point within a radius of ``linewidth/2`` of the
          center lines.
       'bevel'
          the "squared-off" style. It can be thought of as a rounded
          corner where the "circular" part of the corner has been cut off.

Will result in <dl class="value-list">...</dl> so that we can CSS-style based on .value-list, e.g.:

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;
}

resulting in:

grafik

This is the lightweight version of making your own .. value-list:: role, which may or may not be reasonable later on.

r"""
Define how the two endpoints (caps) of an unclosed line are drawn.
Expand All @@ -125,21 +147,27 @@ class CapStyle(str, _AutoStringNameEnum):
controlled by the *CapStyle*.

For a visual impression of each *CapStyle*, `view these docs online
<CapStyle>` or run `CapStyle.demo`:
<CapStyle>` or run `CapStyle.demo`.

**Supported values:**

.. rst-class:: value-list

'butt'
the line is squared off at its endpoint.
'projecting'
the line is squared off as in *butt*, but the filled in area
extends beyond the endpoint a distance of ``linewidth/2``.
'round'
like *butt*, but a semicircular cap is added to the end of the
line, of radius ``linewidth/2``.

.. plot::
:alt: Demo of possible CapStyle's

from matplotlib._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'
Expand All @@ -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})
10 changes: 6 additions & 4 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)

Expand Down
Loading