@@ -1521,7 +1521,14 @@ def show(
15211521 outline_legend_title = outline_legend_title ,
15221522 )
15231523
1524- def _draw_colorbar (spec : ColorbarSpec , fig : Figure , divider : AxesDivider , side_counts : dict [str , int ]) -> None :
1524+ def _draw_colorbar (
1525+ spec : ColorbarSpec ,
1526+ fig : Figure ,
1527+ divider : AxesDivider ,
1528+ side_counts : dict [str , int ],
1529+ clearance : dict [str , float ],
1530+ axes_size_in : tuple [float , float ],
1531+ ) -> None :
15251532 norm = spec .mappable .norm
15261533 if isinstance (norm , LogNorm ):
15271534 vmin , vmax = norm .vmin , norm .vmax
@@ -1555,16 +1562,17 @@ def _draw_colorbar(spec: ColorbarSpec, fig: Figure, divider: AxesDivider, side_c
15551562
15561563 # Append the colorbar axes through the panel's shared divider. This steals space from the
15571564 # panel (so the colorbar matches the equal-aspect plot's extent) and keeps it inside the
1558- # panel's grid cell, never overflowing into a neighbouring panel. Repeated appends on the
1559- # same divider stack additional colorbars on that side; the first sits next to the plot
1560- # with the requested (relative) pad, while each subsequent one uses an absolute pad wide
1561- # enough to clear the previous colorbar's tick labels instead of overlapping them.
1565+ # panel's grid cell, never overflowing into a neighbouring panel. The first colorbar on a
1566+ # side is padded past the panel's own decorations (ticks/labels/title) plus the requested
1567+ # pad; each subsequent one uses an absolute pad wide enough to clear the previous
1568+ # colorbar's tick labels instead of overlapping them.
15621569 n_on_side = side_counts .get (location , 0 )
1563- pad_arg : str | Fixed = Fixed (CBAR_STACK_PAD_INCHES ) if n_on_side else f"{ max (pad , 0.0 ) * 100 } %"
1570+ ref_in = axes_size_in [0 ] if location in {"left" , "right" } else axes_size_in [1 ]
1571+ pad_in = CBAR_STACK_PAD_INCHES if n_on_side else clearance .get (location , 0.0 ) + max (pad , 0.0 ) * ref_in
15641572 cax = divider .append_axes (
15651573 location ,
1566- size = f" { fraction * 100 } %" ,
1567- pad = pad_arg ,
1574+ size = Fixed ( max ( fraction , 0.0 ) * ref_in ) ,
1575+ pad = Fixed ( pad_in ) ,
15681576 axes_class = Axes ,
15691577 )
15701578 side_counts [location ] = n_on_side + 1
@@ -1779,6 +1787,9 @@ def _draw_colorbar(spec: ColorbarSpec, fig: Figure, divider: AxesDivider, side_c
17791787
17801788 if pending_colorbars and fig_params .fig is not None :
17811789 fig = fig_params .fig
1790+ fig .canvas .draw ()
1791+ renderer = fig .canvas .get_renderer ()
1792+ dpi = fig .get_dpi ()
17821793 for axis , requests in pending_colorbars :
17831794 unique_specs : list [ColorbarSpec ] = []
17841795 seen_mappables : set [int ] = set ()
@@ -1788,11 +1799,24 @@ def _draw_colorbar(spec: ColorbarSpec, fig: Figure, divider: AxesDivider, side_c
17881799 continue
17891800 seen_mappables .add (mappable_id )
17901801 unique_specs .append (spec )
1802+ # Clearance (inches) that the panel's own ticks, tick labels and title extend beyond
1803+ # the axes box on each side. The first colorbar on a side is padded past this so it
1804+ # clears those decorations instead of overlapping them (the divider only knows about
1805+ # the axes box, not its decorations).
1806+ box = axis .get_window_extent (renderer )
1807+ tight = axis .get_tightbbox (renderer )
1808+ clearance = {
1809+ "left" : max (0.0 , (box .x0 - tight .x0 ) / dpi ),
1810+ "right" : max (0.0 , (tight .x1 - box .x1 ) / dpi ),
1811+ "bottom" : max (0.0 , (box .y0 - tight .y0 ) / dpi ),
1812+ "top" : max (0.0 , (tight .y1 - box .y1 ) / dpi ),
1813+ }
1814+ axes_size_in = (box .width / dpi , box .height / dpi )
17911815 # One divider per panel so multiple colorbars on the same panel stack via the divider.
17921816 divider = make_axes_locatable (axis )
17931817 side_counts : dict [str , int ] = {}
17941818 for spec in unique_specs :
1795- _draw_colorbar (spec , fig , divider , side_counts )
1819+ _draw_colorbar (spec , fig , divider , side_counts , clearance , axes_size_in )
17961820
17971821 if fig_params .fig is not None and save is not None :
17981822 save_fig (fig_params .fig , path = save )
0 commit comments