@@ -519,8 +519,8 @@ def gen_zero_centered_series(val_min, val_max, period):
519519
520520# Default values for module parameter variables
521521_nyquist_defaults = {
522- 'nyquist.primary_style' : ['-' , ': ' ], # style for primary curve
523- 'nyquist.mirror_style' : ['--' , '-. ' ], # style for mirror curve
522+ 'nyquist.primary_style' : ['-' , '-. ' ], # style for primary curve
523+ 'nyquist.mirror_style' : ['--' , ': ' ], # style for mirror curve
524524 'nyquist.arrows' : 2 ,
525525 'nyquist.arrow_size' : 8 ,
526526 'nyquist.indent_radius' : 1e-6 , # indentation radius
@@ -696,6 +696,7 @@ def nyquist_plot(syslist, omega=None, plot=True, omega_limits=None,
696696 kwargs .pop ('arrow_length' , False )
697697
698698 # Get values for params (and pop from list to allow keyword use in plot)
699+ omega_num_given = omega_num is not None
699700 omega_num = config ._get_param ('freqplot' , 'number_of_samples' , omega_num )
700701 arrows = config ._get_param (
701702 'nyquist' , 'arrows' , kwargs , _nyquist_defaults , pop = True )
@@ -736,8 +737,13 @@ def _parse_linestyle(style_name, allow_false=False):
736737 omega , omega_range_given = _determine_omega_vector (
737738 syslist , omega , omega_limits , omega_num )
738739 if not omega_range_given :
739- # Start contour at zero frequency
740- omega [0 ] = 0.
740+ if omega_num_given :
741+ # Just reset the starting point
742+ omega [0 ] = 0.0
743+ else :
744+ # Insert points between the origin and the first frequency point
745+ omega = np .concatenate ((
746+ np .linspace (0 , omega [0 ], indent_points ), omega [1 :]))
741747
742748 # Go through each system and keep track of the results
743749 counts , contours = [], []
@@ -938,17 +944,23 @@ def _parse_linestyle(style_name, allow_false=False):
938944 x_reg , y_reg , primary_style [0 ], color = color , * args , ** kwargs )
939945 c = p [0 ].get_color ()
940946
947+ # Figure out how much to offset the curve: the offset goes from
948+ # zero at the start of the scaled section to max_curve_offset as
949+ # we move along the curve
950+ curve_offset = _compute_curve_offset (
951+ resp , scale_mask , max_curve_offset )
952+
941953 # Plot the scaled sections of the curve (changing linestyle)
942954 x_scl = np .ma .masked_where (scale_mask , resp .real )
943955 y_scl = np .ma .masked_where (scale_mask , resp .imag )
944956 plt .plot (
945- x_scl * (1 + max_curve_offset ), y_scl * (1 + max_curve_offset ),
957+ x_scl * (1 + curve_offset ), y_scl * (1 + curve_offset ),
946958 primary_style [1 ], color = c , * args , ** kwargs )
947959
948960 # Plot the primary curve (invisible) for setting arrows
949961 x , y = resp .real .copy (), resp .imag .copy ()
950- x [reg_mask ] *= (1 + max_curve_offset )
951- y [reg_mask ] *= (1 + max_curve_offset )
962+ x [reg_mask ] *= (1 + curve_offset [ reg_mask ] )
963+ y [reg_mask ] *= (1 + curve_offset [ reg_mask ] )
952964 p = plt .plot (x , y , linestyle = 'None' , color = c , * args , ** kwargs )
953965
954966 # Add arrows
@@ -962,14 +974,14 @@ def _parse_linestyle(style_name, allow_false=False):
962974 plt .plot (
963975 x_reg , - y_reg , mirror_style [0 ], color = c , * args , ** kwargs )
964976 plt .plot (
965- x_scl * (1 - max_curve_offset ),
966- - y_scl * (1 - max_curve_offset ),
977+ x_scl * (1 - curve_offset ),
978+ - y_scl * (1 - curve_offset ),
967979 mirror_style [1 ], color = c , * args , ** kwargs )
968980
969981 # Add the arrows (on top of an invisible contour)
970982 x , y = resp .real .copy (), resp .imag .copy ()
971- x [reg_mask ] *= (1 - max_curve_offset )
972- y [reg_mask ] *= (1 - max_curve_offset )
983+ x [reg_mask ] *= (1 - curve_offset [ reg_mask ] )
984+ y [reg_mask ] *= (1 - curve_offset [ reg_mask ] )
973985 p = plt .plot (x , - y , linestyle = 'None' , color = c , * args , ** kwargs )
974986 _add_arrows_to_line2D (
975987 ax , p [0 ], arrow_pos , arrowstyle = arrow_style , dir = - 1 )
@@ -1087,6 +1099,62 @@ def _add_arrows_to_line2D(
10871099 arrows .append (p )
10881100 return arrows
10891101
1102+ #
1103+ # Function to compute Nyquist curve offsets
1104+ #
1105+ # This function computes a smoothly varying offset that starts and ends at
1106+ # zero at the ends of a scaled segment.
1107+ #
1108+ def _compute_curve_offset (resp , mask , max_offset ):
1109+ # Compute the arc length along the curve
1110+ s_curve = np .cumsum (
1111+ np .sqrt (np .diff (resp .real ) ** 2 + np .diff (resp .imag ) ** 2 ))
1112+
1113+ # Initialize the offset
1114+ offset = np .zeros (resp .size )
1115+ arclen = np .zeros (resp .size )
1116+
1117+ # Walk through the response and keep track of each continous component
1118+ i , nsegs = 0 , 0
1119+ while i < resp .size :
1120+ # Skip the regular segment
1121+ while i < resp .size and mask [i ]:
1122+ i += 1 # Increment the counter
1123+ if i == resp .size :
1124+ break ;
1125+ # Keep track of the arclength
1126+ arclen [i ] = arclen [i - 1 ] + np .abs (resp [i ] - resp [i - 1 ])
1127+
1128+ nsegs += 0.5
1129+ if i == resp .size :
1130+ break ;
1131+
1132+ # Save the starting offset of this segment
1133+ seg_start = i
1134+
1135+ # Walk through the scaled segment
1136+ while i < resp .size and not mask [i ]:
1137+ i += 1
1138+ if i == resp .size : # See if we are done with this segment
1139+ break ;
1140+ # Keep track of the arclength
1141+ arclen [i ] = arclen [i - 1 ] + np .abs (resp [i ] - resp [i - 1 ])
1142+
1143+ nsegs += 0.5
1144+ if i == resp .size :
1145+ break ;
1146+
1147+ # Save the ending offset of this segment
1148+ seg_end = i
1149+
1150+ # Now compute the scaling for this segment
1151+ s_segment = arclen [seg_end - 1 ] - arclen [seg_start ]
1152+ offset [seg_start :seg_end ] = max_offset * s_segment / s_curve [- 1 ] * \
1153+ np .sin (np .pi * (arclen [seg_start :seg_end ]
1154+ - arclen [seg_start ])/ s_segment )
1155+
1156+ return offset
1157+
10901158
10911159#
10921160# Gang of Four plot
0 commit comments