@@ -118,6 +118,7 @@ def harmonise(self):
118118 """
119119 Align and resize plots in composition to look good
120120 """
121+ self .align_axis_titles ()
121122 self .align ()
122123 self .resize ()
123124
@@ -147,6 +148,20 @@ def align_sub_compositions(self):
147148 for tree in self .sub_compositions :
148149 tree .align ()
149150
151+ @abc .abstractmethod
152+ def align_axis_titles (self ):
153+ """
154+ Align the axis titles along the composing dimension
155+
156+ Since the alignment value used to for this purpose is one of
157+ the fields in the _side_space, it affects the space created
158+ for the panel.
159+
160+ We could align the titles within self.align but we would have
161+ to store the value outside the _side_space and pick it up when
162+ setting the position of the texts!
163+ """
164+
150165 def resize_sub_compositions (self ):
151166 """
152167 Resize panels in the compositions contained in this one
@@ -365,6 +380,50 @@ def top_tags_align(self) -> bool:
365380 arr = np .array (self .top_tag_heights )
366381 return all (arr == arr [0 ])
367382
383+ @property
384+ def left_axis_titles_align (self ) -> bool :
385+ """
386+ Return True if the left axis titles align
387+ """
388+ arr = np .array (self .left_axis_title_clearances )
389+ return all (arr == arr [0 ])
390+
391+ @property
392+ def bottom_axis_titles_align (self ) -> bool :
393+ """
394+ Return True if the bottom axis titles align
395+ """
396+ arr = np .array (self .bottom_axis_title_clearances )
397+ return all (arr == arr [0 ])
398+
399+ @cached_property
400+ @abc .abstractmethod
401+ def left_axis_title_clearance (self ) -> float :
402+ """
403+ Distance between the left y-axis title and the panel
404+ """
405+
406+ @cached_property
407+ @abc .abstractmethod
408+ def bottom_axis_title_clearance (self ) -> float :
409+ """
410+ Distance between the left x-axis title and the panel
411+ """
412+
413+ @cached_property
414+ def left_axis_title_clearances (self ) -> list [float ]:
415+ """
416+ Distances between the left y-axis titles and the panels
417+ """
418+ return [node .left_axis_title_clearance for node in self .nodes ]
419+
420+ @cached_property
421+ def bottom_axis_title_clearances (self ) -> list [float ]:
422+ """
423+ Distances between the bottom x-axis titles and the panels
424+ """
425+ return [node .bottom_axis_title_clearance for node in self .nodes ]
426+
368427 @abc .abstractmethod
369428 def set_left_margin_alignment (self , value : float ):
370429 """
@@ -429,6 +488,22 @@ def set_top_tag_alignment(self, value: float):
429488 In figure dimenstions
430489 """
431490
491+ @abc .abstractmethod
492+ def set_left_axis_title_alignment (self , value : float ):
493+ """
494+ Set the space to align the left axis titles in this composition
495+
496+ In figure dimenstions
497+ """
498+
499+ @abc .abstractmethod
500+ def set_bottom_axis_title_alignment (self , value : float ):
501+ """
502+ Set the space to align the bottom axis titles in this composition
503+
504+ In figure dimenstions
505+ """
506+
432507
433508@dataclass
434509class ColumnsTree (LayoutTree ):
@@ -458,6 +533,11 @@ def align(self):
458533 self .align_panel_bottoms ()
459534 self .align_sub_compositions ()
460535
536+ def align_axis_titles (self ):
537+ self .align_bottom_axis_titles ()
538+ for tree in self .sub_compositions :
539+ tree .align_axis_titles ()
540+
461541 def resize (self ):
462542 """
463543 Resize the widths of gridspec so that panels have equal widths
@@ -552,6 +632,23 @@ def align_top_tags(self):
552632 else :
553633 item .set_top_tag_alignment (value )
554634
635+ def align_bottom_axis_titles (self ):
636+ if self .bottom_axis_titles_align :
637+ pass
638+
639+ values = max (self .bottom_axis_title_clearances ) - np .array (
640+ self .bottom_axis_title_clearances
641+ )
642+ # We ignore 0 values since they can undo values
643+ # set to align this composition with an outer one.
644+ for item , value in zip (self .nodes , values ):
645+ if value == 0 :
646+ continue
647+ if isinstance (item , LayoutSpaces ):
648+ item .b .axis_title_alignment = value
649+ else :
650+ item .set_bottom_axis_title_alignment (value )
651+
555652 @cached_property
556653 def panel_lefts (self ):
557654 left_item = self .nodes [0 ]
@@ -620,6 +717,14 @@ def bottom_tag_height(self) -> float:
620717 def top_tag_height (self ) -> float :
621718 return max (self .top_tag_heights )
622719
720+ @cached_property
721+ def left_axis_title_clearance (self ) -> float :
722+ return self .left_axis_title_clearances [0 ]
723+
724+ @cached_property
725+ def bottom_axis_title_clearance (self ) -> float :
726+ return max (self .bottom_axis_title_clearances )
727+
623728 def set_left_margin_alignment (self , value : float ):
624729 left_item = self .nodes [0 ]
625730 if isinstance (left_item , LayoutSpaces ):
@@ -662,6 +767,20 @@ def set_top_tag_alignment(self, value: float):
662767 else :
663768 item .set_top_tag_alignment (value )
664769
770+ def set_bottom_axis_title_alignment (self , value : float ):
771+ for item in self .nodes :
772+ if isinstance (item , LayoutSpaces ):
773+ item .b .axis_title_alignment = value
774+ else :
775+ item .set_bottom_axis_title_alignment (value )
776+
777+ def set_left_axis_title_alignment (self , value : float ):
778+ left_item = self .nodes [0 ]
779+ if isinstance (left_item , LayoutSpaces ):
780+ left_item .l .axis_title_alignment = value
781+ else :
782+ left_item .set_left_axis_title_alignment (value )
783+
665784
666785@dataclass
667786class RowsTree (LayoutTree ):
@@ -688,6 +807,11 @@ def align(self):
688807 self .align_panel_rights ()
689808 self .align_sub_compositions ()
690809
810+ def align_axis_titles (self ):
811+ self .align_left_axis_titles ()
812+ for tree in self .sub_compositions :
813+ tree .align_axis_titles ()
814+
691815 def resize (self ):
692816 """
693817 Resize the heights of gridspec so that panels have equal heights
@@ -804,6 +928,21 @@ def align_right_tags(self):
804928 else :
805929 item .set_right_tag_alignment (value )
806930
931+ def align_left_axis_titles (self ):
932+ if self .left_axis_titles_align :
933+ pass
934+
935+ values = max (self .left_axis_title_clearances ) - np .array (
936+ self .left_axis_title_clearances
937+ )
938+ for item , value in zip (self .nodes , values ):
939+ if value == 0 :
940+ continue
941+ if isinstance (item , LayoutSpaces ):
942+ item .l .axis_title_alignment = value
943+ else :
944+ item .set_left_axis_title_alignment (value )
945+
807946 @cached_property
808947 def panel_lefts (self ):
809948 values = []
@@ -872,6 +1011,14 @@ def top_tag_height(self) -> float:
8721011 def bottom_tag_height (self ) -> float :
8731012 return self .bottom_tag_heights [- 1 ]
8741013
1014+ @cached_property
1015+ def left_axis_title_clearance (self ) -> float :
1016+ return max (self .left_axis_title_clearances )
1017+
1018+ @cached_property
1019+ def bottom_axis_title_clearance (self ) -> float :
1020+ return self .bottom_axis_title_clearances [- 1 ]
1021+
8751022 def set_left_margin_alignment (self , value : float ):
8761023 for item in self .nodes :
8771024 if isinstance (item , LayoutSpaces ):
@@ -913,3 +1060,17 @@ def set_right_tag_alignment(self, value: float):
9131060 item .r .tag_alignment = value
9141061 else :
9151062 item .set_right_tag_alignment (value )
1063+
1064+ def set_left_axis_title_alignment (self , value : float ):
1065+ for item in self .nodes :
1066+ if isinstance (item , LayoutSpaces ):
1067+ item .l .axis_title_alignment = value
1068+ else :
1069+ item .set_left_axis_title_alignment (value )
1070+
1071+ def set_bottom_axis_title_alignment (self , value : float ):
1072+ bottom_item = self .nodes [- 1 ]
1073+ if isinstance (bottom_item , LayoutSpaces ):
1074+ bottom_item .b .axis_title_alignment = value
1075+ else :
1076+ bottom_item .set_bottom_axis_title_alignment (value )
0 commit comments