-
-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Problem
Matplotlib’s current RectangleSelector
only supports axis-aligned rectangles. Many domains (image annotation, computer vision, OCR, remote sensing, microscopy, UI layout tools) need oriented/rotated bounding boxes with interactive resize/rotate/translate. Today, users hack around this with PolygonSelector
(4 points) or custom event handlers, which leads to inconsistent UX, no angle snapping, and a lot of duplicated code.
Proposed solution
Introduce a new widget: OrientedRectangleSelector
, an interactive selector that behaves like RectangleSelector
but supports arbitrary rotation. It provides:
- Corner and edge handles for resizing (with optional aspect-ratio lock).
- A rotation handle with optional angle snapping.
- Dragging the center to translate.
- Live callbacks during interaction and a final
onselect
on release. - Visual feedback (cursors, handles) and optional blitting for performance.
This mirrors the mental model of RectangleSelector
while adding rotation.
High-level API
from matplotlib.widgets import OrientedRectangleSelector
ors = OrientedRectangleSelector(
ax,
onselect=None, # called on mouse release with final params
onmove_callback=None, # called during interaction with live params
*,
useblit=False,
button=None,
minspanx=0, minspany=0,
spancoords="data",
maxdist=10, # handle hit test (px)
snap_angle=True,
angle_snap_increment=1.0, # degrees
maintain_aspect_ratio=False,
min_size=0.02, # in data units
initial_center=(0.5, 0.5),
initial_width=0.3,
initial_height=0.2,
initial_angle=0.0, # degrees
handle_props=None, # dict for corner/edge/rotate/center styles
line_props=None, # rectangle edge props
state_modifier_keys=None, # {'rotate': 'shift', 'aspect_ratio': 'control', 'snap': 'alt'}
)
Returned/Callback params
{
"center": np.ndarray([cx, cy]),
"width": float,
"height": float,
"angle": float, # degrees
"corners": np.ndarray(shape=(4,2)), # BL, BR, TR, TL in data coords
"area": float
}
Programmatic control
ors.set_rectangle(center=(x, y), width=w, height=h, angle=a)
params = ors.get_rectangle_params()
ors.update_properties({"snap_angle": False, "min_size": 0.05})
Example usage
fig, ax = plt.subplots()
ax.imshow(img, cmap="gray")
def onmove(params):
# live feedback (e.g., show crop/metrics)
pass
def ondone(params):
print(params["center"], params["width"], params["height"], params["angle"])
ors = OrientedRectangleSelector(ax, onselect=ondone, onmove_callback=onmove,
snap_angle=True, angle_snap_increment=1.0)
plt.show()
UX details
- Handles: 4 corners, 4 edge midpoints, 1 rotation handle, 1 center “+”.
- Cursors: move/resize/rotate cursors based on hover target.
- Snapping: configurable granularity (default 1°; can be 15° etc.).
- Constraints: configurable minimum size; easy to add “keep inside axes” later.

Implementation notes
A reference implementation is attached (ready to adapt to Matplotlib conventions):
- Uses a
Rectangle
patch drawn at the origin and transformed viaAffine2D
(rotate + translate) for numerical stability. - Hit-testing in data space; handle detection uses pixel tolerance.
- Optional blitting for smooth interaction.
- Clean separation of interaction modes: translate, rotate, resize-corner, resize-edge.
- Public methods mirror
RectangleSelector
where possible; extras are additive.
Backward compatibility
No breakage. New widget, opt-in. Naming follows RectangleSelector
. Consider adding an alias RotatedRectangleSelector
for searchability.
Performance
Blitting support keeps interaction smooth; complexity is similar to existing selectors. Handle count is fixed (small); transform math is minimal.
Testing
- Unit tests for:
- Local↔world transform correctness.
- Angle snapping correctness.
- Aspect-ratio lock.
- Min-size constraint.
- Image tests for handle placement and rotation rendering.
- Event simulation tests (press/motion/release) to validate callbacks.
Documentation
- User guide with interactive examples.
- API reference with detailed parameter descriptions.
- Changelog for version updates.
[I am ready to do that!]
Future extensions (non-blocking)
- Constrain rectangle within axes/view limits.
- Keyboard nudging & precise numeric entry.
- Multi-rectangle manager as a helper (selection, add/remove).
- Snapping to guide lines / other artists.
Note: I have more ideas for this widget and ready to take full ownership and I can implement them.