Skip to content

ENH: Support open polygonal chains in widgets#31623

Open
lucaznch wants to merge 6 commits into
matplotlib:mainfrom
lucaznch:open-polygonal-chain
Open

ENH: Support open polygonal chains in widgets#31623
lucaznch wants to merge 6 commits into
matplotlib:mainfrom
lucaznch:open-polygonal-chain

Conversation

@lucaznch
Copy link
Copy Markdown
Contributor

@lucaznch lucaznch commented May 6, 2026

PR summary

PolygonSelector is a widget class used to select a polygonal region within an Axes. More precisely, it enables the selection of a closed polygonal chain, i.e., a sequence of vertices connected by line segments where the first and last vertices are connected. Before or after a polygon is defined, the class provides interactive editing capabilities such as repositioning and removing vertices.

An open polygonal chain is a sequence of vertices connected by line segments but where the first and last vertices are not connected. The proposed feature is to enable both programmatic and interactive selection of open polygonal chains within a widget class.

The approach is to introduce a new class, PolylineSelector, dedicated to open polygonal chains. Selection of such polygonal chains is completed by pressing the Enter key after inserting a vertex, and the existing interactive editing functionality is preserved. Common logic for polygonal chain selectors has been consolidated into a new private base class, _PolygonalSelector, which now serves as the base class for both PolygonSelector and PolylineSelector.

Why is this change necessary / what problem does it solve?

PolygonSelector currently supports only closed polygonal regions, while LassoSelector provides freeform selection without vertex control. There is no widget that supports structured, vertex-based selection of open polygonal chains, which are useful for tasks such as defining paths, annotations, or piecewise geometries where closure is not desired.

What is the reasoning for this implementation?

Several implementation approaches were considered:

  1. Introduce an OpenPolygonalChainSelector class dedicated to open chains.

  2. Introduce a PolygonalChainSelector class supporting both open and closed chains.

  3. Introduce an internal _PolygonalChainSelector base class to provide common functionality for the existing PolygonSelector and a new OpenPolygonalChainSelector.

  4. Extend the current PolygonSelector class by adding the closed parameter to explicitly control whether the polygonal chain is open or closed.

Other ideas were also considered:

  1. For a class supporting both chain types, infer whether the chain is open or closed based on user interaction, instead of relying on a new explicit parameter.

  2. Use the Strategy behavioral design pattern in the current implementation to remove the conditional branches (if self.closed ... else ...) added to handle open and closed chain behavior.

The chosen approach provides clearer semantics, while also making the shared and specialized behavior easier to maintain.

Example

output1

Co-authored-by: @maf2310

Closes #28421

AI Disclosure

AI was used to help proofread the grammar of this PR description and to understand some parts of the existing codebase during development. The code changes and design decisions were developed independently.

PR checklist

Extend PolygonSelector with open polygonal chain support
by adding a new parameter to explicitly control
whether the chain is open or closed.

The selection of an open polygonal chain is completed
by pressing the Enter key after inserting a vertex.

The existing interactive editing functionality
also applies to open polygonal chains.

Extend existing tests to cover the new mode.

Closes matplotlib#28421

Co-authored-by: Mafalda Botelho <mafaldabotelho@tecnico.ulisboa.pt>
@lucaznch
Copy link
Copy Markdown
Contributor Author

lucaznch commented May 6, 2026

Opening this PR as a draft since it's still in progress. The core implementation is in place but it still needs more tests and documentation, and possibly some small code improvements. And it is also meant to allow early feedback on the chosen approach and related aspects.

@timhoffm
Copy link
Copy Markdown
Member

timhoffm commented May 7, 2026

I didn't look at the code yet, but the extension conceptually makes sense, and the implementation via a closed parameter seems reasonable. 👍

@maf2310
Copy link
Copy Markdown
Contributor

maf2310 commented May 7, 2026

Thanks for the feedback!

When starting the implementation we were actually leaning towards the third approach mentioned

Introduce an internal _PolygonalChainSelector base class to provide common functionality for the existing PolygonSelector and a new OpenPolygonalChainSelector.

Although it would make the PR a bit more complex, we believe it would be worthwhile since it would centralize the shared logic and provide a cleaner architecture with clearer semantics for both cases. We also believe the level of difficulty would still be appropriate for us.

We ended up going with the current approach because it seemed like the simplest and least invasive starting point, but we are very interested in hearing thoughts about this alternative approach as well.

Extend existing PolygonSelector tests to also cover the
open polygonal chain mode. Since closed and open chains
share most interactive behavior, this verifies that the
open polygonal chain mode correctly implements the same
interactive behavior as the closed mode where applicable.

Add a dedicated test for redraw behavior
specific to open polygonal chains.

Co-authored-by: Mafalda Botelho <mafaldabotelho@tecnico.ulisboa.pt>
@github-actions github-actions Bot added the Documentation: examples files in galleries/examples label May 16, 2026
Document the new open polygonal chain mode in PolygonSelector.

Co-authored-by: Mafalda Botelho <mafaldabotelho@tecnico.ulisboa.pt>
@lucaznch lucaznch force-pushed the open-polygonal-chain branch from 101676a to 7b7d06c Compare May 16, 2026 21:48
@lucaznch
Copy link
Copy Markdown
Contributor Author

More tests have been added along with new documentation. A small note on the documentation is that we intentionally kept changes to the API docstring minimal to avoid potential semantic confusion and to keep the focus on the primary polygon selection behavior.

The PR is now ready for review.

@lucaznch lucaznch marked this pull request as ready for review May 16, 2026 22:24
@lucaznch
Copy link
Copy Markdown
Contributor Author

lucaznch commented May 16, 2026

And as an additional note, I also explored the alternative approach

Introduce an internal _PolygonalChainSelector base class to provide common functionality for the existing PolygonSelector and a new OpenPolygonalChainSelector.

implementing a working prototype (main...lucaznch:matplotlib:polyline). It follows a template-method-like pattern, refactoring the current PolygonSelector(..., closed) into a shared _PolygonalChainSelector base class.

From my point of view this approach has advantages. It provides a clearar separation of semantics between closed polygons and open polygonal chains, while also isolating shared and specialized functionality more explicitly, making it easier to extend I believe.

For example, the bounding box functionality is currently specific to closed polygons. If one wanted to add similar functionality for open polygonal chains (e.g., a "bounding" curve), it would be easier to incorporate in this structure.

That said, I am unsure whether the additional abstraction is beneficial in practice, or whether it perhaps introduces a bit more complexity.

lucaznch and others added 3 commits May 26, 2026 12:05
PolygonSelector had been extended to support open polygonal chains via
the `closed` parameter, controlling whether the chain is closed or open.

This commit refactors the extended implementation by extracting the
shared functionality of polygonal chain selector widgets into a new
`_PolygonalSelector` base class.

PolygonSelector is updated to inherit from `_PolygonalSelector` and
to handle only closed polygonal chains, restoring its original
semantics and behavior.

No functional changes are intended by this refactor. Open polygonal
chain support will be reintroduced in a dedicated selector class in a
following commit.

Existing closed polygon tests had also been extended to cover
the open polygonal chain mode and are reverted accordingly.

Co-authored-by: Mafalda Botelho <mafaldabotelho@tecnico.ulisboa.pt>
Introduce PolylineSelector, a widget class for open polygonal
chains. The class inherits from _PolygonalSelector and reuses
the common polygonal chain selector behavior.

Add tests for the new class.

Co-authored-by: Mafalda Botelho <mafaldabotelho@tecnico.ulisboa.pt>
Introduce gallery examples, add links to them in the
PolylineSelector docstring, and update the What's New
entry to reflect the new changes.

Co-authored-by: Mafalda Botelho <mafaldabotelho@tecnico.ulisboa.pt>
@lucaznch lucaznch changed the title ENH: Support open polygonal chains in PolygonSelector ENH: Support open polygonal chains in widgets May 27, 2026
@github-actions github-actions Bot added the Documentation: API files in lib/ and doc/api label May 27, 2026
@lucaznch
Copy link
Copy Markdown
Contributor Author

In the meantime, we decided to adjust the implementation and move to what we believe is a cleaner and more maintainable architectural approach for this feature.

The previous implementation extended PolygonSelector with a closed parameter controlling whether the polygonal chain was open or closed. This introduced a slight semantic mismatch, as PolygonSelector would support both closed and open polygonal chains, while also making the class somewhat more difficult to maintain.

The new implementation introduces two new classes: PolylineSelector, a new public widget class dedicated to open polygonal chains, and _PolygonalSelector, a new internal base class providing the core structure and behavior for polygonal chain selector widgets.

PolygonSelector now inherits from _PolygonalSelector (instead of _SelectorWidget) while preserving its original API, semantics, and behavior for closed polygonal regions.

Starting from the previous implementation (fully in 870d2e6), we analyzed which parts were common to polygonal chains and which were specific to closed or open chains, and then extracted the shared structure and behavior into _PolygonalSelector, while allowing subclasses to implement or override specialized behavior where appropriate.

Gallery example

One of the gallery examples demonstrates a simple cubic Bézier curve visualizer using PolylineSelector:

cubic_bezier_visualizer_demo.mp4

This example can be extended to include animations and more dynamic visuals:

cubic_bezier_visualizer_demo_v2.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Documentation: API files in lib/ and doc/api Documentation: examples files in galleries/examples topic: widgets/UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ENH]: OpenPolygonSelector

3 participants