Skip to content

Commit a1c8e77

Browse files
committed
Use a common helper function to align spaces
1 parent 2652f96 commit a1c8e77

1 file changed

Lines changed: 97 additions & 77 deletions

File tree

plotnine/_mpl/layout_manager/_layout_tree.py

Lines changed: 97 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from ._plot_side_space import PlotSideSpaces
1111

1212
if TYPE_CHECKING:
13-
from typing import Sequence, TypeAlias
13+
from typing import Any, Callable, Literal, Sequence, TypeAlias
1414

1515
from plotnine._mpl.gridspec import p9GridSpec
1616
from plotnine._mpl.layout_manager._plot_side_space import (
@@ -508,73 +508,53 @@ def align_panels(self):
508508
"""
509509
Align the edges of the panels in the composition
510510
"""
511-
for spaces in self.iter_bottom_spaces():
512-
bottoms = [space.panel_bottom for space in spaces]
513-
high = max(bottoms)
514-
diffs = [high - b for b in bottoms]
515-
for space, diff in zip(spaces, diffs):
516-
space.margin_alignment += diff
517-
518-
for spaces in self.iter_top_spaces():
519-
tops = [space.panel_top for space in spaces]
520-
low = min(tops)
521-
diffs = [b - low for b in tops]
522-
for space, diff in zip(spaces, diffs):
523-
space.margin_alignment += diff
524-
525-
for spaces in self.iter_left_spaces():
526-
lefts = [space.panel_left for space in spaces]
527-
high = max(lefts)
528-
diffs = [high - l for l in lefts]
529-
for space, diff in zip(spaces, diffs):
530-
space.margin_alignment += diff
531-
532-
for spaces in self.iter_right_spaces():
533-
rights = [space.panel_right for space in spaces]
534-
low = min(rights)
535-
diffs = [r - low for r in rights]
536-
for space, diff in zip(spaces, diffs):
537-
space.margin_alignment += diff
511+
_equalize(
512+
self.iter_bottom_spaces(),
513+
lambda s: s.panel_bottom,
514+
"margin_alignment",
515+
)
516+
_equalize(
517+
self.iter_top_spaces(),
518+
lambda s: s.panel_top,
519+
"margin_alignment",
520+
how="min",
521+
)
522+
_equalize(
523+
self.iter_left_spaces(),
524+
lambda s: s.panel_left,
525+
"margin_alignment",
526+
)
527+
_equalize(
528+
self.iter_right_spaces(),
529+
lambda s: s.panel_right,
530+
"margin_alignment",
531+
how="min",
532+
)
538533

539534
def align_tags(self):
540535
"""
541536
Align the tags in the composition
542537
"""
543-
for spaces in self.iter_bottom_spaces():
544-
heights = [
545-
space.tag_height + space.tag_alignment for space in spaces
546-
]
547-
high = max(heights)
548-
diffs = [high - h for h in heights]
549-
for space, diff in zip(spaces, diffs):
550-
space.tag_alignment += diff
551-
552-
for spaces in self.iter_top_spaces():
553-
heights = [
554-
space.tag_height + space.tag_alignment for space in spaces
555-
]
556-
high = max(heights)
557-
diffs = [high - h for h in heights]
558-
for space, diff in zip(spaces, diffs):
559-
space.tag_alignment += diff
560-
561-
for spaces in self.iter_left_spaces():
562-
widths = [
563-
space.tag_width + space.tag_alignment for space in spaces
564-
]
565-
high = max(widths)
566-
diffs = [high - w for w in widths]
567-
for space, diff in zip(spaces, diffs):
568-
space.tag_alignment += diff
569-
570-
for spaces in self.iter_right_spaces():
571-
widths = [
572-
space.tag_width + space.tag_alignment for space in spaces
573-
]
574-
high = max(widths)
575-
diffs = [high - w for w in widths]
576-
for space, diff in zip(spaces, diffs):
577-
space.tag_alignment += diff
538+
_equalize(
539+
self.iter_bottom_spaces(),
540+
lambda s: s.tag_height + s.tag_alignment,
541+
"tag_alignment",
542+
)
543+
_equalize(
544+
self.iter_top_spaces(),
545+
lambda s: s.tag_height + s.tag_alignment,
546+
"tag_alignment",
547+
)
548+
_equalize(
549+
self.iter_left_spaces(),
550+
lambda s: s.tag_width + s.tag_alignment,
551+
"tag_alignment",
552+
)
553+
_equalize(
554+
self.iter_right_spaces(),
555+
lambda s: s.tag_width + s.tag_alignment,
556+
"tag_alignment",
557+
)
578558

579559
def align_axis_titles(self):
580560
"""
@@ -588,20 +568,16 @@ def align_axis_titles(self):
588568
to store the value outside the _side_space and pick it up when
589569
setting the position of the texts!
590570
"""
591-
592-
for spaces in self.iter_bottom_spaces():
593-
clearances = [space.axis_title_clearance for space in spaces]
594-
high = max(clearances)
595-
diffs = [high - b for b in clearances]
596-
for space, diff in zip(spaces, diffs):
597-
space.axis_title_alignment += diff
598-
599-
for spaces in self.iter_left_spaces():
600-
clearances = [space.axis_title_clearance for space in spaces]
601-
high = max(clearances)
602-
diffs = [high - l for l in clearances]
603-
for space, diff in zip(spaces, diffs):
604-
space.axis_title_alignment += diff
571+
_equalize(
572+
self.iter_bottom_spaces(),
573+
lambda s: s.axis_title_clearance,
574+
"axis_title_alignment",
575+
)
576+
_equalize(
577+
self.iter_left_spaces(),
578+
lambda s: s.axis_title_clearance,
579+
"axis_title_alignment",
580+
)
605581

606582
for tree in self.sub_compositions:
607583
tree.align_axis_titles()
@@ -632,6 +608,50 @@ def resize_heights(self):
632608
self.sub_gridspec.set_height_ratios(height_ratios)
633609

634610

611+
def _equalize(
612+
spaces_iter: Iterator[Sequence[Any]],
613+
measure: Callable[[Any], float],
614+
attr: str,
615+
how: Literal["max", "min"] = "max",
616+
):
617+
"""
618+
Equalize a measurement across spaces by adjusting an attribute
619+
620+
For each group of spaces yielded by the iterator, find the extreme
621+
value (max or min) of the measurement, then add the difference to
622+
each space's alignment attribute so that all spaces in the group end
623+
up with the same measurement.
624+
625+
Parameters
626+
----------
627+
spaces_iter
628+
Iterator yielding groups of side spaces to equalize.
629+
Each group is a sequence of spaces along the same row or column of
630+
the composition.
631+
measure
632+
Function that extracts the value to equalize from a side space.
633+
attr
634+
Name of the alignment attribute on the side space to adjust.
635+
The difference is added to the current value.
636+
how
637+
Whether to equalize to the maximum or minimum value in each group.
638+
For "max", spaces with smaller measurements get extra alignment to
639+
match the largest.
640+
For "min", spaces with larger measurements get extra alignment to
641+
match the smallest.
642+
"""
643+
for spaces in spaces_iter:
644+
values = [measure(s) for s in spaces]
645+
if how == "max":
646+
target = max(values)
647+
diffs = [target - v for v in values]
648+
else:
649+
target = min(values)
650+
diffs = [v - target for v in values]
651+
for space, diff in zip(spaces, diffs):
652+
setattr(space, attr, getattr(space, attr) + diff)
653+
654+
635655
# For debugging
636656
def _draw_gridspecs(tree: LayoutTree):
637657
from ..utils import draw_bbox

0 commit comments

Comments
 (0)