@@ -458,47 +458,13 @@ def bode_plot(
458
458
(noutputs if plot_phase else 0 )
459
459
ncols = ninputs
460
460
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
483
461
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
-
489
462
# Set up default sharing of axis limits if not specified
490
463
for kw in ['share_magnitude' , 'share_phase' , 'share_frequency' ]:
491
464
if kw not in kwargs or kwargs [kw ] is None :
492
465
kwargs [kw ] = config .defaults ['freqplot.' + kw ]
493
466
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 )
502
468
503
469
# Get the values for sharing axes limits
504
470
share_magnitude = kwargs .pop ('share_magnitude' , None )
@@ -1780,11 +1746,8 @@ def _parse_linestyle(style_name, allow_false=False):
1780
1746
# Return counts and (optionally) the contour we used
1781
1747
return (counts , contours ) if return_contour else counts
1782
1748
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 )
1788
1751
1789
1752
# Create a list of lines for the output
1790
1753
out = np .empty (len (nyquist_responses ), dtype = object )
@@ -2235,7 +2198,7 @@ def singular_values_response(
2235
2198
2236
2199
def singular_values_plot (
2237
2200
data , omega = None , * fmt , plot = None , omega_limits = None , omega_num = None ,
2238
- label = None , title = None , legend_loc = 'center right' , ** kwargs ):
2201
+ ax = None , label = None , title = None , legend_loc = 'center right' , ** kwargs ):
2239
2202
"""Plot the singular values for a system.
2240
2203
2241
2204
Plot the singular values as a function of frequency for a system or
@@ -2364,22 +2327,8 @@ def singular_values_plot(
2364
2327
else :
2365
2328
return sigmas , omegas
2366
2329
2367
- fig = plt .gcf () # get current figure (or create new one)
2368
- ax_sigma = None # axes for plotting singular values
2369
-
2370
- # Get the current axes if they already exist
2371
- for ax in fig .axes :
2372
- if ax .get_label () == 'control-sigma' :
2373
- ax_sigma = ax
2374
-
2375
- # If no axes present, create them from scratch
2376
- if ax_sigma is None :
2377
- if len (fig .axes ) > 0 :
2378
- # Create a new figure to avoid overwriting in the old one
2379
- fig = plt .figure ()
2380
-
2381
- with plt .rc_context (_freqplot_rcParams ):
2382
- ax_sigma = plt .subplot (111 , label = 'control-sigma' )
2330
+ fig , ax_sigma = _process_ax_keyword (ax , shape = (1 , 1 ), squeeze = True )
2331
+ ax_sigma .set_label ('control-sigma' ) # TODO: deprecate?
2383
2332
2384
2333
# Handle color cycle manually as all singular values
2385
2334
# of the same systems are expected to be of the same color
@@ -2475,7 +2424,7 @@ def singular_values_plot(
2475
2424
# Utility functions
2476
2425
#
2477
2426
# This section of the code contains some utility functions for
2478
- # generating frequency domain plots
2427
+ # generating frequency domain plots.
2479
2428
#
2480
2429
2481
2430
@@ -2742,6 +2691,57 @@ def _process_line_labels(label, nsys, ninputs=0, noutputs=0):
2742
2691
return line_labels
2743
2692
2744
2693
2694
+ def _process_ax_keyword (axs , shape = (1 , 1 ), rcParams = None , squeeze = False ):
2695
+ """Utility function to process ax keyword to plotting commands.
2696
+
2697
+ This function processes the `ax` keyword to plotting commands. If no
2698
+ ax keyword is passed, the current figure is checked to see if it has
2699
+ the correct shape. If the shape matches the desired shape, then the
2700
+ current figure and axes are returned. Otherwise a new figure is
2701
+ created with axes of the desired shape.
2702
+
2703
+ Legacy behavior: some of the older plotting commands use a axes label
2704
+ to identify the proper axes for plotting. This behavior is supported
2705
+ through the use of the label keyword, but will only work if shape ==
2706
+ (1, 1) and squeeze == True.
2707
+
2708
+ """
2709
+ if axs is None :
2710
+ fig = plt .gcf () # get current figure (or create new one)
2711
+ axs = fig .get_axes ()
2712
+
2713
+ # Check to see if axes are the right shape; if not, create new figure
2714
+ # Note: can't actually check the shape, just the total number of axes
2715
+ if len (axs ) != np .prod (shape ):
2716
+ with plt .rc_context (rcParams ):
2717
+ if len (axs ) != 0 :
2718
+ # Create a new figure
2719
+ fig , axs = plt .subplots (* shape , squeeze = False )
2720
+ else :
2721
+ # Create new axes on (empty) figure
2722
+ axs = fig .subplots (* shape , squeeze = False )
2723
+ fig .set_layout_engine ('tight' )
2724
+ fig .align_labels ()
2725
+ else :
2726
+ # Use the existing axes, properly reshaped
2727
+ axs = np .asarray (axs ).reshape (* shape )
2728
+ else :
2729
+ try :
2730
+ axs = np .asarray (axs ).reshape (shape )
2731
+ except ValueError :
2732
+ raise ValueError (
2733
+ "specified axes are not the right shape; "
2734
+ f"got { axs .shape } but expecting { shape } " )
2735
+ fig = axs [0 , 0 ].figure
2736
+
2737
+ # Process the squeeze keyword
2738
+ if squeeze and shape == (1 , 1 ):
2739
+ axs = axs [0 , 0 ] # Just return the single axes object
2740
+ elif squeeze :
2741
+ axs = axs .squeeze ()
2742
+
2743
+ return fig , axs
2744
+
2745
2745
#
2746
2746
# Utility functions to create nice looking labels (KLD 5/23/11)
2747
2747
#
0 commit comments