@@ -869,6 +869,7 @@ def __init__(
869869
870870 @override
871871 def draw (self , renderer ):
872+ self ._snap_axes_to_pixel_grid (renderer )
872873 # implement the tick sharing here
873874 # should be shareable --> either all cartesian or all geographic
874875 # but no mixing (panels can be mixed)
@@ -880,6 +881,53 @@ def draw(self, renderer):
880881 self ._apply_share_label_groups ()
881882 super ().draw (renderer )
882883
884+ def _snap_axes_to_pixel_grid (self , renderer ) -> None :
885+ """
886+ Snap visible axes bounds to the renderer pixel grid.
887+ """
888+ if not rc .find ("subplots.pixelsnap" , context = True ):
889+ return
890+
891+ width = getattr (renderer , "width" , None )
892+ height = getattr (renderer , "height" , None )
893+ if not width or not height :
894+ return
895+
896+ width = float (width )
897+ height = float (height )
898+ if width <= 0 or height <= 0 :
899+ return
900+
901+ invw = 1.0 / width
902+ invh = 1.0 / height
903+ minw = invw
904+ minh = invh
905+
906+ for ax in self ._iter_axes (hidden = False , children = False , panels = True ):
907+ bbox = ax .get_position (original = False )
908+ old = np .array ([bbox .x0 , bbox .y0 , bbox .x1 , bbox .y1 ], dtype = float )
909+ new = np .array (
910+ [
911+ round (old [0 ] * width ) * invw ,
912+ round (old [1 ] * height ) * invh ,
913+ round (old [2 ] * width ) * invw ,
914+ round (old [3 ] * height ) * invh ,
915+ ],
916+ dtype = float ,
917+ )
918+
919+ if new [2 ] <= new [0 ]:
920+ new [2 ] = new [0 ] + minw
921+ if new [3 ] <= new [1 ]:
922+ new [3 ] = new [1 ] + minh
923+
924+ if np .allclose (new , old , rtol = 0.0 , atol = 1e-12 ):
925+ continue
926+ ax .set_position (
927+ [new [0 ], new [1 ], new [2 ] - new [0 ], new [3 ] - new [1 ]],
928+ which = "both" ,
929+ )
930+
883931 def _share_ticklabels (self , * , axis : str ) -> None :
884932 """
885933 Tick label sharing is determined at the figure level. While
0 commit comments