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 ))
338
-
339
- for n , ax in enumerate (axs ):
326
+ rowspans = []
327
+ colspans = []
328
+ heights = []
329
+ widths = []
330
+
331
+ for ax in 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
+ 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 ]
357
348
alignleft = False
358
349
alignright = False
359
350
alignbot = False
360
351
aligntop = False
361
352
alignheight = False
362
353
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 ],
374
364
'left' )
375
365
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 ],
379
369
'right' )
380
370
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 ],
387
375
'top' )
388
376
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 ],
393
380
'bottom' )
394
381
alignbot = True
395
- ###########
382
+
396
383
# Now we make the widths and heights of position boxes
397
384
# 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.
406
392
# But it can be more than twice as large because
407
393
# 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 )
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 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 )
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
- 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 )
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 <= 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 )
501
464
502
465
503
466
def layoutcolorbarsingle (ax , cax , shrink , aspect , location , pad = 0.05 ):
@@ -560,33 +523,28 @@ def layoutcolorbarsingle(ax, cax, shrink, aspect, location, pad=0.05):
560
523
561
524
562
525
def _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
573
532
for ax in axs :
574
533
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 )
590
548
591
549
592
550
def layoutcolorbargridspec (parents , cax , shrink , aspect , location , pad = 0.05 ):
@@ -630,18 +588,16 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
630
588
# Horizontal Layout: need to check all the axes in this gridspec
631
589
for ch in gslb .children :
632
590
subspec = ch .artist
633
- nrows , ncols , row_start , row_stop , col_start , col_stop = \
634
- subspec .get_rows_columns ()
635
591
if location == 'right' :
636
- if col_stop <= maxcol :
592
+ if subspec . colspan . stop - 1 <= maxcol :
637
593
order = [subspec ._layoutbox , lb ]
638
594
# arrange to right of the parents
639
- if col_start > maxcol :
595
+ elif subspec . colspan . start > maxcol :
640
596
order = [lb , subspec ._layoutbox ]
641
597
elif location == 'left' :
642
- if col_start >= mincol :
598
+ if subspec . colspan . start >= mincol :
643
599
order = [lb , subspec ._layoutbox ]
644
- if col_stop < mincol :
600
+ elif subspec . colspan . stop - 1 < mincol :
645
601
order = [subspec ._layoutbox , lb ]
646
602
layoutbox .hstack (order , padding = pad * gslb .width ,
647
603
strength = 'strong' )
@@ -686,17 +642,15 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
686
642
# Vertical Layout: need to check all the axes in this gridspec
687
643
for ch in gslb .children :
688
644
subspec = ch .artist
689
- nrows , ncols , row_start , row_stop , col_start , col_stop = \
690
- subspec .get_rows_columns ()
691
645
if location == 'bottom' :
692
- if row_stop <= minrow :
646
+ if subspec . rowspan . stop - 1 <= minrow :
693
647
order = [subspec ._layoutbox , lb ]
694
- if row_start > maxrow :
648
+ elif subspec . rowspan . start > maxrow :
695
649
order = [lb , subspec ._layoutbox ]
696
650
elif location == 'top' :
697
- if row_stop < minrow :
651
+ if subspec . rowspan . stop - 1 < minrow :
698
652
order = [subspec ._layoutbox , lb ]
699
- if row_start >= maxrow :
653
+ elif subspec . rowspan . start >= maxrow :
700
654
order = [lb , subspec ._layoutbox ]
701
655
layoutbox .vstack (order , padding = pad * gslb .width ,
702
656
strength = 'strong' )
0 commit comments