@@ -458,47 +458,13 @@ def bode_plot(
458458 (noutputs if plot_phase else 0 )
459459 ncols = ninputs
460460
461- # See if we can use the current figure axes
462- fig = plt .gcf () # get current figure (or create new one)
463- if ax is None and plt .get_fignums ():
464- ax = fig .get_axes ()
465- if len (ax ) == nrows * ncols :
466- # Assume that the shape is right (no easy way to infer this)
467- ax = np .array (ax ).reshape (nrows , ncols )
468-
469- # Clear out any old text from the current figure
470- for text in fig .texts :
471- text .set_visible (False ) # turn off the text
472- del text # get rid of it completely
473-
474- elif len (ax ) != 0 :
475- # Need to generate a new figure
476- fig , ax = plt .figure (), None
477-
478- else :
479- # Blank figure, just need to recreate axes
480- ax = None
481-
482- # Create new axes, if needed, and customize them
483461 if ax is None :
484- with plt .rc_context (_freqplot_rcParams ):
485- ax_array = fig .subplots (nrows , ncols , squeeze = False )
486- fig .set_layout_engine ('tight' )
487- fig .align_labels ()
488-
489462 # Set up default sharing of axis limits if not specified
490463 for kw in ['share_magnitude' , 'share_phase' , 'share_frequency' ]:
491464 if kw not in kwargs or kwargs [kw ] is None :
492465 kwargs [kw ] = config .defaults ['freqplot.' + kw ]
493466
494- else :
495- # Make sure the axes are the right shape
496- if ax .shape != (nrows , ncols ):
497- raise ValueError (
498- "specified axes are not the right shape; "
499- f"got { ax .shape } but expecting ({ nrows } , { ncols } )" )
500- ax_array = ax
501- fig = ax_array [0 , 0 ].figure # just in case this is not gcf()
467+ fig , ax_array = _process_ax_keyword (ax , (nrows , ncols ), squeeze = False )
502468
503469 # Get the values for sharing axes limits
504470 share_magnitude = kwargs .pop ('share_magnitude' , None )
@@ -1780,11 +1746,8 @@ def _parse_linestyle(style_name, allow_false=False):
17801746 # Return counts and (optionally) the contour we used
17811747 return (counts , contours ) if return_contour else counts
17821748
1783- # Get the figure and axes to use
1784- if ax is None :
1785- fig , ax = plt .gcf (), plt .gca ()
1786- else :
1787- fig = ax .figure
1749+ fig , ax = _process_ax_keyword (
1750+ ax , shape = (1 , 1 ), squeeze = True , rcParams = _freqplot_rcParams )
17881751
17891752 # Create a list of lines for the output
17901753 out = np .empty (len (nyquist_responses ), dtype = object )
@@ -2229,7 +2192,7 @@ def singular_values_response(
22292192
22302193def singular_values_plot (
22312194 data , omega = None , * fmt , plot = None , omega_limits = None , omega_num = None ,
2232- label = None , title = None , legend_loc = 'center right' , ** kwargs ):
2195+ ax = None , label = None , title = None , legend_loc = 'center right' , ** kwargs ):
22332196 """Plot the singular values for a system.
22342197
22352198 Plot the singular values as a function of frequency for a system or
@@ -2358,22 +2321,8 @@ def singular_values_plot(
23582321 else :
23592322 return sigmas , omegas
23602323
2361- fig = plt .gcf () # get current figure (or create new one)
2362- ax_sigma = None # axes for plotting singular values
2363-
2364- # Get the current axes if they already exist
2365- for ax in fig .axes :
2366- if ax .get_label () == 'control-sigma' :
2367- ax_sigma = ax
2368-
2369- # If no axes present, create them from scratch
2370- if ax_sigma is None :
2371- if len (fig .axes ) > 0 :
2372- # Create a new figure to avoid overwriting in the old one
2373- fig = plt .figure ()
2374-
2375- with plt .rc_context (_freqplot_rcParams ):
2376- ax_sigma = plt .subplot (111 , label = 'control-sigma' )
2324+ fig , ax_sigma = _process_ax_keyword (ax , shape = (1 , 1 ), squeeze = True )
2325+ ax_sigma .set_label ('control-sigma' ) # TODO: deprecate?
23772326
23782327 # Handle color cycle manually as all singular values
23792328 # of the same systems are expected to be of the same color
@@ -2469,7 +2418,7 @@ def singular_values_plot(
24692418# Utility functions
24702419#
24712420# This section of the code contains some utility functions for
2472- # generating frequency domain plots
2421+ # generating frequency domain plots.
24732422#
24742423
24752424
@@ -2736,6 +2685,57 @@ def _process_line_labels(label, nsys, ninputs=0, noutputs=0):
27362685 return line_labels
27372686
27382687
2688+ def _process_ax_keyword (axs , shape = (1 , 1 ), rcParams = None , squeeze = False ):
2689+ """Utility function to process ax keyword to plotting commands.
2690+
2691+ This function processes the `ax` keyword to plotting commands. If no
2692+ ax keyword is passed, the current figure is checked to see if it has
2693+ the correct shape. If the shape matches the desired shape, then the
2694+ current figure and axes are returned. Otherwise a new figure is
2695+ created with axes of the desired shape.
2696+
2697+ Legacy behavior: some of the older plotting commands use a axes label
2698+ to identify the proper axes for plotting. This behavior is supported
2699+ through the use of the label keyword, but will only work if shape ==
2700+ (1, 1) and squeeze == True.
2701+
2702+ """
2703+ if axs is None :
2704+ fig = plt .gcf () # get current figure (or create new one)
2705+ axs = fig .get_axes ()
2706+
2707+ # Check to see if axes are the right shape; if not, create new figure
2708+ # Note: can't actually check the shape, just the total number of axes
2709+ if len (axs ) != np .prod (shape ):
2710+ with plt .rc_context (rcParams ):
2711+ if len (axs ) != 0 :
2712+ # Create a new figure
2713+ fig , axs = plt .subplots (* shape , squeeze = False )
2714+ else :
2715+ # Create new axes on (empty) figure
2716+ axs = fig .subplots (* shape , squeeze = False )
2717+ fig .set_layout_engine ('tight' )
2718+ fig .align_labels ()
2719+ else :
2720+ # Use the existing axes, properly reshaped
2721+ axs = np .asarray (axs ).reshape (* shape )
2722+ else :
2723+ try :
2724+ axs = np .asarray (axs ).reshape (shape )
2725+ except ValueError :
2726+ raise ValueError (
2727+ "specified axes are not the right shape; "
2728+ f"got { axs .shape } but expecting { shape } " )
2729+ fig = axs [0 , 0 ].figure
2730+
2731+ # Process the squeeze keyword
2732+ if squeeze and shape == (1 , 1 ):
2733+ axs = axs [0 , 0 ] # Just return the single axes object
2734+ elif squeeze :
2735+ axs = axs .squeeze ()
2736+
2737+ return fig , axs
2738+
27392739#
27402740# Utility functions to create nice looking labels (KLD 5/23/11)
27412741#
0 commit comments