55
55
_log = logging .getLogger (__name__ )
56
56
57
57
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
66
60
67
61
68
62
def _axes_all_finite_sized (fig ):
@@ -155,7 +149,7 @@ def do_constrained_layout(fig, renderer, h_pad, w_pad,
155
149
# fill in any empty gridspec slots w/ ghost axes...
156
150
_make_ghost_gridspec_slots (fig , gs )
157
151
158
- for nnn in range (2 ):
152
+ for _ in range (2 ):
159
153
# do the algorithm twice. This has to be done because decorators
160
154
# change size after the first re-position (i.e. x/yticklabels get
161
155
# larger/smaller). This second reposition tends to be much milder,
@@ -329,131 +323,109 @@ def _align_spines(fig, gs):
329
323
if (hasattr (ax , 'get_subplotspec' )
330
324
and ax ._layoutbox is not None
331
325
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 ))
326
+ rowspans = []
327
+ colspans = []
328
+ widths = []
329
+ heights = []
338
330
339
331
for n , ax in enumerate (axs ):
340
332
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
+ widths .append (sum (width_ratios [colspan .start :colspan .stop ]))
338
+ heights .append (sum (height_ratios [rowspan .start :rowspan .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
+ width0 , height0 = widths [idx0 ], heights [idx0 ]
357
347
alignleft = False
358
348
alignright = False
359
349
alignbot = False
360
350
aligntop = False
361
351
alignheight = False
362
352
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 ],
353
+ for idx1 in range (idx0 + 1 , len (axs )):
354
+ ax1 = axs [idx1 ]
355
+ rowspan1 = rowspans [idx1 ]
356
+ colspan1 = colspans [idx1 ]
357
+ width1 = widths [idx1 ]
358
+ height1 = heights [idx1 ]
359
+ # Horizontally align axes spines if they have the same min or max:
360
+ if not alignleft and colspan0 .start == colspan1 .start :
361
+ _log .debug ('same start columns; line up layoutbox lefts' )
362
+ layoutbox .align ([ax0 ._poslayoutbox , ax1 ._poslayoutbox ],
374
363
'left' )
375
364
alignleft = True
376
- if not alignright and colnum0max == colnumCmax :
377
- # line up right sides of _poslayoutbox
378
- layoutbox .align ([ax ._poslayoutbox , axc ._poslayoutbox ],
365
+ if not alignright and colspan0 . stop == colspan1 . stop :
366
+ _log . debug ( 'same stop columns; line up layoutbox rights' )
367
+ layoutbox .align ([ax0 ._poslayoutbox , ax1 ._poslayoutbox ],
379
368
'right' )
380
369
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 ],
370
+ # Vertically align axes spines if they have the same min or max:
371
+ if not aligntop and rowspan0 .start == rowspan1 .start :
372
+ _log .debug ('same start rows; line up layoutbox tops' )
373
+ layoutbox .align ([ax0 ._poslayoutbox , ax1 ._poslayoutbox ],
387
374
'top' )
388
375
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 ],
376
+ if not alignbot and rowspan0 .stop == rowspan1 .stop :
377
+ _log .debug ('same stop rows; line up layoutbox bottoms' )
378
+ layoutbox .align ([ax0 ._poslayoutbox , ax1 ._poslayoutbox ],
393
379
'bottom' )
394
380
alignbot = True
395
- ###########
381
+
396
382
# Now we make the widths and heights of position boxes
397
383
# 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.
384
+ # This allows vertically stacked subplots to have different sizes
385
+ # if they occupy different amounts of the gridspec: i.e.
401
386
# gs = gridspec.GridSpec(3, 1)
402
387
# ax1 = gs[0, :]
403
388
# ax2 = gs[1:, :]
404
389
# then drows0 = 1, and drowsC = 2, and ax2
405
390
# should be at least twice as large as ax1.
406
391
# But it can be more than twice as large because
407
392
# 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 )
393
+
394
+ # For height, this only needs to be done if the subplots share a
395
+ # column.
396
+ if not alignheight and len (rowspan0 ) == len (rowspan1 ):
397
+ ax0 ._poslayoutbox .constrain_height (
398
+ ax1 ._poslayoutbox .height * height0 / height1 )
421
399
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 )
427
404
# these constraints stop the smaller axes from
428
405
# 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 width, if they share a row.
414
+ if not alignwidth and len (colspan0 ) == len (colspan1 ):
415
+ ax0 ._poslayoutbox .constrain_width (
416
+ ax1 ._poslayoutbox .width * width0 / width1 )
442
417
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 ))
457
429
458
430
459
431
def _arrange_subplotspecs (gs , hspace = 0 , wspace = 0 ):
@@ -470,34 +442,25 @@ def _arrange_subplotspecs(gs, hspace=0, wspace=0):
470
442
for child0 in sschildren :
471
443
ss0 = child0 .artist
472
444
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
475
447
sschildren = sschildren [1 :]
476
448
for childc in sschildren :
477
449
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
- ####
450
+ rowspanC = ssc .rowspan
451
+ colspanC = ssc .colspan
452
+ # OK, this tells us the relative layout of ax with axc
453
+ pad = wspace / ncols
454
+ if colspan0 .stop <= colspanC .start :
455
+ layoutbox .hstack ([ss0 ._layoutbox , ssc ._layoutbox ], padding = pad )
456
+ if colspanC .stop <= colspan0 .start :
457
+ layoutbox .hstack ([ssc ._layoutbox , ss0 ._layoutbox ], padding = pad )
491
458
# 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 <= rowspanC .start :
461
+ layoutbox .vstack ([ss0 ._layoutbox , ssc ._layoutbox ], padding = pad )
462
+ if rowspanC .stop <= rowspan0 .start :
463
+ layoutbox .vstack ([ssc ._layoutbox , ss0 ._layoutbox ], padding = pad )
501
464
502
465
503
466
def layoutcolorbarsingle (ax , cax , shrink , aspect , location , pad = 0.05 ):
@@ -561,32 +524,21 @@ def layoutcolorbarsingle(ax, cax, shrink, aspect, location, pad=0.05):
561
524
562
525
def _getmaxminrowcolumn (axs ):
563
526
# 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
527
573
- for ax in axs :
574
- 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 )
528
+ def key (pos_ax ):
529
+ pos , ax = pos_ax
530
+ return pos
531
+
532
+ minrow , minrow_ax = min (
533
+ ((ax .get_subplotspec ().rowspan .start , ax ) for ax in axs ), key = key )
534
+ maxrow , maxrow_ax = max (
535
+ ((ax .get_subplotspec ().rowspan .stop - 1 , ax ) for ax in axs ), key = key )
536
+ mincol , mincol_ax = min (
537
+ ((ax .get_subplotspec ().colspan .start , ax ) for ax in axs ), key = key )
538
+ maxcol , maxcol_ax = max (
539
+ ((ax .get_subplotspec ().colspan .stop - 1 , ax ) for ax in axs ), key = key )
540
+ return (minrow , maxrow , minrow_ax , maxrow_ax ,
541
+ mincol , maxcol , mincol_ax , maxcol_ax )
590
542
591
543
592
544
def layoutcolorbargridspec (parents , cax , shrink , aspect , location , pad = 0.05 ):
@@ -630,18 +582,16 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
630
582
# Horizontal Layout: need to check all the axes in this gridspec
631
583
for ch in gslb .children :
632
584
subspec = ch .artist
633
- nrows , ncols , row_start , row_stop , col_start , col_stop = \
634
- subspec .get_rows_columns ()
635
585
if location == 'right' :
636
- if col_stop <= maxcol :
586
+ if subspec . colspan . stop - 1 <= maxcol :
637
587
order = [subspec ._layoutbox , lb ]
638
588
# arrange to right of the parents
639
- if col_start > maxcol :
589
+ elif subspec . colspan . start > maxcol :
640
590
order = [lb , subspec ._layoutbox ]
641
591
elif location == 'left' :
642
- if col_start >= mincol :
592
+ if subspec . colspan . start >= mincol :
643
593
order = [lb , subspec ._layoutbox ]
644
- if col_stop < mincol :
594
+ elif subspec . colspan . stop - 1 < mincol :
645
595
order = [subspec ._layoutbox , lb ]
646
596
layoutbox .hstack (order , padding = pad * gslb .width ,
647
597
strength = 'strong' )
@@ -686,17 +636,15 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
686
636
# Vertical Layout: need to check all the axes in this gridspec
687
637
for ch in gslb .children :
688
638
subspec = ch .artist
689
- nrows , ncols , row_start , row_stop , col_start , col_stop = \
690
- subspec .get_rows_columns ()
691
639
if location == 'bottom' :
692
- if row_stop <= minrow :
640
+ if subspec . rowspan . stop - 1 <= minrow :
693
641
order = [subspec ._layoutbox , lb ]
694
- if row_start > maxrow :
642
+ elif subspec . rowspan . start > maxrow :
695
643
order = [lb , subspec ._layoutbox ]
696
644
elif location == 'top' :
697
- if row_stop < minrow :
645
+ if subspec . rowspan . stop - 1 < minrow :
698
646
order = [subspec ._layoutbox , lb ]
699
- if row_start >= maxrow :
647
+ elif subspec . rowspan . start >= maxrow :
700
648
order = [lb , subspec ._layoutbox ]
701
649
layoutbox .vstack (order , padding = pad * gslb .width ,
702
650
strength = 'strong' )
0 commit comments