@@ -138,6 +138,13 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='b' if int(matplot
138138 f .canvas .mpl_connect (
139139 'button_release_event' ,partial (_RLClickDispatcher ,sys = sys , fig = f ,ax_rlocus = f .axes [1 ],plotstr = plotstr , sisotool = sisotool , bode_plot_params = kwargs ['bode_plot_params' ],tvect = kwargs ['tvect' ]))
140140
141+ # zoom update on xlim/ylim changed, only then data on new limits
142+ # is available, i.e., cannot combine with _RLClickDispatcher
143+ dpfun = partial (
144+ _RLZoomDispatcher , sys = sys , ax_rlocus = ax , plotstr = plotstr )
145+ ax .callbacks .connect ('xlim_changed' , dpfun )
146+ ax .callbacks .connect ('ylim_changed' , dpfun )
147+
141148 # plot open loop poles
142149 poles = array (denp .r )
143150 ax .plot (real (poles ), imag (poles ), 'x' )
@@ -156,6 +163,7 @@ def root_locus(sys, kvect=None, xlim=None, ylim=None, plotstr='b' if int(matplot
156163 ax .set_xlim (xlim )
157164 if ylim :
158165 ax .set_ylim (ylim )
166+
159167 ax .set_xlabel ('Real' )
160168 ax .set_ylabel ('Imaginary' )
161169 if grid and sisotool :
@@ -263,7 +271,7 @@ def _indexes_filt(mymat,tolerance,zoom_xlim=None,zoom_ylim=None):
263271 break
264272
265273 # Check if the zoom box is not overshot and insert points where neccessary
266- if len (indexes_too_far_filtered ) == 0 and len (mymat ) < 300 :
274+ if len (indexes_too_far_filtered ) == 0 and len (mymat ) < 500 :
267275 limits = [zoom_xlim [0 ],zoom_xlim [1 ],zoom_ylim [0 ],zoom_ylim [1 ]]
268276 for index ,limit in enumerate (limits ):
269277 if index <= 1 :
@@ -411,23 +419,30 @@ def _RLSortRoots(mymat):
411419 prevrow = sorted [n , :]
412420 return sorted
413421
414- def _RLClickDispatcher (event ,sys ,fig , ax_rlocus ,plotstr , sisotool = False , bode_plot_params = None , tvect = None ):
415- """Rootlocus plot click dispatcher"""
422+ def _RLZoomDispatcher (event , sys , ax_rlocus , plotstr ):
423+ """Rootlocus plot zoom dispatcher"""
416424
417- # If zoom is used on the rootlocus plot smooth and update it
418- if plt .get_current_fig_manager ().toolbar .mode in ['zoom rect' ,'pan/zoom' ] and event .inaxes == ax_rlocus .axes :
419- (nump , denp ) = _systopoly1d (sys )
420- xlim ,ylim = ax_rlocus .get_xlim (),ax_rlocus .get_ylim ()
425+ nump , denp = _systopoly1d (sys )
426+ xlim , ylim = ax_rlocus .get_xlim (), ax_rlocus .get_ylim ()
427+
428+ kvect , mymat , xlim , ylim = _default_gains (
429+ nump , denp , xlim = None , ylim = None , zoom_xlim = xlim , zoom_ylim = ylim )
430+ _removeLine ('rootlocus' , ax_rlocus )
431+
432+ for i , col in enumerate (mymat .T ):
433+ ax_rlocus .plot (real (col ), imag (col ), plotstr , label = 'rootlocus' ,
434+ scalex = False , scaley = False )
421435
422- kvect , mymat , xlim , ylim = _default_gains ( nump , denp , xlim = None , ylim = None , zoom_xlim = xlim , zoom_ylim = ylim )
423- _removeLine ( 'rootlocus' , ax_rlocus )
436+ def _RLClickDispatcher ( event , sys , fig , ax_rlocus , plotstr , sisotool = False , bode_plot_params = None ,tvect = None ):
437+ """Rootlocus plot click dispatcher"""
424438
425- for i ,col in enumerate (mymat .T ):
426- ax_rlocus .plot (real (col ), imag (col ), plotstr ,label = 'rootlocus' )
439+ # Zoom is handled by specialized callback above, only do gain plot
440+ if event .inaxes == ax_rlocus .axes and \
441+ plt .get_current_fig_manager ().toolbar .mode not in \
442+ {'zoom rect' ,'pan/zoom' }:
427443
428- # if a point is clicked on the rootlocus plot visually emphasize it
429- else :
430- K = _RLFeedbackClicksPoint (event , sys , fig ,ax_rlocus ,sisotool )
444+ # if a point is clicked on the rootlocus plot visually emphasize it
445+ K = _RLFeedbackClicksPoint (event , sys , fig , ax_rlocus , sisotool )
431446 if sisotool and K is not None :
432447 _SisotoolUpdate (sys , fig , K , bode_plot_params , tvect )
433448
@@ -440,21 +455,27 @@ def _RLFeedbackClicksPoint(event,sys,fig,ax_rlocus,sisotool=False):
440455
441456 (nump , denp ) = _systopoly1d (sys )
442457
458+ xlim = ax_rlocus .get_xlim ()
459+ ylim = ax_rlocus .get_ylim ()
460+ x_tolerance = 0.05 * abs ((xlim [1 ] - xlim [0 ]))
461+ y_tolerance = 0.05 * abs ((ylim [1 ] - ylim [0 ]))
462+ gain_tolerance = np .mean ([x_tolerance , y_tolerance ])* 0.1
463+
443464 # Catch type error when event click is in the figure but not in an axis
444465 try :
445466 s = complex (event .xdata , event .ydata )
446467 K = - 1. / sys .horner (s )
468+ K_xlim = - 1. / sys .horner (complex (event .xdata + 0.05 * abs (xlim [1 ] - xlim [0 ]), event .ydata ))
469+ K_ylim = - 1. / sys .horner (complex (event .xdata , event .ydata + 0.05 * abs (ylim [1 ] - ylim [0 ])))
447470
448471 except TypeError :
449472 K = float ('inf' )
473+ K_xlim = float ('inf' )
474+ K_ylim = float ('inf' )
450475
451- xlim = ax_rlocus .get_xlim ()
452- ylim = ax_rlocus .get_ylim ()
453- x_tolerance = 0.05 * (xlim [1 ] - xlim [0 ])
454- y_tolerance = 0.05 * (ylim [1 ] - ylim [0 ])
455- gain_tolerance = np .min ([x_tolerance , y_tolerance ])* 1e-1
476+ gain_tolerance += 0.1 * max ([abs (K_ylim .imag / K_ylim .real ),abs (K_xlim .imag / K_xlim .real )])
456477
457- if abs (K .real ) > 1e-8 and abs (K .imag / K .real ) < gain_tolerance and event .inaxes == ax_rlocus .axes :
478+ if abs (K .real ) > 1e-8 and abs (K .imag / K .real ) < gain_tolerance and event .inaxes == ax_rlocus .axes and K . real > 0. :
458479
459480 # Display the parameters in the output window and figure
460481 print ("Clicked at %10.4g%+10.4gj gain %10.4g damp %10.4g" %
0 commit comments