6
6
7
7
from matplotlib .text import Text
8
8
9
+ from plotnine ._mpl .patches import StripTextPatch
10
+ from plotnine ._utils import ha_as_float , va_as_float
9
11
from plotnine .exceptions import PlotnineError
10
12
11
13
from ..utils import (
35
37
from plotnine ._mpl .text import StripText
36
38
from plotnine .iapi import legend_artists
37
39
from plotnine .themes .elements import margin as Margin
38
- from plotnine .typing import StripPosition
40
+ from plotnine .typing import (
41
+ HorizontalJustification ,
42
+ StripPosition ,
43
+ VerticalJustification ,
44
+ )
39
45
40
46
from ._spaces import LayoutSpaces
41
47
@@ -299,9 +305,9 @@ def axis_ticks_y(self, ax: Axes) -> Iterator[Tick]:
299
305
300
306
return chain (major , minor )
301
307
302
- def strip_text_x_height (self , position : StripPosition ) -> float :
308
+ def strip_text_x_extra_height (self , position : StripPosition ) -> float :
303
309
"""
304
- Height taken up by the top strips
310
+ Height taken up by the top strips that is outside the panels
305
311
"""
306
312
if not self .strip_text_x :
307
313
return 0
@@ -311,11 +317,23 @@ def strip_text_x_height(self, position: StripPosition) -> float:
311
317
for st in self .strip_text_x
312
318
if st .patch .position == position
313
319
]
314
- return self .calc .max_height (artists )
315
320
316
- def strip_text_y_width (self , position : StripPosition ) -> float :
321
+ heights = []
322
+
323
+ for a in artists :
324
+ info = (
325
+ a .text .draw_info
326
+ if isinstance (a , StripTextPatch )
327
+ else a .draw_info
328
+ )
329
+ h = self .calc .height (a )
330
+ heights .append (max (h + h * info .strip_align , 0 ))
331
+
332
+ return max (heights )
333
+
334
+ def strip_text_y_extra_width (self , position : StripPosition ) -> float :
317
335
"""
318
- Width taken up by the right strips
336
+ Width taken up by the top strips that is outside the panels
319
337
"""
320
338
if not self .strip_text_y :
321
339
return 0
@@ -325,7 +343,19 @@ def strip_text_y_width(self, position: StripPosition) -> float:
325
343
for st in self .strip_text_y
326
344
if st .patch .position == position
327
345
]
328
- return self .calc .max_width (artists )
346
+
347
+ widths = []
348
+
349
+ for a in artists :
350
+ info = (
351
+ a .text .draw_info
352
+ if isinstance (a , StripTextPatch )
353
+ else a .draw_info
354
+ )
355
+ w = self .calc .width (a )
356
+ widths .append (max (w + w * info .strip_align , 0 ))
357
+
358
+ return max (widths )
329
359
330
360
def axis_ticks_x_max_height_at (self , location : AxesLocation ) -> float :
331
361
"""
@@ -489,6 +519,8 @@ def _adjust_positions(self, spaces: LayoutSpaces):
489
519
490
520
self ._adjust_axis_text_x (justify )
491
521
self ._adjust_axis_text_y (justify )
522
+ self ._strip_text_x_background_equal_heights ()
523
+ self ._strip_text_y_background_equal_widths ()
492
524
493
525
def _adjust_axis_text_x (self , justify : TextJustifier ):
494
526
"""
@@ -574,6 +606,36 @@ def to_horizontal_axis_dimensions(value: float, ax: Axes) -> float:
574
606
text , ha , - axis_text_col_width , 0 , width = width
575
607
)
576
608
609
+ def _strip_text_x_background_equal_heights (self ):
610
+ """
611
+ Make the strip_text_x_backgrounds have equal heights
612
+
613
+ The smaller heights are expanded to match the largest height
614
+ """
615
+ if not self .strip_text_x :
616
+ return
617
+
618
+ heights = [self .calc .bbox (t .patch ).height for t in self .strip_text_x ]
619
+ max_height = max (heights )
620
+ relative_heights = [max_height / h for h in heights ]
621
+ for text , scale in zip (self .strip_text_x , relative_heights ):
622
+ text .patch .expand = scale
623
+
624
+ def _strip_text_y_background_equal_widths (self ):
625
+ """
626
+ Make the strip_text_y_backgrounds have equal widths
627
+
628
+ The smaller widths are expanded to match the largest width
629
+ """
630
+ if not self .strip_text_y :
631
+ return
632
+
633
+ widths = [self .calc .bbox (t .patch ).width for t in self .strip_text_y ]
634
+ max_width = max (widths )
635
+ relative_widths = [max_width / w for w in widths ]
636
+ for text , scale in zip (self .strip_text_y , relative_widths ):
637
+ text .patch .expand = scale
638
+
577
639
578
640
def _text_is_visible (text : Text ) -> bool :
579
641
"""
@@ -596,16 +658,15 @@ class TextJustifier:
596
658
def horizontally (
597
659
self ,
598
660
text : Text ,
599
- ha : str | float ,
661
+ ha : HorizontalJustification | float ,
600
662
left : float ,
601
663
right : float ,
602
664
width : float | None = None ,
603
665
):
604
666
"""
605
667
Horizontally Justify text between left and right
606
668
"""
607
- lookup = {"left" : 0.0 , "center" : 0.5 , "right" : 1.0 }
608
- rel = lookup .get (ha , ha ) # pyright: ignore[reportCallIssue, reportArgumentType]
669
+ rel = ha_as_float (ha )
609
670
if width is None :
610
671
width = self .spaces .items .calc .width (text )
611
672
x = rel_position (rel , width , left , right )
@@ -615,50 +676,51 @@ def horizontally(
615
676
def vertically (
616
677
self ,
617
678
text : Text ,
618
- va : str | float ,
679
+ va : VerticalJustification | float ,
619
680
bottom : float ,
620
681
top : float ,
621
682
height : float | None = None ,
622
683
):
623
684
"""
624
685
Vertically Justify text between bottom and top
625
686
"""
626
- lookup = {
627
- "top" : 1.0 ,
628
- "center" : 0.5 ,
629
- "baseline" : 0.5 ,
630
- "center_baseline" : 0.5 ,
631
- "bottom" : 0.0 ,
632
- }
633
- rel = lookup .get (va , va ) # pyright: ignore[reportCallIssue, reportArgumentType]
687
+ rel = va_as_float (va )
634
688
635
689
if height is None :
636
690
height = self .spaces .items .calc .height (text )
637
691
y = rel_position (rel , height , bottom , top )
638
692
text .set_y (y )
639
693
text .set_verticalalignment ("bottom" )
640
694
641
- def horizontally_across_panel (self , text : Text , ha : str | float ):
695
+ def horizontally_across_panel (
696
+ self , text : Text , ha : HorizontalJustification | float
697
+ ):
642
698
"""
643
699
Horizontally Justify text accross the panel(s) width
644
700
"""
645
701
self .horizontally (text , ha , self .spaces .l .left , self .spaces .r .right )
646
702
647
- def horizontally_across_plot (self , text : Text , ha : str | float ):
703
+ def horizontally_across_plot (
704
+ self , text : Text , ha : HorizontalJustification | float
705
+ ):
648
706
"""
649
707
Horizontally Justify text across the plot's width
650
708
"""
651
709
self .horizontally (
652
710
text , ha , self .spaces .l .plot_left , self .spaces .r .plot_right
653
711
)
654
712
655
- def vertically_along_panel (self , text : Text , va : str | float ):
713
+ def vertically_along_panel (
714
+ self , text : Text , va : VerticalJustification | float
715
+ ):
656
716
"""
657
717
Horizontally Justify text along the panel(s) height
658
718
"""
659
719
self .vertically (text , va , self .spaces .b .bottom , self .spaces .t .top )
660
720
661
- def vertically_along_plot (self , text : Text , va : str | float ):
721
+ def vertically_along_plot (
722
+ self , text : Text , va : VerticalJustification | float
723
+ ):
662
724
"""
663
725
Vertically Justify text along the plot's height
664
726
"""
0 commit comments