I need to draw rectangles with small amount of INNER padding (e.g. 2 px) so that they don’t come in contact with immediately adjacent rectangles. I would like the padding to remain constant after any resize (zoom).
The example below illustrates what i’ve tried so far, which is to use ax.transData.inverted()
to transform from pixels into data units, but this does not seem to give consistent results (i.e., the padding changes at different zoom levels).
<code>import matplotlib.pyplot as plt
import matplotlib.patches as patches
class Rectangle(patches.Rectangle):
def __init__(self, x, y, w, h, pad, **kwargs) -> None:
self._x, self._y, self._w, self._h, self._pad = x, y, w, h, pad
super().__init__((self._x, self._y), self._w, self._h, **kwargs)
def add_to_axes(self, ax) -> None:
ax.add_patch(self)
self.figure.canvas.mpl_connect("draw_event", self._on_draw)
self._ax = ax
# Keep track of current axes limits
self._xlim = ax.get_xlim()
self._ylim = ax.get_ylim()
self._update()
def _on_draw(self, event) -> None:
# Check if axes limits have changed
if self._xlim == self._ax.get_xlim() and self._ylim == self._ax.get_ylim():
return
self._update()
def _update(self) -> None:
# Update the padding
self._xlim, self._ylim = self._ax.get_xlim(), self._ax.get_ylim()
print("yes - i am a resize event")
# Calculate pixel-based padding
inv = self._ax.transData.inverted()
dx, dy = inv.transform((self._pad, self._pad)) - inv.transform((0, 0)) # <- the issue is somewhere here
print(dx)
print(dy)
# Update the rectangle's position and size
self.set_bounds((self._x + dx/2, self._y + dy/2, self._w - dx, self._h - dy))
# Example usage
fig, ax = plt.subplots()
# List of rectangles defined as (x, y, w, h)
rectangles = [
Rectangle(0, 0, 2, 3, 10),
Rectangle(2, 0, 2, 3, 10),
Rectangle(4, 0, 2, 3, 10),
Rectangle(0, 3, 6, 2, 10)
]
# Add each rectangle to the axes
for rect in rectangles:
rect.add_to_axes(ax)
# Set limits and show plot
ax.set_xlim(-1, 7)
ax.set_ylim(-1, 6)
ax.set_aspect("equal")
plt.show()
</code>
<code>import matplotlib.pyplot as plt
import matplotlib.patches as patches
class Rectangle(patches.Rectangle):
def __init__(self, x, y, w, h, pad, **kwargs) -> None:
self._x, self._y, self._w, self._h, self._pad = x, y, w, h, pad
super().__init__((self._x, self._y), self._w, self._h, **kwargs)
def add_to_axes(self, ax) -> None:
ax.add_patch(self)
self.figure.canvas.mpl_connect("draw_event", self._on_draw)
self._ax = ax
# Keep track of current axes limits
self._xlim = ax.get_xlim()
self._ylim = ax.get_ylim()
self._update()
def _on_draw(self, event) -> None:
# Check if axes limits have changed
if self._xlim == self._ax.get_xlim() and self._ylim == self._ax.get_ylim():
return
self._update()
def _update(self) -> None:
# Update the padding
self._xlim, self._ylim = self._ax.get_xlim(), self._ax.get_ylim()
print("yes - i am a resize event")
# Calculate pixel-based padding
inv = self._ax.transData.inverted()
dx, dy = inv.transform((self._pad, self._pad)) - inv.transform((0, 0)) # <- the issue is somewhere here
print(dx)
print(dy)
# Update the rectangle's position and size
self.set_bounds((self._x + dx/2, self._y + dy/2, self._w - dx, self._h - dy))
# Example usage
fig, ax = plt.subplots()
# List of rectangles defined as (x, y, w, h)
rectangles = [
Rectangle(0, 0, 2, 3, 10),
Rectangle(2, 0, 2, 3, 10),
Rectangle(4, 0, 2, 3, 10),
Rectangle(0, 3, 6, 2, 10)
]
# Add each rectangle to the axes
for rect in rectangles:
rect.add_to_axes(ax)
# Set limits and show plot
ax.set_xlim(-1, 7)
ax.set_ylim(-1, 6)
ax.set_aspect("equal")
plt.show()
</code>
import matplotlib.pyplot as plt
import matplotlib.patches as patches
class Rectangle(patches.Rectangle):
def __init__(self, x, y, w, h, pad, **kwargs) -> None:
self._x, self._y, self._w, self._h, self._pad = x, y, w, h, pad
super().__init__((self._x, self._y), self._w, self._h, **kwargs)
def add_to_axes(self, ax) -> None:
ax.add_patch(self)
self.figure.canvas.mpl_connect("draw_event", self._on_draw)
self._ax = ax
# Keep track of current axes limits
self._xlim = ax.get_xlim()
self._ylim = ax.get_ylim()
self._update()
def _on_draw(self, event) -> None:
# Check if axes limits have changed
if self._xlim == self._ax.get_xlim() and self._ylim == self._ax.get_ylim():
return
self._update()
def _update(self) -> None:
# Update the padding
self._xlim, self._ylim = self._ax.get_xlim(), self._ax.get_ylim()
print("yes - i am a resize event")
# Calculate pixel-based padding
inv = self._ax.transData.inverted()
dx, dy = inv.transform((self._pad, self._pad)) - inv.transform((0, 0)) # <- the issue is somewhere here
print(dx)
print(dy)
# Update the rectangle's position and size
self.set_bounds((self._x + dx/2, self._y + dy/2, self._w - dx, self._h - dy))
# Example usage
fig, ax = plt.subplots()
# List of rectangles defined as (x, y, w, h)
rectangles = [
Rectangle(0, 0, 2, 3, 10),
Rectangle(2, 0, 2, 3, 10),
Rectangle(4, 0, 2, 3, 10),
Rectangle(0, 3, 6, 2, 10)
]
# Add each rectangle to the axes
for rect in rectangles:
rect.add_to_axes(ax)
# Set limits and show plot
ax.set_xlim(-1, 7)
ax.set_ylim(-1, 6)
ax.set_aspect("equal")
plt.show()