Skip to content

Commit 44123c9

Browse files
committed
refactoring to put common functions in ctrlplot.py
1 parent 3732217 commit 44123c9

4 files changed

Lines changed: 102 additions & 114 deletions

File tree

control/ctrlplot.py

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
#
44
# Collection of functions that are used by various plotting functions.
55

6+
from os.path import commonprefix
7+
68
import matplotlib.pyplot as plt
79
import numpy as np
810

911
from . import config
1012

11-
__all__ = ['suptitle']
13+
__all__ = ['suptitle', 'get_plot_axes']
1214

1315

1416
def suptitle(
@@ -56,6 +58,94 @@ def suptitle(
5658
raise ValueError(f"unknown frame '{frame}'")
5759

5860

61+
# Create vectorized function to find axes from lines
62+
def get_plot_axes(line_array):
63+
"""Get a list of axes from an array of lines.
64+
65+
This function can be used to return the set of axes corresponding to
66+
the line array that is returned by `time_response_plot`. This is useful for
67+
generating an axes array that can be passed to subsequent plotting
68+
calls.
69+
70+
Parameters
71+
----------
72+
line_array : array of list of Line2D
73+
A 2D array with elements corresponding to a list of lines appearing
74+
in an axes, matching the return type of a time response data plot.
75+
76+
Returns
77+
-------
78+
axes_array : array of list of Axes
79+
A 2D array with elements corresponding to the Axes assocated with
80+
the lines in `line_array`.
81+
82+
Notes
83+
-----
84+
Only the first element of each array entry is used to determine the axes.
85+
86+
"""
87+
_get_axes = np.vectorize(lambda lines: lines[0].axes)
88+
return _get_axes(line_array)
89+
90+
#
91+
# Utility functions
92+
#
93+
94+
95+
# Utility function to make legend labels
96+
def _make_legend_labels(labels, ignore_common=False):
97+
98+
# Look for a common prefix (up to a space)
99+
common_prefix = commonprefix(labels)
100+
last_space = common_prefix.rfind(', ')
101+
if last_space < 0 or ignore_common:
102+
common_prefix = ''
103+
elif last_space > 0:
104+
common_prefix = common_prefix[:last_space]
105+
prefix_len = len(common_prefix)
106+
107+
# Look for a common suffice (up to a space)
108+
common_suffix = commonprefix(
109+
[label[::-1] for label in labels])[::-1]
110+
suffix_len = len(common_suffix)
111+
# Only chop things off after a comma or space
112+
while suffix_len > 0 and common_suffix[-suffix_len] != ',':
113+
suffix_len -= 1
114+
115+
# Strip the labels of common information
116+
if suffix_len > 0 and not ignore_common:
117+
labels = [label[prefix_len:-suffix_len] for label in labels]
118+
else:
119+
labels = [label[prefix_len:] for label in labels]
120+
121+
return labels
122+
123+
124+
def _update_suptitle(fig, title, rcParams=None, frame='axes'):
125+
if fig is not None and isinstance(title, str):
126+
# Get the current title, if it exists
127+
old_title = None if fig._suptitle is None else fig._suptitle._text
128+
new_title = title
129+
130+
if old_title is not None:
131+
# Find the common part of the titles
132+
common_prefix = commonprefix([old_title, new_title])
133+
134+
# Back up to the last space
135+
last_space = common_prefix.rfind(' ')
136+
if last_space > 0:
137+
common_prefix = common_prefix[:last_space]
138+
common_len = len(common_prefix)
139+
140+
# Add the new part of the title (usually the system name)
141+
if old_title[common_len:] != new_title[common_len:]:
142+
separator = ',' if len(common_prefix) > 0 else ';'
143+
new_title = old_title + separator + new_title[common_len:]
144+
145+
# Add the title
146+
suptitle(title, fig=fig, rcParams=rcParams, frame=frame)
147+
148+
59149
def _find_axes_center(fig, axs):
60150
"""Find the midpoint between axes in display coordinates.
61151

control/freqplot.py

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919

2020
from . import config
2121
from .bdalg import feedback
22-
from .ctrlplot import suptitle, _find_axes_center
22+
from .ctrlplot import suptitle, _find_axes_center, _make_legend_labels, \
23+
_update_suptitle
2324
from .ctrlutil import unwrap
2425
from .exception import ControlMIMONotImplemented
2526
from .frdata import FrequencyResponseData
2627
from .lti import LTI, _process_frequency_response, frequency_response
2728
from .margins import stability_margins
2829
from .statesp import StateSpace
29-
from .timeplot import _make_legend_labels
3030
from .xferfcn import TransferFunction
3131

3232
__all__ = ['bode_plot', 'NyquistResponseData', 'nyquist_response',
@@ -954,28 +954,7 @@ def gen_zero_centered_series(val_min, val_max, period):
954954
else:
955955
title = data[0].title
956956

957-
if fig is not None and isinstance(title, str):
958-
# Get the current title, if it exists
959-
old_title = None if fig._suptitle is None else fig._suptitle._text
960-
new_title = title
961-
962-
if old_title is not None:
963-
# Find the common part of the titles
964-
common_prefix = commonprefix([old_title, new_title])
965-
966-
# Back up to the last space
967-
last_space = common_prefix.rfind(' ')
968-
if last_space > 0:
969-
common_prefix = common_prefix[:last_space]
970-
common_len = len(common_prefix)
971-
972-
# Add the new part of the title (usually the system name)
973-
if old_title[common_len:] != new_title[common_len:]:
974-
separator = ',' if len(common_prefix) > 0 else ';'
975-
new_title = old_title + separator + new_title[common_len:]
976-
977-
# Add the title
978-
suptitle(title, fig=fig, rcParams=rcParams, frame=suptitle_frame)
957+
_update_suptitle(fig, title, rcParams=rcParams, frame=suptitle_frame)
979958

980959
#
981960
# Create legends

control/tests/timeplot_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ def test_response_plots(
191191

192192

193193
def test_axes_setup():
194-
get_plot_axes = ct.timeplot.get_plot_axes
194+
get_plot_axes = ct.get_plot_axes
195195

196196
sys_2x3 = ct.rss(4, 2, 3)
197197
sys_2x3b = ct.rss(4, 2, 3)
@@ -377,7 +377,7 @@ def test_rcParams():
377377
assert ax.title.get_fontsize() == 10
378378
assert ax.xaxis._get_tick_label_size('x') == 10
379379
assert ax.yaxis._get_tick_label_size('y') == 10
380-
assert fig._suptitle.get_fontsize() == 12
380+
assert fig._suptitle.get_fontsize() == 10
381381

382382
def test_relabel():
383383
sys1 = ct.rss(2, inputs='u', outputs='y')

control/timeplot.py

Lines changed: 6 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@
88
# Note: It might eventually make sense to put the functions here
99
# directly into timeresp.py.
1010

11-
import numpy as np
11+
from warnings import warn
12+
1213
import matplotlib as mpl
1314
import matplotlib.pyplot as plt
14-
from os.path import commonprefix
15-
from warnings import warn
15+
import numpy as np
1616

1717
from . import config
18+
from .ctrlplot import _make_legend_labels, _update_suptitle
1819

19-
__all__ = ['time_response_plot', 'combine_time_responses', 'get_plot_axes']
20+
__all__ = ['time_response_plot', 'combine_time_responses']
2021

2122
# Default font dictionary
2223
_timeplot_rcParams = mpl.rcParams.copy()
@@ -157,7 +158,6 @@ def time_response_plot(
157158
#
158159
# Process keywords and set defaults
159160
#
160-
161161
# Set up defaults
162162
time_label = config._get_param(
163163
'timeplot', 'time_label', kwargs, _timeplot_defaults, pop=True)
@@ -597,29 +597,7 @@ def _make_line_label(signal_index, signal_labels, trace_index):
597597
# list of systems (e.g., "Step response for sys[1], sys[2]").
598598
#
599599

600-
if fig is not None and title is not None:
601-
# Get the current title, if it exists
602-
old_title = None if fig._suptitle is None else fig._suptitle._text
603-
new_title = title
604-
605-
if old_title is not None:
606-
# Find the common part of the titles
607-
common_prefix = commonprefix([old_title, new_title])
608-
609-
# Back up to the last space
610-
last_space = common_prefix.rfind(' ')
611-
if last_space > 0:
612-
common_prefix = common_prefix[:last_space]
613-
common_len = len(common_prefix)
614-
615-
# Add the new part of the title (usually the system name)
616-
if old_title[common_len:] != new_title[common_len:]:
617-
separator = ',' if len(common_prefix) > 0 else ';'
618-
new_title = old_title + separator + new_title[common_len:]
619-
620-
# Add the title
621-
with plt.rc_context(rcParams):
622-
fig.suptitle(new_title)
600+
_update_suptitle(fig, title, rcParams=rcParams)
623601

624602
return out
625603

@@ -730,62 +708,3 @@ def combine_time_responses(response_list, trace_labels=None, title=None):
730708
return_x=base.return_x, squeeze=base.squeeze, sysname=base.sysname,
731709
trace_labels=trace_labels, trace_types=trace_types,
732710
plot_inputs=base.plot_inputs)
733-
734-
735-
# Create vectorized function to find axes from lines
736-
def get_plot_axes(line_array):
737-
"""Get a list of axes from an array of lines.
738-
739-
This function can be used to return the set of axes corresponding to
740-
the line array that is returned by `time_response_plot`. This is useful for
741-
generating an axes array that can be passed to subsequent plotting
742-
calls.
743-
744-
Parameters
745-
----------
746-
line_array : array of list of Line2D
747-
A 2D array with elements corresponding to a list of lines appearing
748-
in an axes, matching the return type of a time response data plot.
749-
750-
Returns
751-
-------
752-
axes_array : array of list of Axes
753-
A 2D array with elements corresponding to the Axes assocated with
754-
the lines in `line_array`.
755-
756-
Notes
757-
-----
758-
Only the first element of each array entry is used to determine the axes.
759-
760-
"""
761-
_get_axes = np.vectorize(lambda lines: lines[0].axes)
762-
return _get_axes(line_array)
763-
764-
765-
# Utility function to make legend labels
766-
def _make_legend_labels(labels, ignore_common=False):
767-
768-
# Look for a common prefix (up to a space)
769-
common_prefix = commonprefix(labels)
770-
last_space = common_prefix.rfind(', ')
771-
if last_space < 0 or ignore_common:
772-
common_prefix = ''
773-
elif last_space > 0:
774-
common_prefix = common_prefix[:last_space]
775-
prefix_len = len(common_prefix)
776-
777-
# Look for a common suffice (up to a space)
778-
common_suffix = commonprefix(
779-
[label[::-1] for label in labels])[::-1]
780-
suffix_len = len(common_suffix)
781-
# Only chop things off after a comma or space
782-
while suffix_len > 0 and common_suffix[-suffix_len] != ',':
783-
suffix_len -= 1
784-
785-
# Strip the labels of common information
786-
if suffix_len > 0 and not ignore_common:
787-
labels = [label[prefix_len:-suffix_len] for label in labels]
788-
else:
789-
labels = [label[prefix_len:] for label in labels]
790-
791-
return labels

0 commit comments

Comments
 (0)