Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add hatchcolor parameter for Collections#29044

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
timhoffm merged 16 commits intomatplotlib:mainfromImpaler343:collections-hatchcolor
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
16 commits
Select commitHold shift + click to select a range
d7da1fc
add hatchcolors param for collections
r3ksteJan 3, 2025
2fd17a0
minor fixes
r3ksteJan 3, 2025
f5978ce
added gallery example
r3ksteJan 8, 2025
5377127
documented hatchcolor parameter for collections in next whats new entry
r3ksteJan 20, 2025
6f18c49
update whats new for hatchcolor in collections
r3ksteJan 27, 2025
8a7cd65
update contourf hatch test with cmap.with_alpha()
r3ksteJan 29, 2025
e3b03fc
grammar nits
Impaler343Feb 5, 2025
7668535
enhanced tests and made hatchcolors param required in _iter_collection()
r3ksteFeb 5, 2025
9d0ec23
smoke test for hatchcolors coercion
r3ksteFeb 8, 2025
7e46707
pass hatchcolor arg only if it is supported by the renderer. and made…
r3ksteFeb 16, 2025
c5e9583
Fix hatchcolors argument support in third-party backends
r3ksteFeb 22, 2025
d2b6268
Add note about provisional API for draw_path_collection()
r3ksteFeb 23, 2025
fee895b
Refactor draw_path_collection to make `hatchcolors` a keyword-only ar…
r3ksteMar 16, 2025
b9ac0fb
Made suggested changes
r3ksteMar 22, 2025
2e4784b
added test and improve comments for third party backend support of ha…
r3ksteMar 28, 2025
3b43bc6
skip unnecessary path iteration when hatchcolors is not passed/needed
r3ksteMar 29, 2025
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
36 changes: 36 additions & 0 deletionsdoc/users/next_whats_new/separated_hatchcolor.rst
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -57,3 +57,39 @@ Previously, hatch colors were the same as edge colors, with a fallback to
xy=(.5, 1.03), xycoords=patch4, ha='center', va='bottom')

plt.show()

For collections, a sequence of colors can be passed to the *hatchcolor* parameter
which will be cycled through for each hatch, similar to *facecolor* and *edgecolor*.

Previously, if *edgecolor* was not specified, the hatch color would fall back to
:rc:`patch.edgecolor`, but the alpha value would default to **1.0**, regardless of the
alpha value of the collection. This behavior has been changed such that, if both
*hatchcolor* and *edgecolor* are not specified, the hatch color will fall back
to 'patch.edgecolor' with the alpha value of the collection.

.. plot::
:include-source: true
:alt: A random scatter plot with hatches on the markers. The hatches are colored in blue, orange, and green, respectively. After the first three markers, the colors are cycled through again.

import matplotlib.pyplot as plt
import numpy as np

np.random.seed(19680801)

fig, ax = plt.subplots()

x = [29, 36, 41, 25, 32, 70, 62, 58, 66, 80, 58, 68, 62, 37, 48]
y = [82, 76, 48, 53, 62, 70, 84, 68, 55, 75, 29, 25, 12, 17, 20]
colors = ['tab:blue'] * 5 + ['tab:orange'] * 5 + ['tab:green'] * 5

ax.scatter(
x,
y,
s=800,
hatch="xxxx",
hatchcolor=colors,
facecolor="none",
edgecolor="black",
)

plt.show()
60 changes: 55 additions & 5 deletionsgalleries/examples/shapes_and_collections/hatchcolor_demo.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
"""
================
Patch hatchcolor
================
===============
Hatchcolor Demo
===============

The color of the hatch can be set using the *hatchcolor* parameter. The following
examples show how to use the *hatchcolor* parameter to set the color of the hatch
in `~.patches.Patch` and `~.collections.Collection`.

See also :doc:`/gallery/shapes_and_collections/hatch_demo` for more usage examples
of hatching.

Patch Hatchcolor
----------------

This example shows how to use the *hatchcolor* parameter to set the color of
the hatch. The *hatchcolor* parameter is available for `~.patches.Patch`,
child classes of Patch, and methods that pass through to Patch.
the hatch in a rectangle and a bar plot. The *hatchcolor* parameter is available for
`~.patches.Patch`,child classes of Patch, and methods that pass through to Patch.
"""

import matplotlib.pyplot as plt
import numpy as np

import matplotlib.cm as cm
from matplotlib.patches import Rectangle

fig, (ax1, ax2) = plt.subplots(1, 2)
Expand All@@ -28,6 +39,43 @@
ax2.set_xlim(0, 5)
ax2.set_ylim(0, 5)

# %%
# Collection Hatchcolor
# ---------------------
#
# The following example shows how to use the *hatchcolor* parameter to set the color of
# the hatch in a scatter plot. The *hatchcolor* parameter can also be passed to
# `~.collections.Collection`, child classes of Collection, and methods that pass
# through to Collection.

fig, ax = plt.subplots()

num_points_x = 10
num_points_y = 9
x = np.linspace(0, 1, num_points_x)
y = np.linspace(0, 1, num_points_y)

X, Y = np.meshgrid(x, y)
X[1::2, :] += (x[1] - x[0]) / 2 # stagger every alternate row

# As ax.scatter (PathCollection) is drawn row by row, setting hatchcolors to the
# first row is enough, as the colors will be cycled through for the next rows.
colors = [cm.rainbow(val) for val in x]

ax.scatter(
X.ravel(),
Y.ravel(),
s=1700,
facecolor="none",
edgecolor="gray",
linewidth=2,
marker="h", # Use hexagon as marker
hatch="xxx",
hatchcolor=colors,
)
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)

plt.show()

# %%
Expand All@@ -41,3 +89,5 @@
# - `matplotlib.patches.Polygon`
# - `matplotlib.axes.Axes.add_patch`
# - `matplotlib.axes.Axes.bar` / `matplotlib.pyplot.bar`
# - `matplotlib.collections`
# - `matplotlib.axes.Axes.scatter` / `matplotlib.pyplot.scatter`
33 changes: 23 additions & 10 deletionslib/matplotlib/backend_bases.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -208,7 +208,7 @@
def draw_path_collection(self, gc, master_transform, paths, all_transforms,
offsets, offset_trans, facecolors, edgecolors,
linewidths, linestyles, antialiaseds, urls,
offset_position):
offset_position, *, hatchcolors=None):
"""
Draw a collection of *paths*.

Expand All@@ -217,8 +217,11 @@
*master_transform*. They are then translated by the corresponding
entry in *offsets*, which has been first transformed by *offset_trans*.

*facecolors*, *edgecolors*, *linewidths*, *linestyles*, and
*antialiased* are lists that set the corresponding properties.
*facecolors*, *edgecolors*, *linewidths*, *linestyles*, *antialiased*
and *hatchcolors* are lists that set the corresponding properties.

.. versionadded:: 3.11
Allow *hatchcolors* to be specified.

*offset_position* is unused now, but the argument is kept for
backwards compatibility.
Expand All@@ -235,10 +238,13 @@
path_ids = self._iter_collection_raw_paths(master_transform,
paths, all_transforms)

if hatchcolors is None:
hatchcolors = []

Check warning on line 242 in lib/matplotlib/backend_bases.py

View check run for this annotation

Codecov/ codecov/patch

lib/matplotlib/backend_bases.py#L242

Added line #L242 was not covered by tests

for xo, yo, path_id, gc0, rgbFace in self._iter_collection(
gc, list(path_ids), offsets, offset_trans,
facecolors, edgecolors, linewidths, linestyles,
antialiaseds, urls, offset_position):
antialiaseds, urls, offset_position, hatchcolors=hatchcolors):
path, transform = path_id
# Only apply another translation if we have an offset, else we
# reuse the initial transform.
Expand All@@ -252,7 +258,7 @@

def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight,
coordinates, offsets, offsetTrans, facecolors,
antialiased, edgecolors):
antialiased, edgecolors, *, hatchcolors=None):
"""
Draw a quadmesh.

Expand All@@ -265,11 +271,14 @@

if edgecolors is None:
edgecolors = facecolors
if hatchcolors is None:
hatchcolors = []

Check warning on line 275 in lib/matplotlib/backend_bases.py

View check run for this annotation

Codecov/ codecov/patch

lib/matplotlib/backend_bases.py#L275

Added line #L275 was not covered by tests
linewidths = np.array([gc.get_linewidth()], float)

return self.draw_path_collection(
gc, master_transform, paths, [], offsets, offsetTrans, facecolors,
edgecolors, linewidths, [], [antialiased], [None], 'screen')
edgecolors, linewidths, [], [antialiased], [None], 'screen',
hatchcolors=hatchcolors)

def draw_gouraud_triangles(self, gc, triangles_array, colors_array,
transform):
Expand DownExpand Up@@ -337,7 +346,7 @@

def _iter_collection(self, gc, path_ids, offsets, offset_trans, facecolors,
edgecolors, linewidths, linestyles,
antialiaseds, urls, offset_position):
antialiaseds, urls, offset_position, *, hatchcolors):
"""
Helper method (along with `_iter_collection_raw_paths`) to implement
`draw_path_collection` in a memory-efficient manner.
Expand DownExpand Up@@ -365,11 +374,12 @@
N = max(Npaths, Noffsets)
Nfacecolors = len(facecolors)
Nedgecolors = len(edgecolors)
Nhatchcolors = len(hatchcolors)
Nlinewidths = len(linewidths)
Nlinestyles = len(linestyles)
Nurls = len(urls)

if (Nfacecolors == 0 and Nedgecolors == 0) or Npaths == 0:
if (Nfacecolors == 0 and Nedgecolors == 0 and Nhatchcolors == 0) or Npaths == 0:
return

gc0 = self.new_gc()
Expand All@@ -384,6 +394,7 @@
toffsets = cycle_or_default(offset_trans.transform(offsets), (0, 0))
fcs = cycle_or_default(facecolors)
ecs = cycle_or_default(edgecolors)
hcs = cycle_or_default(hatchcolors)
lws = cycle_or_default(linewidths)
lss = cycle_or_default(linestyles)
aas = cycle_or_default(antialiaseds)
Expand All@@ -392,8 +403,8 @@
if Nedgecolors == 0:
gc0.set_linewidth(0.0)

for pathid, (xo, yo), fc, ec, lw, ls, aa, url in itertools.islice(
zip(pathids, toffsets, fcs, ecs, lws, lss, aas, urls), N):
for pathid, (xo, yo), fc, ec,hc,lw, ls, aa, url in itertools.islice(
zip(pathids, toffsets, fcs, ecs,hcs,lws, lss, aas, urls), N):
if not (np.isfinite(xo) and np.isfinite(yo)):
continue
if Nedgecolors:
Expand All@@ -405,6 +416,8 @@
gc0.set_linewidth(0)
else:
gc0.set_foreground(ec)
if Nhatchcolors:
gc0.set_hatch_color(hc)
if fc is not None and len(fc) == 4 and fc[3] == 0:
fc = None
gc0.set_antialiased(aa)
Expand Down
4 changes: 4 additions & 0 deletionslib/matplotlib/backend_bases.pyi
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -63,6 +63,8 @@ class RendererBase:
antialiaseds: bool | Sequence[bool],
urls: str | Sequence[str],
offset_position: Any,
*,
hatchcolors: ColorType | Sequence[ColorType] | None = None,
) -> None: ...
def draw_quad_mesh(
self,
Expand All@@ -76,6 +78,8 @@ class RendererBase:
facecolors: Sequence[ColorType],
antialiased: bool,
edgecolors: Sequence[ColorType] | ColorType | None,
*,
hatchcolors: Sequence[ColorType] | ColorType | None = None,
) -> None: ...
def draw_gouraud_triangles(
self,
Expand Down
14 changes: 10 additions & 4 deletionslib/matplotlib/backends/backend_pdf.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2030,14 +2030,17 @@ def draw_path(self, gc, path, transform, rgbFace=None):
def draw_path_collection(self, gc, master_transform, paths, all_transforms,
offsets, offset_trans, facecolors, edgecolors,
linewidths, linestyles, antialiaseds, urls,
offset_position):
offset_position, *, hatchcolors=None):
# We can only reuse the objects if the presence of fill and
# stroke (and the amount of alpha for each) is the same for
# all of them
can_do_optimization = True
facecolors = np.asarray(facecolors)
edgecolors = np.asarray(edgecolors)

if hatchcolors is None:
hatchcolors = []

if not len(facecolors):
filled = False
can_do_optimization = not gc.get_hatch()
Expand DownExpand Up@@ -2072,7 +2075,7 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
self, gc, master_transform, paths, all_transforms,
offsets, offset_trans, facecolors, edgecolors,
linewidths, linestyles, antialiaseds, urls,
offset_position)
offset_position, hatchcolors=hatchcolors)

padding = np.max(linewidths)
path_codes = []
Expand All@@ -2088,7 +2091,7 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
for xo, yo, path_id, gc0, rgbFace in self._iter_collection(
gc, path_codes, offsets, offset_trans,
facecolors, edgecolors, linewidths, linestyles,
antialiaseds, urls, offset_position):
antialiaseds, urls, offset_position, hatchcolors=hatchcolors):

self.check_gc(gc0, rgbFace)
dx, dy = xo - lastx, yo - lasty
Expand DownExpand Up@@ -2603,7 +2606,10 @@ def delta(self, other):
different = ours is not theirs
else:
different = bool(ours != theirs)
except ValueError:
except (ValueError, DeprecationWarning):
# numpy version < 1.25 raises DeprecationWarning when array shapes
# mismatch, unlike numpy >= 1.25 which raises ValueError.
# This should be removed when numpy < 1.25 is no longer supported.
ours = np.asarray(ours)
theirs = np.asarray(theirs)
different = (ours.shape != theirs.shape or
Expand Down
8 changes: 5 additions & 3 deletionslib/matplotlib/backends/backend_ps.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -674,7 +674,9 @@ def draw_markers(
def draw_path_collection(self, gc, master_transform, paths, all_transforms,
offsets, offset_trans, facecolors, edgecolors,
linewidths, linestyles, antialiaseds, urls,
offset_position):
offset_position, *, hatchcolors=None):
if hatchcolors is None:
hatchcolors = []
# Is the optimization worth it? Rough calculation:
# cost of emitting a path in-line is
# (len_path + 2) * uses_per_path
Expand All@@ -690,7 +692,7 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
self, gc, master_transform, paths, all_transforms,
offsets, offset_trans, facecolors, edgecolors,
linewidths, linestyles, antialiaseds, urls,
offset_position)
offset_position, hatchcolors=hatchcolors)

path_codes = []
for i, (path, transform) in enumerate(self._iter_collection_raw_paths(
Expand All@@ -709,7 +711,7 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
for xo, yo, path_id, gc0, rgbFace in self._iter_collection(
gc, path_codes, offsets, offset_trans,
facecolors, edgecolors, linewidths, linestyles,
antialiaseds, urls, offset_position):
antialiaseds, urls, offset_position, hatchcolors=hatchcolors):
ps = f"{xo:g} {yo:g} {path_id}"
self._draw_ps(ps, gc0, rgbFace)

Expand Down
8 changes: 5 additions & 3 deletionslib/matplotlib/backends/backend_svg.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -736,7 +736,9 @@ def draw_markers(
def draw_path_collection(self, gc, master_transform, paths, all_transforms,
offsets, offset_trans, facecolors, edgecolors,
linewidths, linestyles, antialiaseds, urls,
offset_position):
offset_position, *, hatchcolors=None):
if hatchcolors is None:
hatchcolors = []
# Is the optimization worth it? Rough calculation:
# cost of emitting a path in-line is
# (len_path + 5) * uses_per_path
Expand All@@ -752,7 +754,7 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
gc, master_transform, paths, all_transforms,
offsets, offset_trans, facecolors, edgecolors,
linewidths, linestyles, antialiaseds, urls,
offset_position)
offset_position, hatchcolors=hatchcolors)

writer = self.writer
path_codes = []
Expand All@@ -770,7 +772,7 @@ def draw_path_collection(self, gc, master_transform, paths, all_transforms,
for xo, yo, path_id, gc0, rgbFace in self._iter_collection(
gc, path_codes, offsets, offset_trans,
facecolors, edgecolors, linewidths, linestyles,
antialiaseds, urls, offset_position):
antialiaseds, urls, offset_position, hatchcolors=hatchcolors):
url = gc0.get_url()
if url is not None:
writer.start('a', attrib={'xlink:href': url})
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp