How do I change the way that matplotlib tiles custom hatching in a patch? I would like to tile the hatch in a grid pattern (like you would square tile) as opposed to the alternating row (like you would lay brick).
┌───┐┌───┐┌───┐
│ ││ ││ │
└───┘└───┘└───┘
┌───┐┌───┐┌───┐
│ ││ ││ │
└───┘└───┘└───┘
vs
┌───┐┌───┐┌───┐
│ ││ ││ │
└───┘└───┘└───┘
──┐┌───┐┌───┐┌─
││ ││ ││
──┘└───┘└───┘└─
Here's sample code that generates a simple custom hatch in matplotlib:
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.hatch
square_path = patches.Polygon(
[[-0.4, -0.4], [4.0, -0.4], [0.4, 0.4], [-0.4, 0.4]],
closed=True, fill=False).get_path()
class SquareHatch(matplotlib.hatch.Shapes):
"""Custom hatches defined by a path drawn inside [-0.5, 0.5] square.
Identifier 's'.
"""
filled = True
size = 1.0
path = square_path
def __init__(self, hatch, density):
"""Initialize the custom hatch."""
self.num_rows = int((hatch.count('s')) * density)
self.shape_vertices = self.path.vertices
self.shape_codes = self.path.codes
matplotlib.hatch.Shapes.__init__(self, hatch, density)
matplotlib.hatch._hatch_types.append(SquareHatch)
# Create a figure and axis
self.fig, self.ax = plt.subplots()
# Create a square patch with hatching
square = patches.Rectangle(xy=(0.25, 0.25), width=0.5, height=0.5, hatch='s', fill=False)
# Add the square patch to the axis
self.ax.add_patch(square)
# Display the plot
plt.show()
I've been searching and I would think this would be an option, but I'm not finding it. AI said it was possible but gave me an option that didn't work... I'd altar my tile, but I can't work out a way to make my pattern work with an offset 2nd row.
The issue comes from the set_vertices_and_codes
function in the Shapes superclass:
def set_vertices_and_codes(self, vertices, codes):
offset = 1.0 / self.num_rows
shape_vertices = self.shape_vertices * offset * self.size
shape_codes = self.shape_codes
if not self.filled:
shape_vertices = np.concatenate( # Forward, then backward.
[shape_vertices, shape_vertices[::-1] * 0.9])
shape_codes = np.concatenate([shape_codes, shape_codes])
vertices_parts = []
codes_parts = []
for row in range(self.num_rows + 1):
####### Offsetting occurs here #############
if row % 2 == 0:
cols = np.linspace(0, 1, self.num_rows + 1)
else:
cols = np.linspace(offset / 2, 1 - offset / 2, self.num_rows)
####### Offsetting occurs here #############
row_pos = row * offset
for col_pos in cols:
vertices_parts.append(shape_vertices + [col_pos, row_pos])
codes_parts.append(shape_codes)
np.concatenate(vertices_parts, out=vertices)
np.concatenate(codes_parts, out=codes)
Changing the cols to np.linspace(0, 1, self.num_rows+1)
regardless of row number removes the offset, but now the self.num_shapes
set earlier in the __init__
of the Shape class is off. Ultimately, I found it easier for the custom hatch to just copy-paste the Shape class with some minor edits to remove the offsetting.
class SquareHatch(matplotlib.hatch.HatchPatternBase):
"""Custom hatches defined by a path drawn inside [-0.5, 0.5] square.
Identifier 's'.
"""
filled = True
size = 1.0
path = square_path
def __init__(self, hatch, density):
"""Initialize the custom hatch."""
self.num_rows = int((hatch.count('s')) * density)
self.shape_vertices = self.path.vertices
self.shape_codes = self.path.codes
self.num_shapes = (self.num_rows+1)**2
self.num_vertices = (self.num_shapes *
len(self.shape_vertices) *
(1 if self.filled else 2))
def set_vertices_and_codes(self, vertices, codes):
offset = 1.0 / self.num_rows
shape_vertices = self.shape_vertices * offset * self.size
shape_codes = self.shape_codes
if not self.filled:
shape_vertices = np.concatenate( # Forward, then backward.
[shape_vertices, shape_vertices[::-1] * 0.9])
shape_codes = np.concatenate([shape_codes, shape_codes])
vertices_parts = []
codes_parts = []
for row in range(self.num_rows + 1):
cols = np.linspace(0, 1, self.num_rows + 1)
row_pos = row * offset
for col_pos in cols:
vertices_parts.append(shape_vertices + [col_pos, row_pos])
codes_parts.append(shape_codes)
np.concatenate(vertices_parts, out=vertices)
np.concatenate(codes_parts, out=codes)