Skip to content
Open
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
2 changes: 2 additions & 0 deletions doc/api/widgets_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ Widget classes
RangeSlider
TextBox
_SelectorWidget
_PolygonalSelector
RectangleSelector
EllipseSelector
Lasso
LassoSelector
PolygonSelector
PolylineSelector
SpanSelector
SubplotTool

Expand Down
51 changes: 51 additions & 0 deletions doc/release/next_whats_new/open_polygonal_chain.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
Introduce open polygonal chains in ``matplotlib.widgets``
---------------------------------------------------------

The widget class `.widgets.PolygonSelector` is used to select a
polygon region of an Axes. More precisely, it enables the selection
of a closed polygonal chain, a sequence of vertices connected by
line segments where the first and last vertices are connected.

.. plot::
:include-source: true
:alt: Closed polygonal chains using PolygonSelector.

import matplotlib.pyplot as plt
from matplotlib.widgets import PolygonSelector

_, ax = plt.subplots()
selector = PolygonSelector(ax)
selector.verts = [(0.1, 0.4), (0.5, 0.9), (0.3, 0.2)]

plt.show()

Support for open polygonal chains has been added through the new
`.widgets.PolylineSelector` class. A polyline is a sequence of
vertices connected by line segments, where the first and
last vertices are not connected, i.e., an open polygonal chain.

.. plot::
:include-source: true
:alt: Open polygonal chains using PolylineSelector.

import matplotlib.pyplot as plt
from matplotlib.widgets import PolylineSelector

_, ax = plt.subplots()
selector = PolylineSelector(ax)
selector.verts = [(0.1, 0.4), (0.5, 0.9), (0.3, 0.2)]

plt.show()

Both selectors share the same interactive editing capabilities,
including vertex repositioning and removal, as well as the ability
to define polygonal chains both programmatically and interactively.

The interactive selection of an open polygonal chain is completed by
pressing the *Enter* key after placing the final vertex.

Internally, the common functionality of polygonal chain selector
widgets has been extracted into the new private base class
`.widgets._PolygonalSelector`. `~.widgets.PolygonSelector` now
inherits from `~.widgets._PolygonalSelector` while preserving
its existing API and behavior.
105 changes: 105 additions & 0 deletions galleries/examples/widgets/polyline_selector_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""
=========================================================
Select four control points to define a cubic Bézier curve
=========================================================

Shows how one can use `.widgets.PolylineSelector` to create an interactive
cubic Bézier curve visualizer. Select four control points to define the curve.
"""

import matplotlib.patches as patches
from matplotlib.path import Path
from matplotlib.widgets import PolylineSelector

EMPTY_PATH = Path([(0, 0)], [Path.MOVETO])


class CubicBezierCurveVisualizer:
"""
Interactive cubic Bézier curve visualizer.

This tool allows you to define a cubic Bézier curve by selecting
four control points using `PolylineSelector`. The resulting curve
is displayed using `PathPatch` and updates as the selection changes,
provided that a valid set of control points is selected. Press the
'enter' key to complete the curve after selecting four control points.
Pressing 'esc' clears the current curve so a new one can be created.

Parameters
----------
ax : `~matplotlib.axes.Axes`
Axes to interact with.
props : dict, optional
Properties with which the polyline selector lines will be drawn.
handle_props : dict, optional
Properties for the control point handles.
"""
def __init__(self, ax, props=None, handle_props=None):
self.ax = ax
self.canvas = ax.figure.canvas
self.curve = patches.PathPatch(EMPTY_PATH, facecolor='none', lw=2)
self.ax.add_patch(self.curve)

if props is None:
props = dict(ls='--', c='gray')
if handle_props is None:
handle_props = dict(marker='o', ms=8, mfc='gray', mec='black')

self.selector = PolylineSelector(ax, self.onselect,
props=props, handle_props=handle_props)

self.cid = self.canvas.mpl_connect('key_press_event', self.on_key_press)

def onselect(self, verts):
if len(verts) != 4:
print("Select exactly 4 points for a cubic Bézier curve.")
self.clear()
return
path = Path(verts, [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4])
self.curve.set_path(path)
self.canvas.draw_idle()

def clear(self):
self.curve.set_path(EMPTY_PATH)
self.canvas.draw_idle()

def on_key_press(self, event):
if event.key == 'escape':
self.clear()

def disconnect(self):
self.selector.disconnect_events()
self.canvas.mpl_disconnect(self.cid)


if __name__ == '__main__':
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

ax.set(xlim=(0, 1), ylim=(0, 1))
ax.grid(alpha=0.5)

bezier_visualizer = CubicBezierCurveVisualizer(ax)

print("Select exactly 4 control points to define a cubic Bézier curve.")
print("Press the 'enter' key to complete the selection.")
print("Hold 'ctrl' to reposition a single point while polyline is incomplete.")
print("Hold 'shift' to move all control points.")
print("Left click and drag a point to reposition it.")
print("Press the 'esc' key to start a new curve.")

plt.show()

bezier_visualizer.disconnect()

# %%
#
# .. admonition:: References
#
# The use of the following functions, methods, classes and modules is shown
# in this example:
#
# - `matplotlib.widgets.PolylineSelector`
# - `matplotlib.path.Path`
# - `matplotlib.patches.PathPatch`
59 changes: 59 additions & 0 deletions galleries/examples/widgets/polyline_selector_simple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
=================
Polyline Selector
=================

Shows how to create a polyline programmatically or interactively
"""

import matplotlib.pyplot as plt

from matplotlib.widgets import PolylineSelector

# %%
#
# To create the polyline programmatically
fig, ax = plt.subplots()
fig.show()

selector = PolylineSelector(ax, lambda *args: None)

# Add three vertices
selector.verts = [(0.1, 0.4), (0.5, 0.9), (0.3, 0.2)]


# %%
#
# To create the polyline interactively

fig2, ax2 = plt.subplots()
fig2.show()

selector2 = PolylineSelector(ax2, lambda *args: None)

print("Click to add vertices sequentially.")
print("Press the 'enter' key to complete the polyline.")
print("Hold 'ctrl' to reposition a single vertex while polyline is incomplete.")
print("Hold 'shift' to move all vertices.")
print("Left click and drag a vertex to reposition it.")
print("Right click to remove a vertex.")
print("Press the 'esc' key to start a new polyline.")


# %%
# .. tags::
#
# component: axes,
# styling: position,
# plot-type: line,
# level: intermediate,
# domain: cartography,
# domain: geometry,
# domain: statistics,
#
# .. admonition:: References
#
# The use of the following functions, methods, classes and modules is shown
# in this example:
#
# - `matplotlib.widgets.PolylineSelector`
Loading
Loading