8000 recalculate loci for sisotool and rlocus on axis scaling by repagh · Pull Request #1153 · python-control/python-control · GitHub
[go: up one dir, main page]

Skip to content

recalculate loci for sisotool and rlocus on axis scaling #1153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 21, 2025

Conversation

repagh
Copy link
Member
@repagh repagh commented Jun 5, 2025

adds recalculate of loci for pan/zoom for the root_locus_map and sisotool . Fixes #1151

@coveralls
Copy link
coveralls commented Jun 5, 2025

Coverage Status

coverage: 94.759% (+0.01%) from 94.746%
when pulling b588045 on repagh:zoom-rlocus
into 632391c on python-control:main.

@repagh repagh requested a review from Copilot June 6, 2025 06:50
Copy link
@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enables dynamic recalculation of root locus and pole-zero plots when the user pans or zooms in sisotool and rlocus, fixing issue #1151.

  • Integrate add_loci_recalculate callback into sisotool and root_locus_plot for auto-replot on axis changes
  • Extend root_locus_map signature to accept xlim, ylim and pass them through to gain calculation
  • Add replot support for pole-zero maps via a new pole_zero_replot function

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
control/sisotool.py Imported and invoked add_loci_recalculate, updated axis reference for root locus
control/rlocus.py Added xlim/ylim parameters, defined add_loci_recalculate, wired callback in root_locus_plot
control/pzmap.py Added replot method and pole_zero_replot for pole-zero redraw on zoom/pan
Comments suppressed due to low confidence (2)

control/sisotool.py:141

  • Variable axes is undefined here; you likely meant to use fig.axes or the local axes variable instead of axes.
ax_rlocus = axes[0,1] #fig.axes[1]

control/pzmap.py:552

  • ControlPlot instances expose their axes as cp.axes, not cp.ax. Update to cp.axes[0,0] to avoid AttributeError.
cp.lines[idx,2].append(cp.ax[0,0].plot(

Comment on lines +162 to 165
if event.inaxes == ax.axes:

fig = ax.figure

Copy link
Preview
Copilot AI Jun 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The original toolbar mode check for 'zoom rect' and 'pan/zoom' was removed, which may trigger click events during zooming or panning. Consider re-adding that guard to prevent unintended callbacks.

Suggested change
if event.inaxes == ax.axes:
fig = ax.figure
fig = ax.figure
toolbar = fig.canvas.toolbar
if toolbar is not None and toolbar.mode in ['zoom rect', 'pan/zoom']:
return
if event.inaxes == ax.axes:

Copilot uses AI. Check for mistakes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my experience, the risk of interpreting a click as a valid gain selection is small, and in my testing (with a print statement indicating when the callback is invoked), the zoom and pan events di 10000 d not propagate to this callback. Removing the zoom_rect and pan/zoom check also removes unintended mode change (updating of the remaining 3 plots is inhibited when zoom / pan is selected), which can be confusing to users.

# Add a reaction to axis scale changes, if given LTI systems, and
# there is no set of pre-defined gains
if gains is None:
add_loci_recalculate(sysdata, cplt, cplt.axes[0,0])
Copy link
Preview
Copilot AI Jun 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Recalculating the entire locus on every xlim/ylim change can be expensive. Consider debouncing these callbacks or limiting the update frequency during continuous zooming.

Copilot uses AI. Check for mistakes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was no issue when interacting with the root-locus plot. Default number of points calculate is a measly 50, with the adaptive code in _default_gains typically a maximum between 100 and 200 points was produced. The callback will not be activated when the user manually specifies the gains.

Copy link
Member
@murrayrm murrayrm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to do a bit of testing, but here are some code style comments, mainly in docstrings (see online style guide, linked below).

control/pzmap.py Outdated
Parameters
----------
cplt: ControlPlot
graphics handles of the existing plot
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency with python-control docstring standards, this should start with a capital letter and end in a period. See https://python-control.readthedocs.io/en/latest/develop.html#documentation-guidelines

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amended commit

control/pzmap.py Outdated
@@ -513,6 +524,35 @@ def _click_dispatcher(event):
return ControlPlot(out, ax, fig, legend=legend)


def pole_zero_replot(pzmap_responses, cp):
"""Update the loci of a plot after zooming/panning
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should end in a period.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amended commit

control/pzmap.py Outdated
----------
pzmap_responses : PoleZeroMap list
Responses to update
cp : ControlPlot
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to use cp here instead of cplt, as above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amended commit

# Legacy processing: return locations of poles and zeros as a tuple
if plot is True:
return responses.loci, responses.gains

return ControlPlot(cplt.lines, cplt.axes, cplt.figure)


def add_loci_recalculate(sysdata, cplt, axis):
""" Add a calback to re-calculate the loci data fitting a zoom action
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix typo (callback) and end in period.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amended commit

@@ -46,6 +46,10 @@ def root_locus_map(sysdata, gains=None):
gains : array_like, optional
Gains to use in computing plot of closed-loop poles. If not given,
gains are chosen to include the main features of the root locus map.
xlim : array_like, 2 elements, optional
Plot range
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add period. Perhaps also expand description (see other uses of xlim, for example in control.root_locus_plot.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amended commit

@@ -137,15 +138,18 @@ def sisotool(sys, initial_gain=None, xlim_rlocus=None, ylim_rlocus=None,
# sys[0, 0], initial_gain=initial_gain, xlim=xlim_rlocus,
# ylim=ylim_rlocus, plotstr=plotstr_rlocus, grid=rlocus_grid,
# ax=fig.axes[1])
ax_rlocus = fig.axes[1]
root_locus_map(sys[0, 0]).plot(
ax_rlocus = axes[0,1] #fig.axes[1]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP8: two spaces before #, one space after.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amended commit

@murrayrm murrayrm merged commit 34c6d59 into python-control:main Jun 21, 2025
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Regression: root-locus plots may be coarse, and no longer react to zoom events
3 participants
0