From d459a898d7b19db10ea3267ff02c7c440e2aaad8 Mon Sep 17 00:00:00 2001 From: Hassan Kibirige Date: Tue, 5 Nov 2024 14:02:02 +0300 Subject: [PATCH] Fix saving to pdf and aspect_ratios fixes #885 --- doc/changelog.qmd | 3 ++ plotnine/_mpl/_plot_side_space.py | 7 ++++ plotnine/_mpl/_plotnine_tight_layout.py | 47 +++++++++---------------- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/doc/changelog.qmd b/doc/changelog.qmd index 987895680..2255e3fc8 100644 --- a/doc/changelog.qmd +++ b/doc/changelog.qmd @@ -12,6 +12,9 @@ title: Changelog - In quarto documents make the output `retina` even if the `fig-format` is `png`. +- Fixed bug where you could not save as pdf if also specifying the + `aspect_ratio`. ({{< issue 885 >}}) + ## v0.14.0 (2024-10-28) diff --git a/plotnine/_mpl/_plot_side_space.py b/plotnine/_mpl/_plot_side_space.py index 9f33800aa..5f4dd873a 100644 --- a/plotnine/_mpl/_plot_side_space.py +++ b/plotnine/_mpl/_plot_side_space.py @@ -67,6 +67,13 @@ class WHSpaceParts: wspace: float # mpl.subplotpars.wspace hspace: float # mpl.subplotpars.hspace + @property + def aspect_ratio(self) -> float: + """ + Aspect ratio of the panels + """ + return (self.h * self.H) / (self.w * self.W) + @dataclass class _side_spaces(ABC): diff --git a/plotnine/_mpl/_plotnine_tight_layout.py b/plotnine/_mpl/_plotnine_tight_layout.py index 25574f769..e3661b8c1 100644 --- a/plotnine/_mpl/_plotnine_tight_layout.py +++ b/plotnine/_mpl/_plotnine_tight_layout.py @@ -12,7 +12,6 @@ from __future__ import annotations import typing -from copy import deepcopy from dataclasses import dataclass from ._plot_side_space import LRTBSpaces, WHSpaceParts, calculate_panel_spacing @@ -56,6 +55,7 @@ class TightParams: All parameters computed for the plotnine tight layout engine """ + facet: facet sides: LRTBSpaces gullies: WHSpaceParts @@ -69,32 +69,26 @@ def __post_init__(self): hspace=self.gullies.hspace, ) - def to_aspect_ratio( - self, facet: facet, ratio: float, parts: WHSpaceParts - ) -> TightParams: - """ - Modify TightParams to get a given aspect ratio - """ - current_ratio = (parts.h * parts.H) / (parts.w * parts.W) - increase_aspect_ratio = ratio > current_ratio - if increase_aspect_ratio: # Taller panel - return self._reduce_width(facet, ratio, parts) - else: - return self._reduce_height(facet, ratio, parts) - - def _reduce_height( - self, facet: facet, ratio: float, parts: WHSpaceParts - ) -> TightParams: + if (ratio := self.facet._aspect_ratio()) is not None: + current_ratio = self.gullies.aspect_ratio + if ratio > current_ratio: + # Increase aspect ratio, taller panels + self._reduce_width(ratio) + elif ratio < current_ratio: + # Increase aspect ratio, wider panels + self._reduce_height(ratio) + + def _reduce_height(self, ratio: float): """ Reduce the height of axes to get the aspect ratio """ - self = deepcopy(self) + parts = self.gullies # New height w.r.t figure height h1 = ratio * parts.w * (parts.W / parts.H) # Half of the total vertical reduction w.r.t figure height - dh = (parts.h - h1) * facet.nrow / 2 + dh = (parts.h - h1) * self.facet.nrow / 2 # Reduce plot area height self.params.top -= dh @@ -104,21 +98,18 @@ def _reduce_height( # Add more vertical plot margin self.sides.t.plot_margin += dh self.sides.b.plot_margin += dh - return self - def _reduce_width( - self, facet: facet, ratio: float, parts: WHSpaceParts - ) -> TightParams: + def _reduce_width(self, ratio: float): """ Reduce the width of axes to get the aspect ratio """ - self = deepcopy(self) + parts = self.gullies # New width w.r.t figure width w1 = (parts.h * parts.H) / (ratio * parts.W) # Half of the total horizontal reduction w.r.t figure width - dw = (parts.w - w1) * facet.ncol / 2 + dw = (parts.w - w1) * self.facet.ncol / 2 # Reduce width self.params.left += dw @@ -128,7 +119,6 @@ def _reduce_width( # Add more horizontal margin self.sides.l.plot_margin += dw self.sides.r.plot_margin += dw - return self def get_plotnine_tight_layout(pack: LayoutPack) -> TightParams: @@ -137,10 +127,7 @@ def get_plotnine_tight_layout(pack: LayoutPack) -> TightParams: """ sides = LRTBSpaces(pack) gullies = calculate_panel_spacing(pack, sides) - tight_params = TightParams(sides, gullies) - ratio = pack.facet._aspect_ratio() - if ratio is not None: - tight_params = tight_params.to_aspect_ratio(pack.facet, ratio, gullies) + tight_params = TightParams(pack.facet, sides, gullies) return tight_params