@@ -449,6 +449,7 @@ def __init__(self, ax, mappable=None, *, cmap=None,
449
449
self .filled = filled
450
450
self .extendfrac = extendfrac
451
451
self .extendrect = extendrect
452
+ self ._extend_patches = []
452
453
self .solids = None
453
454
self .solids_patches = []
454
455
self .lines = []
@@ -509,6 +510,11 @@ def __init__(self, ax, mappable=None, *, cmap=None,
509
510
setattr (self .ax , x , getattr (self , x ))
510
511
# Set the cla function to the cbar's method to override it
511
512
self .ax .cla = self ._cbar_cla
513
+ # Callbacks for the extend calculations to handle inverting the axis
514
+ self ._extend_cid1 = self .ax .callbacks .connect (
515
+ "xlim_changed" , self ._do_extends )
516
+ self ._extend_cid2 = self .ax .callbacks .connect (
517
+ "ylim_changed" , self ._do_extends )
512
518
513
519
def _cbar_cla (self ):
514
520
"""Function to clear the interactive colorbar state."""
@@ -574,17 +580,20 @@ def draw_all(self):
574
580
# extensions:
575
581
self .vmin , self .vmax = self ._boundaries [self ._inside ][[0 , - 1 ]]
576
582
# Compute the X/Y mesh.
577
- X , Y , extendlen = self ._mesh ()
583
+ X , Y = self ._mesh ()
578
584
# draw the extend triangles, and shrink the inner axes to accommodate.
579
585
# also adds the outline path to self.outline spine:
580
- self ._do_extends (extendlen )
581
-
586
+ self ._do_extends ()
587
+ lower , upper = self .vmin , self .vmax
588
+ if self ._long_axis ().get_inverted ():
589
+ # If the axis is inverted, we need to swap the vmin/vmax
590
+ lower , upper = upper , lower
582
591
if self .orientation == 'vertical' :
583
592
self .ax .set_xlim (0 , 1 )
584
- self .ax .set_ylim (self . vmin , self . vmax )
593
+ self .ax .set_ylim (lower , upper )
585
594
else :
586
595
self .ax .set_ylim (0 , 1 )
587
- self .ax .set_xlim (self . vmin , self . vmax )
596
+ self .ax .set_xlim (lower , upper )
588
597
589
598
# set up the tick locators and formatters. A bit complicated because
590
599
# boundary norms + uniform spacing requires a manual locator.
@@ -637,12 +646,19 @@ def _add_solids_patches(self, X, Y, C, mappable):
637
646
patches .append (patch )
638
647
self .solids_patches = patches
639
648
640
- def _do_extends (self , extendlen ):
649
+ def _do_extends (self , ax = None ):
641
650
"""
642
651
Add the extend tri/rectangles on the outside of the axes.
652
+
653
+ ax is unused, but required due to the callbacks on xlim/ylim changed
643
654
"""
655
+ # Clean up any previous extend patches
656
+ for patch in self ._extend_patches :
657
+ patch .remove ()
658
+ self ._extend_patches = []
644
659
# extend lengths are fraction of the *inner* part of colorbar,
645
660
# not the total colorbar:
661
+ _ , extendlen = self ._proportional_y ()
646
662
bot = 0 - (extendlen [0 ] if self ._extend_lower () else 0 )
647
663
top = 1 + (extendlen [1 ] if self ._extend_upper () else 0 )
648
664
@@ -684,12 +700,17 @@ def _do_extends(self, extendlen):
684
700
if self .orientation == 'horizontal' :
685
701
xy = xy [:, ::- 1 ]
686
702
# add the patch
687
- color = self .cmap (self .norm (self ._values [0 ]))
703
+ val = - 1 if self ._long_axis ().get_inverted () else 0
704
+ color = self .cmap (self .norm (self ._values [val ]))
688
705
patch = mpatches .PathPatch (
689
706
mpath .Path (xy ), facecolor = color , linewidth = 0 ,
690
707
antialiased = False , transform = self .ax .transAxes ,
691
- hatch = hatches [0 ], clip_on = False )
708
+ hatch = hatches [0 ], clip_on = False ,
709
+ # Place it right behind the standard patches, which is
710
+ # needed if we updated the extends
711
+ zorder = np .nextafter (self .ax .patch .zorder , - np .inf ))
692
712
self .ax .add_patch (patch )
713
+ self ._extend_patches .append (patch )
693
714
if self ._extend_upper ():
694
715
if not self .extendrect :
695
716
# triangle
@@ -700,12 +721,17 @@ def _do_extends(self, extendlen):
700
721
if self .orientation == 'horizontal' :
701
722
xy = xy [:, ::- 1 ]
702
723
# add the patch
703
- color = self .cmap (self .norm (self ._values [- 1 ]))
724
+ val = 0 if self ._long_axis ().get_inverted () else - 1
725
+ color = self .cmap (self .norm (self ._values [val ]))
704
726
patch = mpatches .PathPatch (
705
727
mpath .Path (xy ), facecolor = color ,
706
728
linewidth = 0 , antialiased = False ,
707
- transform = self .ax .transAxes , hatch = hatches [- 1 ], clip_on = False )
729
+ transform = self .ax .transAxes , hatch = hatches [- 1 ], clip_on = False ,
730
+ # Place it right behind the standard patches, which is
731
+ # needed if we updated the extends
732
+ zorder = np .nextafter (self .ax .patch .zorder , - np .inf ))
708
733
self .ax .add_patch (patch )
734
+ self ._extend_patches .append (patch )
709
735
return
710
736
711
737
def add_lines (self , * args , ** kwargs ):
@@ -1024,6 +1050,9 @@ def remove(self):
1024
1050
self .mappable .callbacks .disconnect (self .mappable .colorbar_cid )
1025
1051
self .mappable .colorbar = None
1026
1052
self .mappable .colorbar_cid = None
1053
+ # Remove the extension callbacks
1054
+ self .ax .callbacks .disconnect (self ._extend_cid1 )
1055
+ self .ax .callbacks .disconnect (self ._extend_cid2 )
1027
1056
1028
1057
try :
1029
1058
ax = self .mappable .axes
@@ -1133,19 +1162,23 @@ def _mesh(self):
1133
1162
norm = copy .deepcopy (self .norm )
1134
1163
norm .vmin = self .vmin
1135
1164
norm .vmax = self .vmax
1136
- y , extendlen = self ._proportional_y ()
1137
- # invert:
1138
- if (isinstance (norm , (colors .BoundaryNorm , colors .NoNorm )) or
1139
- self .boundaries is not None ):
1140
- y = y * (self .vmax - self .vmin ) + self .vmin # not using a norm.
1165
+ y , _ = self ._proportional_y ()
1166
+ # Use the vmin and vmax of the colorbar, which may not be the same
1167
+ # as the norm. There are situations where the colormap has a
1168
+ # narrower range than the colorbar and we want to accommodate the
1169
+ # extra contours.
1170
+ if (isinstance (norm , (colors .BoundaryNorm , colors .NoNorm ))
1171
+ or self .boundaries is not None ):
1172
+ # not using a norm.
1173
+ y = y * (self .vmax - self .vmin ) + self .vmin
1141
1174
else :
1142
1175
y = norm .inverse (y )
1143
1176
self ._y = y
1144
1177
X , Y = np .meshgrid ([0. , 1. ], y )
1145
1178
if self .orientation == 'vertical' :
1146
- return (X , Y , extendlen )
1179
+ return (X , Y )
1147
1180
else :
1148
- return (Y , X , extendlen )
1181
+ return (Y , X )
1149
1182
1150
1183
def _forward_boundaries (self , x ):
1151
1184
# map boundaries equally between 0 and 1...
@@ -1292,11 +1325,13 @@ def _get_extension_lengths(self, frac, automin, automax, default=0.05):
1292
1325
1293
1326
def _extend_lower (self ):
1294
1327
"""Return whether the lower limit is open ended."""
1295
- return self .extend in ('both' , 'min' )
1328
+ minmax = "max" if self ._long_axis ().get_inverted () else "min"
1329
+ return self .extend in ('both' , minmax )
1296
1330
1297
1331
def _extend_upper (self ):
1298
1332
"""Return whether the upper limit is open ended."""
1299
- return self .extend in ('both' , 'max' )
1333
+ minmax = "min" if self ._long_axis ().get_inverted () else "max"
1334
+ return self .extend in ('both' , minmax )
1300
1335
1301
1336
def _long_axis (self ):
1302
1337
"""Return the long axis"""
0 commit comments