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,110 @@ 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
+ heights = []
329
+ widths = []
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
+ 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.
385
+ # This allows vertically stacked subplots to have different sizes
386
+ # if they occupy different amounts of the gridspec: i.e.
401
387
# gs = gridspec.GridSpec(3, 1)
402
388
# ax1 = gs[0, :]
403
389
# ax2 = gs[1:, :]
404
390
# then drows0 = 1, and drowsC = 2, and ax2
405
391
# should be at least twice as large as ax1.
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 height, this only needs to be done if the subplots share a
396
+ # column.
397
+ if not alignheight and len (rowspan0 ) == len (rowspan1 ):
398
+ ax0 ._poslayoutbox .constrain_height (
399
+ ax1 ._poslayoutbox .height * height0 / height1 )
421
400
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 )
401
+ elif _spans_overlap (colspan0 , colspan1 ):
402
+ if height0 > height1 :
403
+ ax0 ._poslayoutbox .constrain_height_min (
404
+ ax1 ._poslayoutbox .height * height0 / height1 )
427
405
# these constraints stop the smaller axes from
428
406
# 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 )
407
+ ax1 ._poslayoutbox .constrain_height_min (
408
+ ax0 ._poslayoutbox .height * height1 / (height0 * 1.8 ))
409
+ elif height0 < height1 :
410
+ ax1 ._poslayoutbox .constrain_height_min (
411
+ ax0 ._poslayoutbox .height * height1 / height0 )
412
+ ax0 ._poslayoutbox .constrain_height_min (
413
+ ax0 ._poslayoutbox .height * height0 / (height1 * 1.8 ))
414
+ # For width, if they share a row.
415
+ if not alignwidth and len (colspan0 ) == len (colspan1 ):
416
+ ax0 ._poslayoutbox .constrain_width (
417
+ ax1 ._poslayoutbox .width * width0 / width1 )
442
418
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 ))
419
+ elif _spans_overlap (rowspan0 , rowspan1 ):
420
+ if width0 > width1 :
421
+ ax0 ._poslayoutbox .constrain_width_min (
422
+ ax1 ._poslayoutbox .width * width0 / width1 )
423
+ ax1 ._poslayoutbox .constrain_width_min (
424
+ ax0 ._poslayoutbox .width * width1 / (width0 * 1.8 ))
425
+ elif width0 < width1 :
426
+ ax1 ._poslayoutbox .constrain_width_min (
427
+ ax0 ._poslayoutbox .width * width1 / width0 )
428
+ ax0 ._poslayoutbox .constrain_width_min (
429
+ ax1 ._poslayoutbox .width * width0 / (width1 * 1.8 ))
457
430
458
431
459
432
def _arrange_subplotspecs (gs , hspace = 0 , wspace = 0 ):
@@ -470,34 +443,25 @@ def _arrange_subplotspecs(gs, hspace=0, wspace=0):
470
443
for child0 in sschildren :
471
444
ss0 = child0 .artist
472
445
nrows , ncols = ss0 .get_gridspec ().get_geometry ()
473
- rowNum0min , colNum0min = divmod ( ss0 .num1 , ncols )
474
- rowNum0max , colNum0max = divmod ( ss0 .num2 , ncols )
446
+ rowspan0 = ss0 .rowspan
447
+ colspan0 = ss0 .colspan
475
448
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
- ####
449
+ for child1 in sschildren :
450
+ ss1 = child1 .artist
451
+ rowspan1 = ss1 .rowspan
452
+ colspan1 = ss1 .colspan
453
+ # OK, this tells us the relative layout of ax with ax1
454
+ pad = wspace / ncols
455
+ if colspan0 .stop <= colspan1 .start :
456
+ layoutbox .hstack ([ss0 ._layoutbox , ss1 ._layoutbox ], padding = pad )
457
+ if colspan1 .stop <= colspan0 .start :
458
+ layoutbox .hstack ([ss1 ._layoutbox , ss0 ._layoutbox ], padding = pad )
491
459
# 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 )
460
+ pad = hspace / nrows
461
+ if rowspan0 .stop <= rowspan1 .start :
462
+ layoutbox .vstack ([ss0 ._layoutbox , ss1 ._layoutbox ], padding = pad )
463
+ if rowspan1 .stop <= rowspan0 .start :
464
+ layoutbox .vstack ([ss1 ._layoutbox , ss0 ._layoutbox ], padding = pad )
501
465
502
466
503
467
def layoutcolorbarsingle (ax , cax , shrink , aspect , location , pad = 0.05 ):
@@ -561,32 +525,21 @@ def layoutcolorbarsingle(ax, cax, shrink, aspect, location, pad=0.05):
561
525
562
526
def _getmaxminrowcolumn (axs ):
563
527
# 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
528
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 )
529
+ def key (pos_ax ):
530
+ pos , ax = pos_ax
531
+ return pos
532
+
533
+ minrow , minrow_ax = min (
534
+ ((ax .get_subplotspec ().rowspan .start , ax ) for ax in axs ), key = key )
535
+ maxrow , maxrow_ax = max (
536
+ ((ax .get_subplotspec ().rowspan .stop - 1 , ax ) for ax in axs ), key = key )
537
+ mincol , mincol_ax = min (
538
+ ((ax .get_subplotspec ().colspan .start , ax ) for ax in axs ), key = key )
539
+ maxcol , maxcol_ax = max (
540
+ ((ax .get_subplotspec ().colspan .stop - 1 , ax ) for ax in axs ), key = key )
541
+ return (minrow , maxrow , minrow_ax , maxrow_ax ,
542
+ mincol , maxcol , mincol_ax , maxcol_ax )
590
543
591
544
592
545
def layoutcolorbargridspec (parents , cax , shrink , aspect , location , pad = 0.05 ):
@@ -630,18 +583,16 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
630
583
# Horizontal Layout: need to check all the axes in this gridspec
631
584
for ch in gslb .children :
632
585
subspec = ch .artist
633
- nrows , ncols , row_start , row_stop , col_start , col_stop = \
634
- subspec .get_rows_columns ()
635
586
if location == 'right' :
636
- if col_stop <= maxcol :
587
+ if subspec . colspan . stop - 1 <= maxcol :
637
588
order = [subspec ._layoutbox , lb ]
638
589
# arrange to right of the parents
639
- if col_start > maxcol :
590
+ elif subspec . colspan . start > maxcol :
640
591
order = [lb , subspec ._layoutbox ]
641
592
elif location == 'left' :
642
- if col_start >= mincol :
593
+ if subspec . colspan . start >= mincol :
643
594
order = [lb , subspec ._layoutbox ]
644
- if col_stop < mincol :
595
+ elif subspec . colspan . stop - 1 < mincol :
645
596
order = [subspec ._layoutbox , lb ]
646
597
layoutbox .hstack (order , padding = pad * gslb .width ,
647
598
strength = 'strong' )
@@ -686,17 +637,15 @@ def layoutcolorbargridspec(parents, cax, shrink, aspect, location, pad=0.05):
686
637
# Vertical Layout: need to check all the axes in this gridspec
687
638
for ch in gslb .children :
688
639
subspec = ch .artist
689
- nrows , ncols , row_start , row_stop , col_start , col_stop = \
690
- subspec .get_rows_columns ()
691
640
if location == 'bottom' :
692
- if row_stop <= minrow :
641
+ if subspec . rowspan . stop - 1 <= minrow :
693
642
order = [subspec ._layoutbox , lb ]
694
- if row_start > maxrow :
643
+ elif subspec . rowspan . start > maxrow :
695
644
order = [lb , subspec ._layoutbox ]
696
645
elif location == 'top' :
697
- if row_stop < minrow :
646
+ if subspec . rowspan . stop - 1 < minrow :
698
647
order = [subspec ._layoutbox , lb ]
699
- if row_start >= maxrow :
648
+ elif subspec . rowspan . start >= maxrow :
700
649
order = [lb , subspec ._layoutbox ]
701
650
layoutbox .vstack (order , padding = pad * gslb .width ,
702
651
strength = 'strong' )
0 commit comments