% File: numodel-plot-manual.tex % Standalone manual for the numodel-plot package. \documentclass{ltxdoc} \usepackage[left=3.5cm, right=2cm, top=2cm, bottom=2cm, marginparwidth=3.5cm, marginparsep=0.3cm]{geometry} \usepackage{numodel-plot} \usepackage{subcaption} \EnableCrossrefs \CodelineIndex % Fonts. fontspec + unicode-math require LuaLaTeX. \usepackage{fontspec} \setmainfont{Arial} \usepackage{unicode-math} \setmathfont{Lete Sans Math} \setmonofont{Fira Mono} % Breakable inline verbatim (see numodel-manual.tex for rationale). \usepackage{fvextra} \AtBeginDocument{% \MakeShortVerb{\|}% \DeleteShortVerb{\|}% \DefineShortVerb[breaklines,breakanywhere]{\|}% } % tcolorbox+listings for example boxes that both display and execute. \usepackage{tcolorbox} \tcbuselibrary{listings,skins,breakable} \lstdefinestyle{numodelcode}{ basicstyle=\small\ttfamily, breaklines=true, columns=fullflexible, keepspaces=true, showstringspaces=false, } \NewTCBListing{plotexample}{}{% enhanced, breakable, listing style=numodelcode, colback=black!3, colframe=black!50, boxrule=0.4pt, arc=2pt, before skip=10pt, after skip=10pt, } \begin{document} \CheckSum{0} \changes{v0.1}{2026/04/24}{Initial version, extracted from internal project sources.} \changes{v0.2.0}{2026/05/16}{l3build workflow; bundle structure with numodel; \cs{drawplot} now invokes \cs{calcplotdims} internally; default axis-label-format renamed to \texttt{ieee}.} \changes{v0.3.0}{2026/05/17}{Fix \cs{par}-token leak in \cs{calcplotdims} that bloated the picture bounding box inside horizontal boxes; fix \texttt{grid / unknown} key syntax so custom \texttt{grid=} values work; move \texttt{legend pos=outer north east} into the \texttt{numodel/axis} style so it can be overridden; add rendered example plots throughout the manual.} \changes{v0.4.0}{2026/05/19}{Version-sync release with \textsf{numodel}~v0.4.0; no functional changes to \textsf{numodel-plot} itself. Example files renamed from \texttt{numodel-plot-simple.tex}/\texttt{numodel-plot-scaled.tex} to \texttt{numodel-plot-example-basic.tex}/\texttt{numodel-plot-example-scaled.tex}.} \changes{v0.5.0}{2026/05/23}{Version-sync release with \textsf{numodel}~v0.5.0; no functional changes to \textsf{numodel-plot} itself.} \GetFileInfo{numodel-plot.sty} \DoNotIndex{\newcommand,\newenvironment,\def,\edef,\let,\global, \RequirePackage,\usepgfplotslibrary,\pgfplotsset,\ProvidesPackage, \NeedsTeXFormat,\makeatletter,\makeatother,\endinput,\providecommand, \NewDocumentCommand,\ExplSyntaxOn,\ExplSyntaxOff,\ifnum,\ifdefined, \fi,\else,\relax,\undefined,\fpeval,\si,\qty,\num,\penalty,\nobreak, \begin,\end,\thinspace} \title{The \textsf{numodel-plot} package\thanks{This document corresponds to \textsf{numodel-plot}~\fileversion, dated \today.}} \author{Paul Zuurbier \\ \texttt{mail@paulzuurbier.nl}} \date{\today} \maketitle \begin{abstract} A PGFPlots engine that auto-sizes plots to a whole number of tick intervals, supports configurable axis-label formats (IEEE-style by default; ISO 80000-1 also supported), and automatically selects label placement for 1-, 2-, and 4-quadrant graphs. Part of the \textsf{numodel} package suite, but can be loaded standalone as an independent PGFPlots styling layer. \end{abstract} \tableofcontents \section{Introduction} \textsf{numodel-plot} fills a gap between bare PGFPlots and the heavy styling required for physics-teaching material: it sizes every axis to an integer number of centimetre ticks, lays out the axis origin according to which quadrants of the coordinate plane contain data, and renders axis labels as either |quantity (unit)| (IEEE, the default) or one of four alternative conventions selectable at package level. It was extracted from a Dutch high-school physics test set where uniform plot appearance across hundreds of graphs is more valuable than per-graph tweaking, and hence adopts an opinionated default style. Users who need one-off deviations are expected to drop to plain PGFPlots with the variable macros |\xmin|, |\xmax|, \ldots{} exposed by this package. \newpage \section{Usage} Minimum working example (assuming |\usepackage{numodel-plot}| in the preamble): \begin{plotexample} \def\xmin{0} \def\xmax{10} \def\ymin{0} \def\ymax{5} \def\xlabelqty{t} \def\xlabelunit{\s} \def\ylabelqty{v} \def\ylabelunit{\m\per\s} \drawplot{\addplot[domain=\xmin:\xmax]{0.5*x};} \end{plotexample} The user sets the data range (|\xmin|\ldots|\ymax|) and optionally a quantity symbol plus \textsf{siunitx} unit for each axis. |\drawplot| internally calls |\calcplotdims| to round the range to a clean tick lattice and compute the axis size in centimetres, then renders a full |tikzpicture|+|axis| environment whose body is the argument (one or more |\addplot| lines). Labels are built automatically from |\xlabelqty|+|\xlabelunit| (and likewise for the $y$-axis). If the data magnitude exceeds $10^{4}$ or is below $10^{-2}$, a factor~$10^{n}$ is injected into the label and PGFPlots' own |scaled ticks| are configured so that tick numbers remain small. In the next example the data magnitude is $5\times10^{7}$ and the unit |\mega\joule\per\kilo\gram| already carries two engineering prefixes; the package extracts every prefix and combines them with the magnitude into a single power-of-ten factor: \begin{plotexample} \def\xmin{0} \def\xmax{10} \def\ymin{0} \def\ymax{5e7} \def\xlabelqty{m} \def\xlabelunit{\kilo\gram} \def\ylabelqty{E} \def\ylabelunit{\mega\joule\per\kilo\gram} \drawplot{\addplot[domain=\xmin:\xmax]{5e6*x};} \end{plotexample} Users preferring full control can omit |\xlabelqty|/|\xlabelunit| and set |\xlabel|/|\ylabel| directly; the package will use them verbatim. \section{Configuration} \DescribeMacro{\numodelplotsetup} Configuration is set through a single key--value interface: \begin{quote} \begin{verbatim} \numodelplotsetup{axis-label-format=ieee, grid=mm-dots} \end{verbatim} \end{quote} \subsection{Keys} \begin{description} \item[\texttt{axis-label-format}] Default |ieee|. Determines the notation emitted for axis labels built from |\xlabelqty| and |\xlabelunit|: \begin{center} \begin{tabular}{lll} \texttt{ieee} & \verb|v (m/s)| & IEEE (default) \\ \texttt{iso} & \verb|v / (m/s)| & ISO 80000-1 \\ \texttt{brackets} & \verb|v [m/s]| & older physics convention \\ \texttt{qty-only} & \verb|v| & quantity symbol only \\ \texttt{unit-only} & \verb|m/s| & unit only \\ \end{tabular} \end{center} When scaling is applied (data exceeds $10^{4}$ or below $10^{-2}$), the factor is integrated into the label, e.g.\ \verb|v (10^4 m/s)| for IEEE. Under |qty-only| the exponent remains in PGFPlots' scaled-tick label instead (otherwise the scale information would be lost). \item[\texttt{grid}] Default |mm-dots| (black millimetre dots, matching engineering millimetre paper). Accepts |none|, or any PGFPlots style list which will be passed verbatim to the |numodel/grid| style. \item[\texttt{xcmmax}, \texttt{ycmmax}] Maximum axis width and height in centimetres (defaults 12 and 10). \end{description} The first three axis-label formats render as follows. Each plot uses |\numodelplotsetup{xcmmax=3, ycmmax=3}| so the axis itself is trimmed to a 2~cm by 3~cm tick lattice (the package's invariant 1~cm major-grid spacing is preserved): \begin{plotexample} \captionsetup{type=figure}% \begin{subfigure}{0.33\textwidth}\centering \numodelplotsetup{xcmmax=3, ycmmax=3, axis-label-format=ieee}% \def\xmin{0}\def\xmax{10}\def\ymin{0}\def\ymax{5}% \def\xlabelqty{v}\def\xlabelunit{\m\per\s}% \def\ylabelqty{F}\def\ylabelunit{\N}% \drawplot{\addplot[domain=\xmin:\xmax,thick]{0.4*x};} \caption*{\texttt{ieee}} \end{subfigure}\hspace{-1cm}% \begin{subfigure}{0.33\textwidth}\centering \numodelplotsetup{xcmmax=3, ycmmax=3, axis-label-format=iso}% \def\xmin{0}\def\xmax{10}\def\ymin{0}\def\ymax{5}% \def\xlabelqty{v}\def\xlabelunit{\m\per\s}% \def\ylabelqty{F}\def\ylabelunit{\N}% \drawplot{\addplot[domain=\xmin:\xmax,thick]{0.4*x};} \caption*{\texttt{iso}} \end{subfigure}\hspace{-1cm}% \begin{subfigure}{0.33\textwidth}\centering \numodelplotsetup{xcmmax=3, ycmmax=3, axis-label-format=brackets}% \def\xmin{0}\def\xmax{10}\def\ymin{0}\def\ymax{5}% \def\xlabelqty{v}\def\xlabelunit{\m\per\s}% \def\ylabelqty{F}\def\ylabelunit{\N}% \drawplot{\addplot[domain=\xmin:\xmax,thick]{0.4*x};} \caption*{\texttt{brackets}} \end{subfigure} \end{plotexample} \numodelplotsetup{axis-label-format=ieee}% Three grid variants, sized the same way: \begin{plotexample} \captionsetup{type=figure}% \begin{subfigure}{0.33\textwidth}\centering \numodelplotsetup{xcmmax=3, ycmmax=3, grid=mm-dots}% \def\xmin{0}\def\xmax{10}\def\ymin{0}\def\ymax{5}% \def\xlabelqty{t}\def\xlabelunit{\s}% \def\ylabelqty{v}\def\ylabelunit{\m\per\s}% \drawplot{\addplot[domain=\xmin:\xmax,thick]{0.5*x};} \caption*{\texttt{mm-dots}} \end{subfigure}\hspace{-1cm}% \begin{subfigure}{0.33\textwidth}\centering \numodelplotsetup{xcmmax=3, ycmmax=3, grid=none}% \def\xmin{0}\def\xmax{10}\def\ymin{0}\def\ymax{5}% \def\xlabelqty{t}\def\xlabelunit{\s}% \def\ylabelqty{v}\def\ylabelunit{\m\per\s}% \drawplot{\addplot[domain=\xmin:\xmax,thick]{0.5*x};} \caption*{\texttt{none}} \end{subfigure}\hspace{-1cm}% \begin{subfigure}{0.33\textwidth}\centering \numodelplotsetup{xcmmax=3, ycmmax=3, grid={grid=major, grid style={gray, very thin}}}% \def\xmin{0}\def\xmax{10}\def\ymin{0}\def\ymax{5}% \def\xlabelqty{t}\def\xlabelunit{\s}% \def\ylabelqty{v}\def\ylabelunit{\m\per\s}% \drawplot{\addplot[domain=\xmin:\xmax,thick]{0.5*x};} \caption*{\texttt{\{major,gray\}}} \end{subfigure} \end{plotexample} \numodelplotsetup{grid=mm-dots}% \subsection{PGFPlots styles} The package defines three PGFPlots styles applied by |\drawplot|: |numodel/grid|, |numodel/ticks|, |numodel/axis|. These can be overridden wholesale through |\pgfplotsset{numodel/axis/.style={...}}| from the calling preamble, giving projects a single choke point for house-style customisation. One override per style, on the same plot: \begin{plotexample} \captionsetup{type=figure}% \begin{subfigure}{0.33\textwidth}\centering \pgfplotsset{numodel/grid/.style={grid=major, grid style={gray!50, very thin}}}% \numodelplotsetup{xcmmax=3, ycmmax=3}% \def\xmin{0}\def\xmax{10}\def\ymin{0}\def\ymax{5}% \def\xlabelqty{t}\def\xlabelunit{\s}% \def\ylabelqty{v}\def\ylabelunit{\m\per\s}% \drawplot{\addplot[domain=\xmin:\xmax,thick]{0.5*x};} \caption*{\texttt{numodel/grid}} \end{subfigure}\hspace{-1cm}% \begin{subfigure}{0.33\textwidth}\centering \pgfplotsset{numodel/ticks/.style={tick style={red, thick}, minor tick num=4}}% \numodelplotsetup{xcmmax=3, ycmmax=3}% \def\xmin{0}\def\xmax{10}\def\ymin{0}\def\ymax{5}% \def\xlabelqty{t}\def\xlabelunit{\s}% \def\ylabelqty{v}\def\ylabelunit{\m\per\s}% \drawplot{\addplot[domain=\xmin:\xmax,thick]{0.5*x};} \caption*{\texttt{numodel/ticks}} \end{subfigure}\hspace{-1cm}% \begin{subfigure}{0.33\textwidth}\centering \pgfplotsset{numodel/axis/.append style={axis line style={->}}}% \numodelplotsetup{xcmmax=3, ycmmax=3}% \def\xmin{0}\def\xmax{10}\def\ymin{0}\def\ymax{5}% \def\xlabelqty{t}\def\xlabelunit{\s}% \def\ylabelqty{v}\def\ylabelunit{\m\per\s}% \drawplot{\addplot[domain=\xmin:\xmax,thick]{0.5*x};} \caption*{\texttt{numodel/axis}} \end{subfigure} \end{plotexample} \section{Public API} \DescribeMacro{\drawplot} Renders a |tikzpicture| containing an |axis| whose body is the single mandatory argument. Typically a block of |\addplot| and |\addlegendentry| lines. Calls |\calcplotdims| internally, so the user does not need to invoke it separately. \DescribeMacro{\calcplotdims} Reads |\xmin|, |\xmax|, |\ymin|, |\ymax|, and (if set) |\xlabelqty|/|\xlabelunit|/|\ylabelqty|/|\ylabelunit|. Writes |\xcm|, |\ycm|, |\xtickdistance|, |\ytickdistance|, |\xlabel|, |\ylabel|, and may rewrite |\xmin|\ldots|\ymax| to align with the tick lattice (floor/ceil to the nearest tick). It also appends axis-positioning styles to |numodel/axis| based on which quadrants the data occupies. |\drawplot| invokes it automatically; expose for advanced cases where dimensions are needed before rendering (overlay TikZ, custom |axis| environment). \DescribeMacro{\xlabelqty}\DescribeMacro{\xlabelunit} \DescribeMacro{\ylabelqty}\DescribeMacro{\ylabelunit} Input hooks for automatic label construction. |\xlabelqty| is the mathematical quantity symbol (e.g.\ |v|, |F|, |E|); the corresponding |\xlabelunit| is a bare \textsf{siunitx} unit macro sequence (e.g.\ |\m\per\s|, |\J|, |\N\m|) \emph{without} a surrounding |\si{}| or |\qty{}| wrapper. \DescribeMacro{\xcmmax}\DescribeMacro{\ycmmax} Maximum axis dimensions in centimetres. Can be set directly through |\def| for backwards compatibility, or via |\numodelplotsetup|. \DescribeMacro{\qtyPlain} Like \textsf{siunitx}'s |\qty| but prints no numeric mantissa when the final output after prefix extraction has mantissa~1 and exponent~0. Used internally to inject scale factors into axis labels; exposed because the same need recurs in other scaled-axis contexts. \DescribeMacro{\pzuIfUnitNonEngTF} Boolean conditional testing whether a unit macro sequence contains a non-engineering SI prefix (|\centi|, |\deci|, |\deca|, |\hecto|, plus the \textsf{siunitx} short forms |\cm|, |\dm|, |\hPa|, \ldots). Used internally to suppress scaling on units where the user has already encoded the order of magnitude; exposed for completeness. \section{Requirements} \textsf{numodel-plot} requires \textsf{expl3}, \textsf{xparse}, \textsf{l3keys2e}, \textsf{siunitx} (mandatory, for quantities in labels), and \textsf{pgfplots} (with the |fillbetween| library). LuaLaTeX is not required at the plot layer (it is required by the sibling \textsf{numodel} package). \end{document}