5555_log = logging .getLogger (__name__ )
5656
5757
58- def _in_same_column (colnum0min , colnum0max , colnumCmin , colnumCmax ):
59- return (colnumCmin <= colnum0min <= colnumCmax
60- or colnumCmin <= colnum0max <= colnumCmax )
61-
62-
63- def _in_same_row (rownum0min , rownum0max , rownumCmin , rownumCmax ):
64- return (rownumCmin <= rownum0min <= rownumCmax
65- or rownumCmin <= rownum0max <= rownumCmax )
58+ def _spans_overlap (span0 , span1 ):
59+ return span0 .start in span1 or span1 .start in span0
6660
6761
6862def _axes_all_finite_sized (fig ):
@@ -155,7 +149,7 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad,
155149 # fill in any empty gridspec slots w/ ghost axes...
156150 _make_ghost_gridspec_slots (fig , gs )
157151
158- for nnn in range (2 ):
152+ for _ in range (2 ):
159153 # do the algorithm twice. This has to be done because decorators
160154 # change size after the first re-position (i.e. x/yticklabels get
161155 # larger/smaller). This second reposition tends to be much milder,
@@ -329,131 +323,109 @@ def _align_spines(fig, gs):
329323 if (hasattr (ax , 'get_subplotspec' )
330324 and ax ._layoutbox is not None
331325 and ax .get_subplotspec ().get_gridspec () == gs )]
332- rownummin = np .zeros (len (axs ), dtype = np .int8 )
333- rownummax = np .zeros (len (axs ), dtype = np .int8 )
334- colnummin = np .zeros (len (axs ), dtype = np .int8 )
335- colnummax = np .zeros (len (axs ), dtype = np .int8 )
336- width = np .zeros (len (axs ))
337- height = np .zeros (len (axs ))
338-
339- for n , ax in enumerate (axs ):
326+ rowspans = []
327+ colspans = []
328+ heights = []
329+ widths = []
330+
331+ for ax in axs :
340332 ss0 = ax .get_subplotspec ()
341- rownummin [n ], colnummin [n ] = divmod (ss0 .num1 , ncols )
342- rownummax [n ], colnummax [n ] = divmod (ss0 .num2 , ncols )
343- width [n ] = np .sum (
344- width_ratios [colnummin [n ]:(colnummax [n ] + 1 )])
345- height [n ] = np .sum (
346- height_ratios [rownummin [n ]:(rownummax [n ] + 1 )])
347-
348- for nn , ax in enumerate (axs [:- 1 ]):
349- # now compare ax to all the axs:
350- #
351- # If the subplotspecs have the same colnumXmax, then line
352- # up their right sides. If they have the same min, then
353- # line up their left sides (and vertical equivalents).
354- rownum0min , colnum0min = rownummin [nn ], colnummin [nn ]
355- rownum0max , colnum0max = rownummax [nn ], colnummax [nn ]
356- width0 , height0 = width [nn ], height [nn ]
333+ rowspan = ss0 .rowspan
334+ colspan = ss0 .colspan
335+ rowspans .append (rowspan )
336+ colspans .append (colspan )
337+ heights .append (sum (height_ratios [rowspan .start :rowspan .stop ]))
338+ widths .append (sum (width_ratios [colspan .start :colspan .stop ]))
339+
340+ for idx0 , ax0 in enumerate (axs ):
341+ # Compare ax to all other axs: If the subplotspecs start (/stop) at
342+ # the same column, then line up their left (/right) sides; likewise
343+ # for rows/top/bottom.
344+ rowspan0 = rowspans [idx0 ]
345+ colspan0 = colspans [idx0 ]
346+ height0 = heights [idx0 ]
347+ width0 = widths [idx0 ]
357348 alignleft = False
358349 alignright = False
359350 alignbot = False
360351 aligntop = False
361352 alignheight = False
362353 alignwidth = False
363- for mm in range (nn + 1 , len (axs )):
364- axc = axs [mm ]
365- rownumCmin , colnumCmin = rownummin [mm ], colnummin [mm ]
366- rownumCmax , colnumCmax = rownummax [mm ], colnummax [mm ]
367- widthC , heightC = width [mm ], height [mm ]
368- # Horizontally align axes spines if they have the
369- # same min or max:
370- if not alignleft and colnum0min == colnumCmin :
371- # we want the _poslayoutboxes to line up on left
372- # side of the axes spines...
373- layoutbox .align ([ax ._poslayoutbox , axc ._poslayoutbox ],
354+ for idx1 in range (idx0 + 1 , len (axs )):
355+ ax1 = axs [idx1 ]
356+ rowspan1 = rowspans [idx1 ]
357+ colspan1 = colspans [idx1 ]
358+ width1 = widths [idx1 ]
359+ height1 = heights [idx1 ]
360+ # Horizontally align axes spines if they have the same min or max:
361+ if not alignleft and colspan0 .start == colspan1 .start :
362+ _log .debug ('same start columns; line up layoutbox lefts' )
363+ layoutbox .align ([ax0 ._poslayoutbox , ax1 ._poslayoutbox ],
374364 'left' )
375365 alignleft = True
376- if not alignright and colnum0max == colnumCmax :
377- # line up right sides of _poslayoutbox
378- layoutbox .align ([ax ._poslayoutbox , axc ._poslayoutbox ],
366+ if not alignright and colspan0 . stop == colspan1 . stop :
367+ _log . debug ( 'same stop columns; line up layoutbox rights' )
368+ layoutbox .align ([ax0 ._poslayoutbox , ax1 ._poslayoutbox ],
379369 'right' )
380370 alignright = True
381- # Vertically align axes spines if they have the
382- # same min or max:
383- if not aligntop and rownum0min == rownumCmin :
384- # line up top of _poslayoutbox
385- _log .debug ('rownum0min == rownumCmin' )
386- layoutbox .align ([ax ._poslayoutbox , axc ._poslayoutbox ],
371+ # Vertically align axes spines if they have the same min or max:
372+ if not aligntop and rowspan0 .start == rowspan1 .start :
373+ _log .debug ('same start rows; line up layoutbox tops' )
374+ layoutbox .align ([ax0 ._poslayoutbox , ax1 ._poslayoutbox ],
387375 'top' )
388376 aligntop = True
389- if not alignbot and rownum0max == rownumCmax :
390- # line up bottom of _poslayoutbox
391- _log .debug ('rownum0max == rownumCmax' )
392- layoutbox .align ([ax ._poslayoutbox , axc ._poslayoutbox ],
377+ if not alignbot and rowspan0 .stop == rowspan1 .stop :
378+ _log .debug ('same stop rows; line up layoutbox bottoms' )
379+ layoutbox .align ([ax0 ._poslayoutbox , ax1 ._poslayoutbox ],
393380 'bottom' )
394381 alignbot = True
395- ###########
382+
396383 # Now we make the widths and heights of position boxes
397384 # similar. (i.e the spine locations)
398- # This allows vertically stacked subplots to have
399- # different sizes if they occupy different amounts
400- # of the gridspec: i.e.
401- # gs = gridspec.GridSpec(3, 1)
402- # ax1 = gs[0, :]
403- # ax2 = gs[1:, :]
404- # then drows0 = 1, and drowsC = 2, and ax2
405- # should be at least twice as large as ax1.
385+ # This allows vertically stacked subplots to have different sizes
386+ # if they occupy different amounts of the gridspec, e.g. if
387+ # gs = gridspec.GridSpec(3, 1)
388+ # ax0 = gs[0, :]
389+ # ax1 = gs[1:, :]
390+ # then len(rowspan0) = 1, and len(rowspan1) = 2,
391+ # and ax1 should be at least twice as large as ax0.
406392 # But it can be more than twice as large because
407393 # it needs less room for the labeling.
408- #
409- # For height, this only needs to be done if the
410- # subplots share a column. For width if they
411- # share a row.
412-
413- drowsC = (rownumCmax - rownumCmin + 1 )
414- drows0 = (rownum0max - rownum0min + 1 )
415- dcolsC = (colnumCmax - colnumCmin + 1 )
416- dcols0 = (colnum0max - colnum0min + 1 )
417-
418- if not alignheight and drows0 == drowsC :
419- ax ._poslayoutbox .constrain_height (
420- axc ._poslayoutbox .height * height0 / heightC )
394+
395+ # For heights, do it if the subplots share a column.
396+ if not alignheight and len (rowspan0 ) == len (rowspan1 ):
397+ ax0 ._poslayoutbox .constrain_height (
398+ ax1 ._poslayoutbox .height * height0 / height1 )
421399 alignheight = True
422- elif _in_same_column (colnum0min , colnum0max ,
423- colnumCmin , colnumCmax ):
424- if height0 > heightC :
425- ax ._poslayoutbox .constrain_height_min (
426- axc ._poslayoutbox .height * height0 / heightC )
400+ elif _spans_overlap (colspan0 , colspan1 ):
401+ if height0 > height1 :
402+ ax0 ._poslayoutbox .constrain_height_min (
403+ ax1 ._poslayoutbox .height * height0 / height1 )
427404 # these constraints stop the smaller axes from
428405 # being allowed to go to zero height...
429- axc ._poslayoutbox .constrain_height_min (
430- ax ._poslayoutbox .height * heightC /
431- (height0 * 1.8 ))
432- elif height0 < heightC :
433- axc ._poslayoutbox .constrain_height_min (
434- ax ._poslayoutbox .height * heightC / height0 )
435- ax ._poslayoutbox .constrain_height_min (
436- ax ._poslayoutbox .height * height0 /
437- (heightC * 1.8 ))
438- # widths...
439- if not alignwidth and dcols0 == dcolsC :
440- ax ._poslayoutbox .constrain_width (
441- axc ._poslayoutbox .width * width0 / widthC )
406+ ax1 ._poslayoutbox .constrain_height_min (
407+ ax0 ._poslayoutbox .height * height1 / (height0 * 1.8 ))
408+ elif height0 < height1 :
409+ ax1 ._poslayoutbox .constrain_height_min (
410+ ax0 ._poslayoutbox .height * height1 / height0 )
411+ ax0 ._poslayoutbox .constrain_height_min (
412+ ax0 ._poslayoutbox .height * height0 / (height1 * 1.8 ))
413+ # For widths, do it if the subplots share a row.
414+ if not alignwidth and len (colspan0 ) == len (colspan1 ):
415+ ax0 ._poslayoutbox .constrain_width (
416+ ax1 ._poslayoutbox .width * width0 / width1 )
442417 alignwidth = True
443- elif _in_same_row (rownum0min , rownum0max ,
444- rownumCmin , rownumCmax ):
445- if width0 > widthC :
446- ax ._poslayoutbox .constrain_width_min (
447- axc ._poslayoutbox .width * width0 / widthC )
448- axc ._poslayoutbox .constrain_width_min (
449- ax ._poslayoutbox .width * widthC /
450- (width0 * 1.8 ))
451- elif width0 < widthC :
452- axc ._poslayoutbox .constrain_width_min (
453- ax ._poslayoutbox .width * widthC / width0 )
454- ax ._poslayoutbox .constrain_width_min (
455- axc ._poslayoutbox .width * width0 /
456- (widthC * 1.8 ))
418+ elif _spans_overlap (rowspan0 , rowspan1 ):
419+ if width0 > width1 :
420+ ax0 ._poslayoutbox .constrain_width_min (
421+ ax1 ._poslayoutbox .width * width0 / width1 )
422+ ax1 ._poslayoutbox .constrain_width_min (
423+ ax0 ._poslayoutbox .width * width1 / (width0 * 1.8 ))
424+ elif width0 < width1 :
425+ ax1 ._poslayoutbox .constrain_width_min (
426+ ax0 ._poslayoutbox .width * width1 / width0 )
427+ ax0 ._poslayoutbox .constrain_width_min (
428+ ax1 ._poslayoutbox .width * width0 / (width1 * 1.8 ))
457429
458430
459431def _arrange_subplotspecs (gs , hspace = 0 , wspace = 0 ):
@@ -470,34 +442,25 @@ def _arrange_subplotspecs(gs, hspace=0, wspace=0):
470442 for child0 in sschildren :
471443 ss0 = child0 .artist
472444 nrows , ncols = ss0 .get_gridspec ().get_geometry ()
473- rowNum0min , colNum0min = divmod ( ss0 .num1 , ncols )
474- rowNum0max , colNum0max = divmod ( ss0 .num2 , ncols )
445+ rowspan0 = ss0 .rowspan
446+ colspan0 = ss0 .colspan
475447 sschildren = sschildren [1 :]
476- for childc in sschildren :
477- ssc = childc .artist
478- rowNumCmin , colNumCmin = divmod (ssc .num1 , ncols )
479- rowNumCmax , colNumCmax = divmod (ssc .num2 , ncols )
480- # OK, this tells us the relative layout of ax
481- # with axc
482- thepad = wspace / ncols
483- if colNum0max < colNumCmin :
484- layoutbox .hstack ([ss0 ._layoutbox , ssc ._layoutbox ],
485- padding = thepad )
486- if colNumCmax < colNum0min :
487- layoutbox .hstack ([ssc ._layoutbox , ss0 ._layoutbox ],
488- padding = thepad )
489-
490- ####
448+ for child1 in sschildren :
449+ ss1 = child1 .artist
450+ rowspan1 = ss1 .rowspan
451+ colspan1 = ss1 .colspan
452+ # OK, this tells us the relative layout of child0 with child1.
453+ pad = wspace / ncols
454+ if colspan0 .stop <= colspan1 .start :
455+ layoutbox .hstack ([ss0 ._layoutbox , ss1 ._layoutbox ], padding = pad )
456+ if colspan1 .stop <= colspan0 .start :
457+ layoutbox .hstack ([ss1 ._layoutbox , ss0 ._layoutbox ], padding = pad )
491458 # vertical alignment
492- thepad = hspace / nrows
493- if rowNum0max < rowNumCmin :
494- layoutbox .vstack ([ss0 ._layoutbox ,
495- ssc ._layoutbox ],
496- padding = thepad )
497- if rowNumCmax < rowNum0min :
498- layoutbox .vstack ([ssc ._layoutbox ,
499- ss0 ._layoutbox ],
500- padding = thepad )
459+ pad = hspace / nrows
460+ if rowspan0 .stop <= rowspan1 .start :
461+ layoutbox .vstack ([ss0 ._layoutbox , ss1 ._layoutbox ], padding = pad )
462+ if rowspan1 .stop <= rowspan0 .start :
463+ layoutbox .vstack ([ss1 ._layoutbox , ss0 ._layoutbox ], padding = pad )
501464
502465
503466def layoutcolorbarsingle (ax , cax , shrink , aspect , location , pad = 0.05 ):
@@ -560,33 +523,28 @@ def layoutcolorbarsingle(ax, cax, shrink, aspect, location, pad=0.05):
560523
561524
562525def _getmaxminrowcolumn (axs ):
563- # helper to get the min/max rows and columns of a list of axes.
564- maxrow = - 100000
565- minrow = 1000000
566- maxax = None
567- minax = None
568- maxcol = - 100000
569- mincol = 1000000
570- maxax_col = None
571- minax_col = None
572-
526+ """
527+ Find axes covering the first and last rows and columns of a list of axes.
528+ """
529+ startrow = startcol = np .inf
530+ stoprow = stopcol = - np .inf
531+ startax_row = startax_col = stopax_row = stopax_col = None
573532 for ax in axs :
574533 subspec = ax .get_subplotspec ()
575- nrows , ncols , row_start , row_stop , col_start , col_stop = \
576- subspec .get_rows_columns ()
577- if row_stop > maxrow :
578- maxrow = row_stop
579- maxax = ax
580- if row_start < minrow :
581- minrow = row_start
582- minax = ax
583- if col_stop > maxcol :
584- maxcol = col_stop
585- maxax_col = ax
586- if col_start < mincol :
587- mincol = col_start
588- minax_col = ax
589- return (minrow , maxrow , minax , maxax , mincol , maxcol , minax_col , maxax_col )
534+ if subspec .rowspan .start < startrow :
535+ startrow = subspec .rowspan .start
536+ startax_row = ax
537+ if subspec .rowspan .stop > stoprow :
538+ stoprow = subspec .rowspan .stop
539+ stopax_row = ax
540+ if subspec .colspan .start < startcol :
541+ startcol = subspec .colspan .start
542+ startax_col = ax
543+ if subspec .colspan .stop > stopcol :
544+ stopcol = subspec .colspan .stop
545+ stopax_col = ax
546+ return (startrow , stoprow - 1 , startax_row , stopax_row ,
547+ startcol , stopcol - 1 , startax_col , stopax_col )
590548
591549
592550def layoutcolorbargridspec (parents , cax , shrink , aspect , location , pad = 0.05 ):
@@ -630,18 +588,16 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
630588 # Horizontal Layout: need to check all the axes in this gridspec
631589 for ch in gslb .children :
632590 subspec = ch .artist
633- nrows , ncols , row_start , row_stop , col_start , col_stop = \
634- subspec .get_rows_columns ()
635591 if location == 'right' :
636- if col_stop <= maxcol :
592+ if subspec . colspan . stop - 1 <= maxcol :
637593 order = [subspec ._layoutbox , lb ]
638594 # arrange to right of the parents
639- if col_start > maxcol :
595+ elif subspec . colspan . start > maxcol :
640596 order = [lb , subspec ._layoutbox ]
641597 elif location == 'left' :
642- if col_start >= mincol :
598+ if subspec . colspan . start >= mincol :
643599 order = [lb , subspec ._layoutbox ]
644- if col_stop < mincol :
600+ elif subspec . colspan . stop - 1 < mincol :
645601 order = [subspec ._layoutbox , lb ]
646602 layoutbox .hstack (order , padding = pad * gslb .width ,
647603 strength = 'strong' )
@@ -686,17 +642,15 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
686642 # Vertical Layout: need to check all the axes in this gridspec
687643 for ch in gslb .children :
688644 subspec = ch .artist
689- nrows , ncols , row_start , row_stop , col_start , col_stop = \
690- subspec .get_rows_columns ()
691645 if location == 'bottom' :
692- if row_stop <= minrow :
646+ if subspec . rowspan . stop - 1 <= minrow :
693647 order = [subspec ._layoutbox , lb ]
694- if row_start > maxrow :
648+ elif subspec . rowspan . start > maxrow :
695649 order = [lb , subspec ._layoutbox ]
696650 elif location == 'top' :
697- if row_stop < minrow :
651+ if subspec . rowspan . stop - 1 < minrow :
698652 order = [subspec ._layoutbox , lb ]
699- if row_start >= maxrow :
653+ elif subspec . rowspan . start >= maxrow :
700654 order = [lb , subspec ._layoutbox ]
701655 layoutbox .vstack (order , padding = pad * gslb .width ,
702656 strength = 'strong' )
0 commit comments