Layout and Typography¶
Layout optimization¶
For most figures, simple_layout(fig) is all you need — it automatically
optimizes margins so labels and titles don’t clip or overlap:
import matplotlib.pyplot as plt
import dartwork_mpl as dm
import numpy as np
dm.style.use("scientific")
fig, ax = plt.subplots(figsize=(dm.cm2in(15), dm.cm2in(10)), dpi=300)
ax.plot(np.linspace(0, 10, 100), np.sin(np.linspace(0, 10, 100)), color="oc.blue6")
ax.set_xlabel("Time [s]", fontsize=dm.fs(0))
ax.set_ylabel("Response", fontsize=dm.fs(0))
dm.simple_layout(fig) # auto-optimizes margins — replaces tight_layout()
Try it — drag the sliders to see how figure dimensions map onto an A4 page:
Multi-panel figures¶
For multi-panel layouts, use GridSpec and pass it to simple_layout:
fig = plt.figure(figsize=(dm.cm2in(15), dm.cm2in(12)), dpi=300)
gs = fig.add_gridspec(2, 2, hspace=0.35, wspace=0.25)
axes = [fig.add_subplot(gs[i, j]) for i in range(2) for j in range(2)]
for ax in axes:
ax.plot(np.linspace(0, 1, 40), np.random.rand(40), color="oc.blue6", lw=0.8)
dm.label_axes(axes) # adds (a), (b), (c), (d) panel labels
dm.set_decimal(axes[0], xn=2, yn=1) # format tick labels to fixed decimals
# Pass gs so simple_layout respects your GridSpec spacing
dm.simple_layout(fig, gs=gs)
Tip: You generally don’t need to set explicit
left,right,top,bottomvalues on GridSpec —simple_layoutfinds optimal margins automatically. Only add manual margins when you need fine positional control (e.g., making room for a colorbar or external legend).
Content-Aware Layout with auto_layout¶
For figures with complex text elements that might overflow, auto_layout() provides
an intelligent alternative that automatically detects and fixes overflow:
import dartwork_mpl as dm
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(dm.cm2in(9), dm.cm2in(6)))
ax.plot(np.linspace(0, 10, 100), np.sin(np.linspace(0, 10, 100)))
ax.set_ylabel("Very Long Label That Might\nOverflow the Figure Bounds")
ax.set_title("Complex Multi-Line Title\nWith Potential Overflow", fontsize=dm.fs(2))
# Auto-adjusts margins to prevent any overflow
dm.auto_layout(fig, padding=0.08, max_iter=5, verbose=True)
How it works:
Applies initial layout with minimal margins
Measures actual text overflow on all four sides
Increases margins only where needed
Repeats until convergence (typically 1-3 iterations)
This is especially useful for:
Axes with very long y-axis labels
Multi-line titles that extend beyond figure bounds
Figures with many annotations
Automatically generated charts where label length is unknown
Responsive Margins with set_xmargin and set_ymargin¶
Control axis data margins responsively based on data range:
# Add 10% margin to x-axis data range
dm.set_xmargin(ax, margin=0.1)
# Add different margins for y-axis
dm.set_ymargin(ax, margin=0.05)
# Useful for:
# - Preventing data from touching axis edges
# - Creating visual breathing room
# - Ensuring markers aren't clipped at boundaries
Which Layout Function to Use?¶
Scenario |
Recommended Function |
Why |
|---|---|---|
Simple plots with standard labels |
|
Fast, consistent margins |
Complex multi-line titles/labels |
|
Automatic overflow detection |
GridSpec with spacing control |
|
Respects hspace/wspace |
Need exact margin control |
|
Precise inch-based margins |
Unknown label lengths (AI-generated) |
|
Adapts to content |
Debugging layout issues |
|
Shows iteration diagnostics |
simple_layout vs tight_layout¶
Simple layouts look fine with either method. The real difference shows up when figures get more complex — multi-panel grids, long axis labels, colorbars, and titles all competing for space.
Interactive visualizer — see how dartwork-mpl calculates margins dynamically:
Below is the same figure — a two-panel layout with a multi-line y-label and a colorbar — rendered with each approach:
tight_layout()simple_layout()What’s different?
tight_layout() calculates padding heuristically — it tries to prevent
overlap but doesn’t guarantee uniform margins or optimal use of space. When
panels have different label lengths (e.g., a multi-line ylabel vs. a
short one), or when a colorbar shifts the effective axes width, the
heuristic can produce lopsided spacing or wasted whitespace.
simple_layout() uses scipy’s L-BFGS-B optimizer to minimize the gap
between the actual tight bounding boxes and target margins you specify in
inches. This means:
Uniform margins — every figure edge gets exactly the space you request
Colorbar-aware — the optimizer sees the full bounding box, including colorbars and legends, not just the axes
GridSpec-native — pass
gs=gsand it respects yourhspace/wspacewhile only adjusting outer margins
Key layout functions:
Function |
What it solves |
|---|---|
|
Optimizes outer margins via scipy — replaces |
|
Content-aware layout that prevents text overflow |
|
Sets responsive x-axis margins based on data range |
|
Sets responsive y-axis margins based on data range |
|
Adds (a), (b), (c) labels with auto-positioning for ylabels |
|
Creates |
|
Fixes tick decimal places for publication-ready labels |
|
Creates point-based offsets for precise text positioning |
|
Merges multiple axes bounding boxes into one |
Annotation & Formatting¶
Dartwork-mpl includes several helpers that automate tedious formatting tasks.
Auto-aligned panel labels (label_axes)¶
Manually positioning (a), (b), (c) labels across panels with different y-axis label lengths often results in misaligned text. dm.label_axes(axes) calculates the bounding boxes of your y-labels and perfectly aligns the panel labels to the leftmost edge.
Interactive visualizer — drag the slider to compare:
Consistent tick decimals (set_decimal)¶
Raw matplotlib ticks can sometimes mix integers and floats (e.g. 0.5, 1, 1.5), which looks unprofessional. dm.set_decimal() forces consistent decimal places for publication-ready formatting.
Bidirectional arrow axes (arrow_axis)¶
For conceptual or qualitative plots (like “Risk vs Return”), drawing bidirectional label axes manually is surprisingly difficult in matplotlib. dm.arrow_axis() handles the positioning, offsets, and arrows automatically.
Typography¶
import matplotlib.pyplot as plt
import dartwork_mpl as dm
dm.style.use("scientific-kr") # English/Korean fonts set together
fig, ax = plt.subplots(figsize=(dm.cm2in(9), dm.cm2in(6)), dpi=300)
ax.plot([0, 1, 2], [0, 1, 0.4], color="oc.green6", lw=dm.lw(0.5))
ax.set_title("Experiment result", fontsize=dm.fs(2), fontweight=dm.fw(1))
ax.set_xlabel("Time", fontsize=dm.fs(0))
ax.set_ylabel("Response", fontsize=dm.fs(0))
dm.simple_layout(fig)
# Preview bundled fonts
dm.plot_fonts(ncols=4, font_size=12)
Scaling helpers:
Helper |
What it does |
|---|---|
|
Font size = base size + |
|
Weight = base weight + |
|
Line width relative to |
See Font Families for the full font catalog and Font Utilities for detailed usage.
See also¶
Next → Save and Validation — multi-format export and automatic visual quality checks
API › Layout Utilities for all layout helper functions and arguments
Colors and Colormaps for named colors and gradients