-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
REORG: JoinStyle and CapStyle classes #18544
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
********************** | ||
``matplotlib._enums`` | ||
********************** | ||
|
||
.. automodule:: matplotlib._enums | ||
:no-members: | ||
|
||
.. autoclass:: JoinStyle | ||
:members: demo | ||
:exclude-members: bevel, miter, round, input_description | ||
|
||
.. autoclass:: CapStyle | ||
:members: demo | ||
:exclude-members: butt, round, projecting, input_description | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
""" | ||
========= | ||
CapStyle | ||
========= | ||
|
||
The `matplotlib._enums.CapStyle` controls how Matplotlib draws the corners | ||
where two different line segments meet. For more details, see the | ||
`~matplotlib._enums.CapStyle` docs. | ||
""" | ||
|
||
import matplotlib.pyplot as plt | ||
from matplotlib._enums import CapStyle | ||
|
||
CapStyle.demo() | ||
plt.show() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,90 +1,15 @@ | ||
""" | ||
========================== | ||
Join styles and cap styles | ||
========================== | ||
========= | ||
JoinStyle | ||
========= | ||
|
||
This example demonstrates the available join styles and cap styles. | ||
|
||
Both are used in `.Line2D` and various ``Collections`` from | ||
`matplotlib.collections` as well as some functions that create these, e.g. | ||
`~matplotlib.pyplot.plot`. | ||
|
||
|
||
Join styles | ||
=========== | ||
|
||
Join styles define how the connection between two line segments is drawn. | ||
|
||
See the respective ``solid_joinstyle``, ``dash_joinstyle`` or ``joinstyle`` | ||
parameters. | ||
The `matplotlib._enums.JoinStyle` controls how Matplotlib draws the corners | ||
where two different line segments meet. For more details, see the | ||
`~matplotlib._enums.JoinStyle` docs. | ||
""" | ||
|
||
import numpy as np | ||
import matplotlib.pyplot as plt | ||
from matplotlib._enums import JoinStyle | ||
|
||
|
||
def plot_angle(ax, x, y, angle, style): | ||
phi = np.radians(angle) | ||
xx = [x + .5, x, x + .5*np.cos(phi)] | ||
yy = [y, y, y + .5*np.sin(phi)] | ||
ax.plot(xx, yy, lw=12, color='tab:blue', solid_joinstyle=style) | ||
ax.plot(xx, yy, lw=1, color='black') | ||
ax.plot(xx[1], yy[1], 'o', color='tab:red', markersize=3) | ||
|
||
|
||
fig, ax = plt.subplots(figsize=(8, 6)) | ||
ax.set_title('Join style') | ||
|
||
for x, style in enumerate(['miter', 'round', 'bevel']): | ||
ax.text(x, 5, style) | ||
for y, angle in enumerate([20, 45, 60, 90, 120]): | ||
plot_angle(ax, x, y, angle, style) | ||
if x == 0: | ||
ax.text(-1.3, y, f'{angle} degrees') | ||
ax.text(1, 4.7, '(default)') | ||
|
||
ax.set_xlim(-1.5, 2.75) | ||
ax.set_ylim(-.5, 5.5) | ||
ax.set_axis_off() | ||
JoinStyle.demo() | ||
plt.show() | ||
|
||
|
||
############################################################################# | ||
# | ||
# Cap styles | ||
# ========== | ||
# | ||
# Cap styles define how the the end of a line is drawn. | ||
# | ||
# See the respective ``solid_capstyle``, ``dash_capstyle`` or ``capstyle`` | ||
# parameters. | ||
|
||
fig, ax = plt.subplots(figsize=(8, 2)) | ||
ax.set_title('Cap style') | ||
|
||
for x, style in enumerate(['butt', 'round', 'projecting']): | ||
ax.text(x+0.25, 1, style, ha='center') | ||
xx = [x, x+0.5] | ||
yy = [0, 0] | ||
ax.plot(xx, yy, lw=12, color='tab:blue', solid_capstyle=style) | ||
ax.plot(xx, yy, lw=1, color='black') | ||
ax.plot(xx, yy, 'o', color='tab:red', markersize=3) | ||
ax.text(2.25, 0.7, '(default)', ha='center') | ||
|
||
ax.set_ylim(-.5, 1.5) | ||
ax.set_axis_off() | ||
|
||
|
||
############################################################################# | ||
# | ||
# ------------ | ||
# | ||
# References | ||
# """""""""" | ||
# | ||
# The use of the following functions, methods, classes and modules is shown | ||
# in this example: | ||
|
||
import matplotlib | ||
matplotlib.axes.Axes.plot | ||
matplotlib.pyplot.plot |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
""" | ||
Enums representing sets of strings that Matplotlib uses as input parameters. | ||
|
||
Matplotlib often uses simple data types like strings or tuples to define a | ||
concept; e.g. the line capstyle can be specified as one of 'butt', 'round', | ||
or 'projecting'. The classes in this module are used internally and serve to | ||
document these concepts formally. | ||
|
||
As an end-user you will not use these classes directly, but only the values | ||
they define. | ||
""" | ||
|
||
from enum import Enum, auto | ||
from matplotlib import cbook, docstring | ||
|
||
|
||
class _AutoStringNameEnum(Enum): | ||
"""Automate the ``name = 'name'`` part of making a (str, Enum).""" | ||
|
||
def _generate_next_value_(name, start, count, last_values): | ||
return name | ||
|
||
def __hash__(self): | ||
return str(self).__hash__() | ||
|
||
|
||
def _deprecate_case_insensitive_join_cap(s): | ||
s_low = s.lower() | ||
if s != s_low: | ||
if s_low in ['miter', 'round', 'bevel']: | ||
cbook.warn_deprecated( | ||
"3.3", message="Case-insensitive capstyles are deprecated " | ||
"since %(since)s and support for them will be removed " | ||
"%(removal)s; please pass them in lowercase.") | ||
elif s_low in ['butt', 'round', 'projecting']: | ||
cbook.warn_deprecated( | ||
"3.3", message="Case-insensitive joinstyles are deprecated " | ||
"since %(since)s and support for them will be removed " | ||
"%(removal)s; please pass them in lowercase.") | ||
# Else, error out at the check_in_list stage. | ||
return s_low | ||
|
||
|
||
class JoinStyle(str, _AutoStringNameEnum): | ||
""" | ||
Define how the connection between two line segments is drawn. | ||
|
||
For a visual impression of each *JoinStyle*, `view these docs online | ||
<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 | ||
center of the stroked line. | ||
|
||
By default, `~.backend_bases.GraphicsContextBase` defines the boundaries of | ||
a stroked line to simply be every point within some radius, | ||
``linewidth/2``, away from any point of the center line. However, this | ||
results in corners appearing "rounded", which may not be the desired | ||
behavior if you are drawing, for example, a polygon or pointed star. | ||
|
||
**Supported values:** | ||
|
||
.. rst-class:: value-list | ||
|
||
'miter' | ||
the "arrow-tip" style. Each boundary of the filled-in area will | ||
extend in a straight line parallel to the tangent vector of the | ||
centerline at the point it meets the corner, until they meet in a | ||
sharp point. | ||
'round' | ||
stokes every point within a radius of ``linewidth/2`` of the center | ||
lines. | ||
'bevel' | ||
the "squared-off" style. It can be thought of as a rounded corner | ||
where the "circular" part of the corner has been cut off. | ||
|
||
.. note:: | ||
|
||
Very long miter tips are cut off (to form a *bevel*) after a | ||
backend-dependent limit called the "miter limit", which specifies the | ||
maximum allowed ratio of miter length to line width. For example, the | ||
PDF backend uses the default value of 10 specified by the PDF standard, | ||
while the SVG backend does not even specify the miter limit, resulting | ||
in a default value of 4 per the SVG specification. Matplotlib does not | ||
currently allow the user to adjust this parameter. | ||
|
||
A more detailed description of the effect of a miter limit can be found | ||
in the `Mozilla Developer Docs | ||
<https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-miterlimit>`_ | ||
|
||
.. plot:: | ||
:alt: Demo of possible JoinStyle's | ||
|
||
from matplotlib._enums import JoinStyle | ||
JoinStyle.demo() | ||
|
||
""" | ||
|
||
miter = auto() | ||
round = auto() | ||
bevel = auto() | ||
|
||
def __init__(self, s): | ||
s = _deprecate_case_insensitive_join_cap(s) | ||
Enum.__init__(self) | ||
|
||
@staticmethod | ||
def demo(): | ||
"""Demonstrate how each JoinStyle looks for various join angles.""" | ||
import numpy as np | ||
import matplotlib.pyplot as plt | ||
|
||
def plot_angle(ax, x, y, angle, style): | ||
phi = np.radians(angle) | ||
xx = [x + .5, x, x + .5*np.cos(phi)] | ||
yy = [y, y, y + .5*np.sin(phi)] | ||
ax.plot(xx, yy, lw=12, color='tab:blue', solid_joinstyle=style) | ||
ax.plot(xx, yy, lw=1, color='black') | ||
ax.plot(xx[1], yy[1], 'o', color='tab:red', markersize=3) | ||
|
||
fig, ax = plt.subplots(figsize=(5, 4), constrained_layout=True) | ||
ax.set_title('Join style') | ||
for x, style in enumerate(['miter', 'round', 'bevel']): | ||
ax.text(x, 5, style) | ||
for y, angle in enumerate([20, 45, 60, 90, 120]): | ||
plot_angle(ax, x, y, angle, style) | ||
if x == 0: | ||
ax.text(-1.3, y, f'{angle} degrees') | ||
ax.set_xlim(-1.5, 2.75) | ||
ax.set_ylim(-.5, 5.5) | ||
ax.set_axis_off() | ||
fig.show() | ||
|
||
|
||
JoinStyle.input_description = "{" \ | ||
+ ", ".join([f"'{js.name}'" for js in JoinStyle]) \ | ||
+ "}" | ||
|
||
|
||
class CapStyle(str, _AutoStringNameEnum): | ||
r""" | ||
Define how the two endpoints (caps) of an unclosed line are drawn. | ||
|
||
How to draw the start and end points of lines that represent a closed curve | ||
(i.e. that end in a `~.path.Path.CLOSEPOLY`) is controlled by the line's | ||
`JoinStyle`. For all other lines, how the start and end points are drawn is | ||
controlled by the *CapStyle*. | ||
|
||
For a visual impression of each *CapStyle*, `view these docs online | ||
<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._enums import CapStyle | ||
CapStyle.demo() | ||
|
||
""" | ||
butt = 'butt' | ||
projecting = 'projecting' | ||
round = 'round' | ||
|
||
def __init__(self, s): | ||
s = _deprecate_case_insensitive_join_cap(s) | ||
Enum.__init__(self) | ||
|
||
@staticmethod | ||
def demo(): | ||
"""Demonstrate how each CapStyle looks for a thick line segment.""" | ||
import matplotlib.pyplot as plt | ||
|
||
fig = plt.figure(figsize=(4, 1.2)) | ||
ax = fig.add_axes([0, 0, 1, 0.8]) | ||
ax.set_title('Cap style') | ||
|
||
for x, style in enumerate(['butt', 'round', 'projecting']): | ||
ax.text(x+0.25, 0.85, style, ha='center') | ||
xx = [x, x+0.5] | ||
yy = [0, 0] | ||
ax.plot(xx, yy, lw=12, color='tab:blue', solid_capstyle=style) | ||
ax.plot(xx, yy, lw=1, color='black') | ||
ax.plot(xx, yy, 'o', color='tab:red', markersize=3) | ||
ax.text(2.25, 0.55, '(default)', ha='center') | ||
|
||
ax.set_ylim(-.5, 1.5) | ||
ax.set_axis_off() | ||
fig.show() | ||
|
||
|
||
CapStyle.input_description = "{" \ | ||
+ ", ".join([f"'{cs.name}'" for cs in CapStyle]) \ | ||
+ "}" | ||
|
||
docstring.interpd.update({'JoinStyle': JoinStyle.input_description, | ||
'CapStyle': CapStyle.input_description}) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this example deleted? If you moved the docs to a class defn, fine, but leave the stub here with a link to the docs? Maybe w/ a small minimal example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't feel like it made sense to have the exact same example in two different places. My goal with these API docs was to be a strictly-better drop-in replacement for this example.
Is the concern here that the code that makes (what is now) the
JoinStyle.demo
figure is not as easily accessible in the docs anymore? I would propose that the example was not really adding much to the documentation via its code, but only really via its output (the visual representation of each Join/CapStyle is what's important).Is the concern discoverability? I know that we have issues with Google sending people to old versions of the docs, but presumably the hope is that the new page quickly gets indexed as the go-to place for JoinStyle. In which case the relevant example figure will still be there.
If the concern is that people looking for that specific example will be confused as to where it went, then my impression of what's the best way to leave a "stub" behind is, instead of leaving an actual stub page with a link, we should really just use something like the reredirects package to simply have an HTTP 301 response whenever someone tries to visit that page. As a side-effect this will also help with Google search indexing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like the redirects package looks very interesting, though version 0.0.0 is a negative.
But the other concern is that the example will no longer will show up in the example gallery...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha fair, didn't realize the version number was so sketch, but frankly I can white room that package in a couple hours if we wanted to have our own implementation.
Gitlab reports that the whole "package" is only 62 lines of Python code. Was mostly linking it as a proof-of-principle that this is not impossible to do inside of Sphinx.
This may be totally off-base, but I struggle to imagine the user that scrolls through the examples gallery and is enticed to click on the JoinStyle demo in particular. Unless you know what it is already? But people that already know the name for something typically find examples by getting directly linked in via Google...
If I'm outvoted on that point, I guess we would just add two stub examples, one which just calls
CapStyle.demo
and one that callsJoinStyle.demo
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you are browsing the examples, and come across it you learn something new. If you don't know what a join style is, you can never google it. People need some way to know about the features in the library, and the example pages are basically it.
If you just want to have it call and link the class demo, thats perhaps fine. Then the plot shows up, and the user is linked to complete docs. But I think leaving it in as an example in the gallery is useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding redirects is not the issue, but collating through the hundreds of old versions is a chore.
As for examples, I thought there was some talk of putting a smaller set of examples for all plot types, and maybe we could do the same for all these new style classes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ that would be great. However I don't think we should delete existing examples until that is created?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 for leaving stub examples and linking to the class. and later collecting these examples in a dedicated section.
(This is for discoverability rather than for keeping links alive).