Skip to content

Add configuration of Shadow and pie shadow #25389

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 1 commit into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions doc/users/next_whats_new/pie_shadow_control.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The pie chart shadow can be controlled
--------------------------------------

The *shadow* argument to `~.Axes.pie` can now be a dict, allowing more control
of the `.Shadow`-patch used.
6 changes: 6 additions & 0 deletions doc/users/next_whats_new/shadow_shade.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Shadow shade can be controlled
------------------------------

The `.Shadow` patch now has a *shade* argument to control the shadow darkness.
If 1, the shadow is black, if 0, the shadow has the same color as the patch that
is shadowed. The default value, which earlier was fixed, is 0.7.
55 changes: 0 additions & 55 deletions galleries/examples/pie_and_polar_charts/pie_demo2.py

This file was deleted.

29 changes: 28 additions & 1 deletion galleries/examples/pie_and_polar_charts/pie_features.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""
.. redirect-from:: gallery/pie_and_polar_charts/pie_demo2

==========
Pie charts
==========
Expand Down Expand Up @@ -88,7 +90,7 @@

fig, ax = plt.subplots()
ax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
shadow=True, startangle=90)
shadow=True, startangle=90)
plt.show()

# %%
Expand All @@ -97,6 +99,31 @@
# slices are rotated counter-clockwise by 90 degrees, and the frog slice starts
# on the positive y-axis.
#
# Controlling the size
# --------------------
#
# By changing the *radius* parameter, and often the text size for better visual
# appearance, the pie chart can be scaled.

fig, ax = plt.subplots()

ax.pie(sizes, labels=labels, autopct='%.0f%%',
textprops={'size': 'smaller'}, radius=0.5)
plt.show()

# %%
# Modifying the shadow
# --------------------
#
# The *shadow* parameter may optionally take a dictionary with arguments to
# the `.Shadow` patch. This can be used to modify the default shadow.

fig, ax = plt.subplots()
ax.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
shadow={'ox': -0.04, 'edgecolor': 'none', 'shade': 0.9}, startangle=90)
plt.show()

# %%
# .. admonition:: References
#
# The use of the following functions, methods, classes and modules is shown
Expand Down
14 changes: 10 additions & 4 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3102,8 +3102,12 @@ def pie(self, x, explode=None, labels=None, colors=None,
If set to ``None``, labels are not drawn but are still stored for
use in `.legend`.

shadow : bool, default: False
Draw a shadow beneath the pie.
shadow : bool or dict, default: False
If bool, whether to draw a shadow beneath the pie. If dict, draw a shadow
passing the properties in the dict to `.Shadow`.

.. versionadded:: 3.8
*shadow* can be a dict.

startangle : float, default: 0 degrees
The angle by which the start of the pie is rotated,
Expand Down Expand Up @@ -3231,8 +3235,10 @@ def get_next_color():
if shadow:
# Make sure to add a shadow after the call to add_patch so the
# figure and transform props will be set.
shad = mpatches.Shadow(w, -0.02, -0.02, label='_nolegend_')
self.add_patch(shad)
shadow_dict = {'ox': -0.02, 'oy': -0.02, 'label': '_nolegend_'}
if isinstance(shadow, dict):
shadow_dict.update(shadow)
self.add_patch(mpatches.Shadow(w, **shadow_dict))

if labeldistance is not None:
xt = x + labeldistance * radius * math.cos(thetam)
Expand Down
14 changes: 11 additions & 3 deletions lib/matplotlib/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,12 +613,12 @@ def __str__(self):
return f"Shadow({self.patch})"

@_docstring.dedent_interpd
def __init__(self, patch, ox, oy, **kwargs):
def __init__(self, patch, ox, oy, *, shade=0.7, **kwargs):
"""
Create a shadow of the given *patch*.

By default, the shadow will have the same face color as the *patch*,
but darkened.
but darkened. The darkness can be controlled by *shade*.

Parameters
----------
Expand All @@ -627,6 +627,12 @@ def __init__(self, patch, ox, oy, **kwargs):
ox, oy : float
The shift of the shadow in data coordinates, scaled by a factor
of dpi/72.
shade : float, default: 0.7
How the darkness of the shadow relates to the original color. If 1, the
shadow is black, if 0, the shadow has the same color as the *patch*.

.. versionadded:: 3.8

**kwargs
Properties of the shadow patch. Supported keys are:

Expand All @@ -638,7 +644,9 @@ def __init__(self, patch, ox, oy, **kwargs):
self._shadow_transform = transforms.Affine2D()

self.update_from(self.patch)
color = .3 * np.asarray(colors.to_rgb(self.patch.get_facecolor()))
if not 0 <= shade <= 1:
raise ValueError("shade must be between 0 and 1.")
color = (1 - shade) * np.asarray(colors.to_rgb(self.patch.get_facecolor()))
self.update({'facecolor': color, 'edgecolor': color, 'alpha': 0.5,
# Place shadow patch directly behind the inherited patch.
'zorder': np.nextafter(self.patch.zorder, -np.inf),
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5697,6 +5697,30 @@ def test_pie_nolabel_but_legend():
plt.legend()


@image_comparison(['pie_shadow.png'], style='mpl20')
def test_pie_shadow():
# Also acts as a test for the shade argument of Shadow
sizes = [15, 30, 45, 10]
colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral']
explode = (0, 0.1, 0, 0) # only "explode" the 2nd slice
_, axes = plt.subplots(2, 2)
axes[0][0].pie(sizes, explode=explode, colors=colors,
shadow=True, startangle=90,
wedgeprops={'linewidth': 0})

axes[0][1].pie(sizes, explode=explode, colors=colors,
shadow=False, startangle=90,
wedgeprops={'linewidth': 0})

axes[1][0].pie(sizes, explode=explode, colors=colors,
shadow={'ox': -0.05, 'oy': -0.05, 'shade': 0.9, 'edgecolor': 'none'},
startangle=90, wedgeprops={'linewidth': 0})

axes[1][1].pie(sizes, explode=explode, colors=colors,
shadow={'ox': 0.05, 'linewidth': 2, 'shade': 0.2},
startangle=90, wedgeprops={'linewidth': 0})


def test_pie_textprops():
data = [23, 34, 45]
labels = ["Long name 1", "Long name 2", "Long name 3"]
Expand Down
4 changes: 3 additions & 1 deletion lib/matplotlib/tests/test_legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,10 @@ def test_alpha_rcparam():
leg.legendPatch.set_facecolor([1, 0, 0, 0.5])


@image_comparison(['fancy'], remove_text=True)
@image_comparison(['fancy'], remove_text=True, tol=0.05)
def test_fancy():
# Tolerance caused by changing default shadow "shade" from 0.3 to 1 - 0.7 =
# 0.30000000000000004
# using subplot triggers some offsetbox functionality untested elsewhere
plt.subplot(121)
plt.plot([5] * 10, 'o--', label='XX')
Expand Down