Skip to content

Commit dd55da7

Browse files
committed
Improvements of canvas sizing
1 parent 9003d07 commit dd55da7

5 files changed

Lines changed: 336 additions & 71 deletions

File tree

src/maxplotlib/backends/matplotlib/utils.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import pint
77

88

9-
def setup_tex_fonts(fontsize=14, usetex=False):
9+
def setup_tex_fonts(fontsize=10, usetex=False):
1010
"""
1111
Sets up LaTeX fonts for plotting.
1212
"""
@@ -15,6 +15,8 @@ def setup_tex_fonts(fontsize=14, usetex=False):
1515
"font.family": "serif",
1616
"pgf.rcfonts": False,
1717
"axes.labelsize": fontsize,
18+
"axes.titlesize": fontsize,
19+
"figure.titlesize": fontsize,
1820
"font.size": fontsize,
1921
"legend.fontsize": fontsize,
2022
"xtick.labelsize": fontsize,
@@ -58,13 +60,13 @@ def convert_to_inches(length_str):
5860

5961
def _2pt(width, dpi=300, verbose: bool = False):
6062
if verbose:
61-
print(f"Converting width: {width} to points with dpi={dpi}")
63+
print(f"Converting width: {width} to points")
6264

6365
if isinstance(width, (int, float)):
6466
return width
6567
elif isinstance(width, str):
6668
length_in = convert_to_inches(width)
67-
length_pt = length_in * dpi
69+
length_pt = length_in * 72.27
6870
if verbose:
6971
print(f"Converted length: {length_in} inches = {length_pt} points")
7072
return length_pt

src/maxplotlib/canvas/canvas.py

Lines changed: 80 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def plot_matplotlib(tikzfigure: TikzFigure, ax, layers=None):
155155
node.x,
156156
node.y,
157157
node.content,
158-
fontsize=10,
158+
fontsize=self._fontsize,
159159
ha="center",
160160
va="center",
161161
wrap=True,
@@ -183,9 +183,9 @@ def __init__(
183183
caption: str | None = None,
184184
description: str | None = None,
185185
label: str | None = None,
186-
fontsize: int = 14,
187-
dpi: int = 300,
188-
width: str = "5cm",
186+
fontsize: int = 10,
187+
dpi: int | None = None,
188+
width: str | None = None,
189189
ratio: str = "golden", # TODO Add literal
190190
usetex: bool | None = None,
191191
subplot_spacing: SubplotSpacing | None = None,
@@ -201,9 +201,9 @@ def __init__(
201201
caption (str): Caption for the figure.
202202
description (str): Description for the figure.
203203
label (str): Label for the figure.
204-
fontsize (int): Font size. Default is 14.
205-
dpi (int): DPI for the figure. Default is 300.
206-
width (str): Width of the figure. Default is "17cm".
204+
fontsize (int): Font size. Default is 10.
205+
dpi (int | None): Optional export/render DPI override.
206+
width (str | None): Optional figure width, e.g. "7cm".
207207
ratio (str): Aspect ratio. Default is "golden".
208208
usetex (bool | None): Default text.usetex behavior for this canvas.
209209
If None, read from MAXPLOTLIB_USETEX environment variable.
@@ -762,7 +762,8 @@ def savefig(
762762
layers=layers,
763763
)
764764
_fn = f"{filename_no_extension}_{layers}.{extension}"
765-
fig.savefig(_fn)
765+
savefig_kwargs = {"dpi": self.dpi} if self.dpi is not None else {}
766+
fig.savefig(_fn, **savefig_kwargs)
766767
print(f"Saved {_fn}")
767768
else:
768769
if layers is None:
@@ -772,14 +773,16 @@ def savefig(
772773
full_filepath = f"{filename_no_extension}_{layers}.{extension}"
773774

774775
if self._plotted:
775-
self._matplotlib_fig.savefig(full_filepath)
776+
savefig_kwargs = {"dpi": self.dpi} if self.dpi is not None else {}
777+
self._matplotlib_fig.savefig(full_filepath, **savefig_kwargs)
776778
else:
777779
fig, axs = self.plot(
778780
backend="matplotlib",
779781
savefig=True,
780782
layers=layers,
781783
)
782-
fig.savefig(full_filepath)
784+
savefig_kwargs = {"dpi": self.dpi} if self.dpi is not None else {}
785+
fig.savefig(full_filepath, **savefig_kwargs)
783786
if verbose:
784787
print(f"Saved {full_filepath}")
785788
elif backend == "plotext":
@@ -841,8 +844,8 @@ def savefig(
841844
def plot(
842845
self,
843846
backend: Backends = "matplotlib",
844-
savefig=False,
845-
layers=None,
847+
savefig: bool = False,
848+
layers: list | None = None,
846849
usetex: bool | None = None,
847850
verbose: bool = False,
848851
):
@@ -884,17 +887,22 @@ def show(
884887
verbose: bool = False,
885888
):
886889
if verbose:
887-
print(f"Showing figure using backend: {backend}")
890+
print(f"Showing canvas using backend: {backend}")
888891

889892
if backend == "matplotlib":
890-
self.plot(
893+
if verbose:
894+
print("Generating Matplotlib figure for display...")
895+
fig, axes = self.plot(
891896
backend="matplotlib",
892897
savefig=False,
893898
layers=layers,
894899
usetex=usetex,
895900
verbose=verbose,
896901
)
897-
# self._matplotlib_fig.show()
902+
if verbose:
903+
print("Displaying Matplotlib figure...")
904+
plt.show()
905+
return fig, axes
898906
elif backend == "plotly":
899907
resolved_usetex = self._usetex if usetex is None else usetex
900908
fig = self.plot_plotly(
@@ -936,6 +944,7 @@ def plot_matplotlib(
936944

937945
resolved_usetex = self._usetex if usetex is None else usetex
938946
tex_fonts = setup_tex_fonts(fontsize=self.fontsize, usetex=resolved_usetex)
947+
render_dpi = self.dpi if savefig else None
939948

940949
setup_plotstyle(
941950
tex_fonts=tex_fonts,
@@ -947,27 +956,40 @@ def plot_matplotlib(
947956
if verbose:
948957
print("Plot style set up.")
949958
print(f"{self._figsize = } {self._width = } {self._ratio = }")
959+
subplot_kwargs = {
960+
"squeeze": False,
961+
"gridspec_kw": self._gridspec_kw,
962+
}
950963
if self._figsize is not None:
951-
fig_width, fig_height = self._figsize
952-
else:
964+
subplot_kwargs["figsize"] = self._figsize
965+
elif self._width is not None:
953966
fig_width, fig_height = set_size(
954967
width=self._width,
955968
ratio=self._ratio,
956-
dpi=self.dpi,
969+
dpi=render_dpi,
957970
verbose=verbose,
958971
)
972+
subplot_kwargs["figsize"] = (fig_width, fig_height)
959973
if verbose:
960-
print(f"Figure size: {fig_width} x {fig_height} points")
974+
if "figsize" in subplot_kwargs:
975+
fig_width, fig_height = subplot_kwargs["figsize"]
976+
print(f"Figure size: {fig_width} x {fig_height} inches")
977+
else:
978+
print("Figure size: Matplotlib default")
979+
print(f"Render DPI override: {render_dpi} (export DPI: {self.dpi})")
980+
981+
if render_dpi is not None:
982+
subplot_kwargs["dpi"] = render_dpi
961983

962984
fig, axes = plt.subplots(
963985
self.nrows,
964986
self.ncols,
965-
figsize=(fig_width, fig_height),
966-
squeeze=False,
967-
dpi=self.dpi,
968-
gridspec_kw=self._gridspec_kw,
987+
**subplot_kwargs,
969988
)
970989

990+
if verbose:
991+
print(f"Created Matplotlib figure and axes with shape {axes.shape}")
992+
971993
for (row, col), subplot in self._subplot_dict.items():
972994
ax = axes[row][col]
973995
if isinstance(subplot, TikzFigure):
@@ -977,8 +999,16 @@ def plot_matplotlib(
977999
# ax.set_title(f"Subplot ({row}, {col})")
9781000
ax.grid()
9791001

1002+
if verbose:
1003+
print("Finished plotting subplots.")
1004+
9801005
if self._suptitle:
981-
fig.suptitle(self._suptitle, **self._suptitle_kwargs)
1006+
suptitle_kwargs = dict(self._suptitle_kwargs)
1007+
suptitle_kwargs.setdefault("fontsize", self.fontsize)
1008+
fig.suptitle(self._suptitle, **suptitle_kwargs)
1009+
1010+
if verbose:
1011+
print("Set suptitle.")
9821012

9831013
# Set caption, labels, etc., if needed
9841014
self._plotted = True
@@ -1019,6 +1049,7 @@ def plot_tikzfigure(
10191049
"No subplots to plot. Call add_subplot() or Canvas.subplots() first."
10201050
)
10211051

1052+
axis_width, axis_height = self._get_tikzfigure_axis_dimensions()
10221053
fig = TikzFigure()
10231054

10241055
# Add each subplot as a subfigure axis
@@ -1041,8 +1072,10 @@ def plot_tikzfigure(
10411072
else None
10421073
),
10431074
grid=line_plot._grid,
1044-
caption=line_plot._title or f"Subplot {col + 1}",
1075+
title=line_plot._title or f"Subplot {col + 1}",
10451076
width=0.45,
1077+
axis_width=axis_width,
1078+
height=axis_height,
10461079
)
10471080

10481081
# Add each plot line to the subfigure
@@ -1069,6 +1102,28 @@ def plot_tikzfigure(
10691102

10701103
return fig
10711104

1105+
def _get_tikzfigure_axis_dimensions(self) -> tuple[str | None, str | None]:
1106+
if self._width is None:
1107+
return None, None
1108+
1109+
total_width_in, total_height_in = set_size(
1110+
width=self._width,
1111+
ratio=self._ratio,
1112+
dpi=self._dpi if self._dpi is not None else 300,
1113+
)
1114+
total_width_cm = total_width_in * 2.54
1115+
total_height_cm = total_height_in * 2.54
1116+
horizontal_sep_cm = getattr(TikzFigure, "GROUPPLOT_HORIZONTAL_SEP_CM", 1.5)
1117+
available_width_cm = total_width_cm - horizontal_sep_cm * (self.ncols - 1)
1118+
if available_width_cm <= 0:
1119+
raise ValueError(
1120+
f'Canvas width "{self._width}" is too small for {self.ncols} '
1121+
"tikzfigure subplot(s)."
1122+
)
1123+
1124+
axis_width_cm = available_width_cm / self.ncols
1125+
return f"{axis_width_cm:.6g}cm", f"{total_height_cm:.6g}cm"
1126+
10721127
def plot_plotext(
10731128
self,
10741129
savefig: bool = False,
@@ -1124,15 +1179,6 @@ def plot_plotly(
11241179
usetex=resolved_usetex,
11251180
) # adjust or redefine for Plotly if needed
11261181

1127-
# Set default width and height if not specified
1128-
if self._figsize is not None:
1129-
fig_width, fig_height = self._figsize
1130-
else:
1131-
fig_width, fig_height = set_size(
1132-
width=self._width,
1133-
ratio=self._ratio,
1134-
)
1135-
# print(self._width, fig_width, fig_height)
11361182
# Create subplot titles in row-major order (Plotly expects rows*cols entries)
11371183
subplot_titles = [""] * (self.nrows * self.ncols)
11381184
for (row, col), sp in self._subplot_dict.items():

0 commit comments

Comments
 (0)