@@ -2997,7 +2997,7 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
29972997 barsabove = False , errorevery = 1 , ecolor = None , elinewidth = None ,
29982998 capsize = None , capthick = None , xlolims = False , xuplims = False ,
29992999 ylolims = False , yuplims = False , zlolims = False , zuplims = False ,
3000- arrow_length_ratio = .4 , ** kwargs ):
3000+ ** kwargs ):
30013001 """
30023002 Plot lines and/or markers with errorbars around them.
30033003
@@ -3074,10 +3074,6 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
30743074 Used to avoid overlapping error bars when two series share x-axis
30753075 values.
30763076
3077- arrow_length_ratio : float, default: 0.4
3078- Passed to :meth:`quiver`, the ratio of the arrow head with respect
3079- to the quiver.
3080-
30813077 Returns
30823078 -------
30833079 errlines : list
@@ -3183,7 +3179,7 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
31833179 eb_lines_style [key ] = kwargs [key ]
31843180
31853181 # Make the style dict for caps (the "hats").
3186- eb_cap_style = {** base_style , 'linestyle' : 'none ' }
3182+ eb_cap_style = {** base_style , 'linestyle' : 'None ' }
31873183 if capsize is None :
31883184 capsize = rcParams ["errorbar.capsize" ]
31893185 if capsize > 0 :
@@ -3207,14 +3203,8 @@ def _extract_errs(err, data, lomask, himask):
32073203 else :
32083204 low_err , high_err = err , err
32093205
3210- # for compatibility with the 2d errorbar function, when both upper
3211- # and lower limits specified, we need to draw the markers / line
3212- common_mask = (lomask == himask ) & everymask
3213- _lomask = lomask | common_mask
3214- _himask = himask | common_mask
3215-
3216- lows = np .where (_lomask , data - low_err , data )
3217- highs = np .where (_himask , data + high_err , data )
3206+ lows = np .where (lomask | ~ everymask , data , data - low_err )
3207+ highs = np .where (himask | ~ everymask , data , data + high_err )
32183208
32193209 return lows , highs
32203210
@@ -3229,6 +3219,33 @@ def _extract_errs(err, data, lomask, himask):
32293219 capmarker = {0 : '|' , 1 : '|' , 2 : '_' }
32303220 i_xyz = {'x' : 0 , 'y' : 1 , 'z' : 2 }
32313221
3222+ # Calculate marker size from points to quiver length. Because these are
3223+ # not markers, and 3D Axes do not use the normal transform stack, this
3224+ # is a bit involved. Since the quiver arrows will change size as the
3225+ # scene is rotated, they are given a standard size based on viewing
3226+ # them directly in planar form.
3227+ quiversize = eb_cap_style .get ('markersize' ,
3228+ rcParams ['lines.markersize' ]) ** 2
3229+ quiversize *= self .figure .dpi / 72
3230+ quiversize = self .transAxes .inverted ().transform ([
3231+ (0 , 0 ), (quiversize , quiversize )])
3232+ quiversize = np .mean (np .diff (quiversize , axis = 0 ))
3233+ # quiversize is now in Axes coordinates, and to convert back to data
3234+ # coordinates, we need to run it through the inverse 3D transform. For
3235+ # consistency, this uses a fixed azimuth and elevation.
3236+ with cbook ._setattr_cm (self , azim = 0 , elev = 0 ):
3237+ invM = np .linalg .inv (self .get_proj ())
3238+ # azim=elev=0 produces the Y-Z plane, so quiversize in 2D 'x' is 'y' in
3239+ # 3D, hence the 1 index.
3240+ quiversize = np .dot (invM , np .array ([quiversize , 0 , 0 , 0 ]))[1 ]
3241+ # Quivers use a fixed 15-degree arrow head, so scale up the length so
3242+ # that the size corresponds to the base. In other words, this constant
3243+ # corresponds to the equation tan(15) = (base / 2) / (arrow length).
3244+ quiversize *= 1.8660254037844388
3245+ eb_quiver_style = {** eb_cap_style ,
3246+ 'length' : quiversize , 'arrow_length_ratio' : 1 }
3247+ eb_quiver_style .pop ('markersize' , None )
3248+
32323249 # loop over x-, y-, and z-direction and draw relevant elements
32333250 for zdir , data , err , lolims , uplims in zip (
32343251 ['x' , 'y' , 'z' ], [x , y , z ], [xerr , yerr , zerr ],
@@ -3249,18 +3266,16 @@ def _extract_errs(err, data, lomask, himask):
32493266 lolims = np .broadcast_to (lolims , len (data )).astype (bool )
32503267 uplims = np .broadcast_to (uplims , len (data )).astype (bool )
32513268
3252- nolims = ~ (lolims | uplims )
3253-
32543269 # a nested list structure that expands to (xl,xh),(yl,yh),(zl,zh),
32553270 # where x/y/z and l/h correspond to dimensions and low/high
32563271 # positions of errorbars in a dimension we're looping over
32573272 coorderr = [
3258- _extract_errs (err * dir_vector [i ], coord ,
3259- ~ lolims & everymask , ~ uplims & everymask )
3273+ _extract_errs (err * dir_vector [i ], coord , lolims , uplims )
32603274 for i , coord in enumerate ([x , y , z ])]
32613275 (xl , xh ), (yl , yh ), (zl , zh ) = coorderr
32623276
32633277 # draws capmarkers - flat caps orthogonal to the error bars
3278+ nolims = ~ (lolims | uplims )
32643279 if nolims .any () and capsize > 0 :
32653280 lo_caps_xyz = _apply_mask ([xl , yl , zl ], nolims & everymask )
32663281 hi_caps_xyz = _apply_mask ([xh , yh , zh ], nolims & everymask )
@@ -3278,24 +3293,12 @@ def _extract_errs(err, data, lomask, himask):
32783293 caplines .append (cap_lo )
32793294 caplines .append (cap_hi )
32803295
3281- if (lolims | uplims ).any ():
3282- limits = [
3283- _extract_errs (err * dir_vector [i ], coord , uplims , lolims )
3284- for i , coord in enumerate ([x , y , z ])]
3285-
3286- (xlo , xup ), (ylo , yup ), (zlo , zup ) = limits
3287- lomask = lolims & everymask
3288- upmask = uplims & everymask
3289- lolims_xyz = np .array (_apply_mask ([xlo , ylo , zlo ], upmask ))
3290- uplims_xyz = np .array (_apply_mask ([xup , yup , zup ], lomask ))
3291- lo_xyz = np .array (_apply_mask ([x , y , z ], upmask ))
3292- up_xyz = np .array (_apply_mask ([x , y , z ], lomask ))
3293- x0 , y0 , z0 = np .concatenate ([lo_xyz , up_xyz ], axis = - 1 )
3294- dx , dy , dz = np .concatenate ([lolims_xyz - lo_xyz ,
3295- uplims_xyz - up_xyz ], axis = - 1 )
3296- self .quiver (x0 , y0 , z0 , dx , dy , dz ,
3297- arrow_length_ratio = arrow_length_ratio ,
3298- ** eb_lines_style )
3296+ if lolims .any ():
3297+ xh0 , yh0 , zh0 = _apply_mask ([xh , yh , zh ], lolims & everymask )
3298+ self .quiver (xh0 , yh0 , zh0 , * dir_vector , ** eb_quiver_style )
3299+ if uplims .any ():
3300+ xl0 , yl0 , zl0 = _apply_mask ([xl , yl , zl ], uplims & everymask )
3301+ self .quiver (xl0 , yl0 , zl0 , * - dir_vector , ** eb_quiver_style )
32993302
33003303 errline = art3d .Line3DCollection (np .array (coorderr ).T ,
33013304 ** eb_lines_style )
0 commit comments