@@ -2997,7 +2997,7 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
2997
2997
barsabove = False , errorevery = 1 , ecolor = None , elinewidth = None ,
2998
2998
capsize = None , capthick = None , xlolims = False , xuplims = False ,
2999
2999
ylolims = False , yuplims = False , zlolims = False , zuplims = False ,
3000
- arrow_length_ratio = .4 , ** kwargs ):
3000
+ ** kwargs ):
3001
3001
"""
3002
3002
Plot lines and/or markers with errorbars around them.
3003
3003
@@ -3074,10 +3074,6 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
3074
3074
Used to avoid overlapping error bars when two series share x-axis
3075
3075
values.
3076
3076
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
-
3081
3077
Returns
3082
3078
-------
3083
3079
errlines : list
@@ -3183,7 +3179,7 @@ def errorbar(self, x, y, z, zerr=None, yerr=None, xerr=None, fmt='',
3183
3179
eb_lines_style [key ] = kwargs [key ]
3184
3180
3185
3181
# Make the style dict for caps (the "hats").
3186
- eb_cap_style = {** base_style , 'linestyle' : 'none ' }
3182
+ eb_cap_style = {** base_style , 'linestyle' : 'None ' }
3187
3183
if capsize is None :
3188
3184
capsize = rcParams ["errorbar.capsize" ]
3189
3185
if capsize > 0 :
@@ -3207,14 +3203,8 @@ def _extract_errs(err, data, lomask, himask):
3207
3203
else :
3208
3204
low_err , high_err = err , err
3209
3205
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 )
3218
3208
3219
3209
return lows , highs
3220
3210
@@ -3229,6 +3219,33 @@ def _extract_errs(err, data, lomask, himask):
3229
3219
capmarker = {0 : '|' , 1 : '|' , 2 : '_' }
3230
3220
i_xyz = {'x' : 0 , 'y' : 1 , 'z' : 2 }
3231
3221
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
+
3232
3249
# loop over x-, y-, and z-direction and draw relevant elements
3233
3250
for zdir , data , err , lolims , uplims in zip (
3234
3251
['x' , 'y' , 'z' ], [x , y , z ], [xerr , yerr , zerr ],
@@ -3249,18 +3266,16 @@ def _extract_errs(err, data, lomask, himask):
3249
3266
lolims = np .broadcast_to (lolims , len (data )).astype (bool )
3250
3267
uplims = np .broadcast_to (uplims , len (data )).astype (bool )
3251
3268
3252
- nolims = ~ (lolims | uplims )
3253
-
3254
3269
# a nested list structure that expands to (xl,xh),(yl,yh),(zl,zh),
3255
3270
# where x/y/z and l/h correspond to dimensions and low/high
3256
3271
# positions of errorbars in a dimension we're looping over
3257
3272
coorderr = [
3258
- _extract_errs (err * dir_vector [i ], coord ,
3259
- ~ lolims & everymask , ~ uplims & everymask )
3273
+ _extract_errs (err * dir_vector [i ], coord , lolims , uplims )
3260
3274
for i , coord in enumerate ([x , y , z ])]
3261
3275
(xl , xh ), (yl , yh ), (zl , zh ) = coorderr
3262
3276
3263
3277
# draws capmarkers - flat caps orthogonal to the error bars
3278
+ nolims = ~ (lolims | uplims )
3264
3279
if nolims .any () and capsize > 0 :
3265
3280
lo_caps_xyz = _apply_mask ([xl , yl , zl ], nolims & everymask )
3266
3281
hi_caps_xyz = _apply_mask ([xh , yh , zh ], nolims & everymask )
@@ -3278,24 +3293,12 @@ def _extract_errs(err, data, lomask, himask):
3278
3293
caplines .append (cap_lo )
3279
3294
caplines .append (cap_hi )
3280
3295
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 )
3299
3302
3300
3303
errline = art3d .Line3DCollection (np .array (coorderr ).T ,
3301
3304
** eb_lines_style )
0 commit comments