% \iffalse meta-comment
%
% numodel-plot.dtx
% Copyright (C) 2026 Paul Zuurbier <mail@paulzuurbier.nl>
%
% This work may be distributed and/or modified under the conditions
% of the LaTeX Project Public License, either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in https://www.latex-project.org/lppl.txt
%
% This work has the LPPL maintenance status 'maintained'.
% The Current Maintainer of this work is Paul Zuurbier.
%
% This work consists of the files numodel-plot.dtx and numodel-plot.ins,
% and the derived file numodel-plot.sty.
%
%<*driver>
\ProvidesFile{numodel-plot.dtx}[2026/05/19 v0.4.0 numodel-plot documentation]
\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
\RecordChanges
\begin{document}
\DocInput{numodel-plot.dtx}
\end{document}
%</driver>
%
% \fi
%
% \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}.}
%
% \GetFileInfo{numodel-plot.dtx}
%
% \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.
%
% \section{Usage}
%
% Minimum working example:
% \begin{quote}
% \begin{verbatim}
% \usepackage{numodel-plot}
% ...
% \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{verbatim}
% \end{quote}
% renders as
% \begin{center}
% \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{center}
% 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{quote}
% \begin{verbatim}
% \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{verbatim}
% \end{quote}
% \begin{center}
% \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{center}
%
% 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{center}
% \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{center}
% \numodelplotsetup{axis-label-format=ieee}%
%
% Three grid variants, sized the same way:
% \begin{center}
% \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{center}
% \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{center}
% \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{center}
%
% \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).
%
% \StopEventually{}
%
% \section{Implementation}
%
% All internal helper macros use the \texttt{\textbackslash nmp@\ldots}
% prefix (standard LaTeX internal convention).  Because the package
% may be loaded through |\input| as well as |\usepackage|, an
% explicit |\makeatletter| wraps the body so that |@| is a letter
% regardless of caller.
%
%    \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{numodel-plot}[2026/05/19 v0.4.0 Auto-sizing PGFPlots engine]

\makeatletter

\RequirePackage{expl3}
\RequirePackage{xparse}
\RequirePackage{l3keys2e}
\RequirePackage{siunitx}
\RequirePackage{pgfplots}
\usepgfplotslibrary{fillbetween}

\pgfplotsset{
  compat=1.18,
  numodel/grid/.style={
    grid=both,
    grid style={
      line width=0.5pt,
      draw=black,
      line cap=round,
      dash pattern=on 0pt off 1mm
      },
    major grid style={
      line width=0.5pt,
      draw=black,
      line cap=round,
      dash pattern=on 0pt off 1mm}
      },
  numodel/ticks/.style={
    tick style={black}
  },
  numodel/axis/.style={
    xticklabel style={/pgf/number format/.cd, fixed, precision=6, fixed zerofill=false, use comma, 1000 sep={\,}, /tikz/.cd, text depth=0.3ex},
    yticklabel style={/pgf/number format/.cd, fixed, precision=6, fixed zerofill=false, use comma, 1000 sep={\,}, /tikz/.cd, text depth=0.3ex},
    axis lines=left,
    xlabel near ticks,
    ylabel near ticks,
    ylabel style={rotate=-90},
    axis line style={-}
  }
}

\ExplSyntaxOn

\tl_new:N \g__numodelplot_axislabel_tl
\tl_gset:Nn \g__numodelplot_axislabel_tl {ieee}

\providecommand{\xcmmax}{12}
\providecommand{\ycmmax}{10}

\keys_define:nn { numodel-plot }
  {
    axis-label-format .choice:,
    axis-label-format / iso       .code:n =
      \tl_gset:Nn \g__numodelplot_axislabel_tl {iso},
    axis-label-format / ieee      .code:n =
      \tl_gset:Nn \g__numodelplot_axislabel_tl {ieee},
    axis-label-format / brackets  .code:n =
      \tl_gset:Nn \g__numodelplot_axislabel_tl {brackets},
    axis-label-format / qty-only  .code:n =
      \tl_gset:Nn \g__numodelplot_axislabel_tl {qty-only},
    axis-label-format / unit-only .code:n =
      \tl_gset:Nn \g__numodelplot_axislabel_tl {unit-only},

    grid .choice:,
    grid / mm-dots .code:n =
      \pgfplotsset{ numodel/grid/.style={
        grid=both,
        grid~style={line~width=0.5pt, draw=black, line~cap=round,
          dash~pattern=on~0pt~off~1mm},
        major~grid~style={line~width=0.5pt, draw=black, line~cap=round,
          dash~pattern=on~0pt~off~1mm}
      } },
    grid / none .code:n =
      \pgfplotsset{ numodel/grid/.style={grid=none} },
    grid / unknown .code:n =
      \pgfplotsset{ numodel/grid/.style={#1} },

    xcmmax .code:n = \def\xcmmax{#1},
    ycmmax .code:n = \def\ycmmax{#1},
  }

\NewDocumentCommand{\numodelplotsetup}{m}
  { \keys_set:nn { numodel-plot } {#1} }

\ExplSyntaxOff

\ExplSyntaxOn
\bool_new:N \g__pzu_suppress_qp_bool
\cs_new_eq:NN \__pzu_orig_int_output:nnn \__siunitx_number_output_integer:nnn
\cs_new_eq:NN \__pzu_orig_qty_print_unit:n \__siunitx_quantity_print_unit:n
\NewDocumentCommand{\qtyPlain}{O{} m m}{%
  \group_begin:
    \bool_gset_false:N \g__pzu_suppress_qp_bool
    \cs_set:Npn \__siunitx_number_output_integer:nnn ##1##2##3 {
      \bool_lazy_all:nTF
        {
          { \str_if_eq_p:nn { ##1 . ##2 } { 1. } }
          { \str_if_eq_p:nn {##3} { 0 } }
          { ! \l__siunitx_number_zero_exponent_bool }
          { ! \l__siunitx_number_unity_mantissa_bool }
        }
        { \bool_gset_true:N \g__pzu_suppress_qp_bool }
        { \__pzu_orig_int_output:nnn {##1} {##2} {##3} }
    }
    \cs_set_protected:Npn \__siunitx_quantity_print_unit:n ##1 {
      \bool_if:NTF \l__siunitx_quantity_break_bool
        { \penalty \binoppenalty }
        { \nobreak }
      \bool_if:NF \g__pzu_suppress_qp_bool
        { \tl_use:N \l__siunitx_quantity_product_tl }
      \siunitx_print_unit:n {##1}
    }
    \qty[#1]{#2}{#3}%
  \group_end:
}
\ExplSyntaxOff

\ExplSyntaxOn
\bool_new:N \l__pzu_noneng_bool
\tl_const:Nn \c__pzu_noneng_tokens_tl {
  \centi \deci \deca \hecto
  \cm \cg \cL
  \dm \dg \dL
  \dam \dag \daL
  \hm \hg \hL \hPa
}
\prg_new_protected_conditional:Npnn \__pzu_unit_has_noneng:n #1 { TF } {
  \bool_set_false:N \l__pzu_noneng_bool
  \tl_map_inline:Nn \c__pzu_noneng_tokens_tl {
    \tl_if_in:nnT {#1} {##1}
      { \bool_set_true:N \l__pzu_noneng_bool \tl_map_break: }
  }
  \bool_if:NTF \l__pzu_noneng_bool
    { \prg_return_true: }
    { \prg_return_false: }
}
\prg_generate_conditional_variant:Nnn \__pzu_unit_has_noneng:n { o } { TF }
\NewDocumentCommand{\pzuIfUnitNonEngTF}{m m m}{%
  \__pzu_unit_has_noneng:oTF {#1} {#2} {#3}%
}
\ExplSyntaxOff

\ExplSyntaxOn

\cs_new_protected:Npn \__numodelplot_xlabel_emit_unscaled:
  {
    \str_case:Vn \g__numodelplot_axislabel_tl
      {
        {iso}
          { \def \xlabel { $\nmp@savedxlabelqty \, /\, ( \si{\nmp@savedxlabelunit} )$ } }
        {ieee}
          { \def \xlabel { $\nmp@savedxlabelqty \,( \si{\nmp@savedxlabelunit} )$ } }
        {brackets}
          { \def \xlabel { $\nmp@savedxlabelqty \,[ \si{\nmp@savedxlabelunit} ]$ } }
        {qty-only}
          { \def \xlabel { $\nmp@savedxlabelqty$ } }
        {unit-only}
          { \def \xlabel { $\si{\nmp@savedxlabelunit}$ } }
      }
  }

\cs_new_protected:Npn \__numodelplot_xlabel_emit_scaled:
  {
    \str_case:Vn \g__numodelplot_axislabel_tl
      {
        {iso}
          {
            \edef\xlabel{\noexpand$\noexpand\nmp@savedxlabelqty
              \noexpand\,/\noexpand\,(\noexpand\qtyPlain
              [evaluate-expression=false, round-mode=none,
               prefix-mode=extract-exponent,
               extract-mass-in-kilograms=true,
               print-zero-exponent=false,
               print-unity-mantissa=false]%
              {1e\nmp@xlabelexpuse}{\noexpand\nmp@savedxlabelunit})\noexpand$}%
          }
        {ieee}
          {
            \edef\xlabel{\noexpand$\noexpand\nmp@savedxlabelqty
              \noexpand\,(\noexpand\qtyPlain
              [evaluate-expression=false, round-mode=none,
               prefix-mode=extract-exponent,
               extract-mass-in-kilograms=true,
               print-zero-exponent=false,
               print-unity-mantissa=false]%
              {1e\nmp@xlabelexpuse}{\noexpand\nmp@savedxlabelunit})\noexpand$}%
          }
        {brackets}
          {
            \edef\xlabel{\noexpand$\noexpand\nmp@savedxlabelqty
              \noexpand\,[\noexpand\qtyPlain
              [evaluate-expression=false, round-mode=none,
               prefix-mode=extract-exponent,
               extract-mass-in-kilograms=true,
               print-zero-exponent=false,
               print-unity-mantissa=false]%
              {1e\nmp@xlabelexpuse}{\noexpand\nmp@savedxlabelunit}]\noexpand$}%
          }
        {qty-only}
          { \def \xlabel { $\nmp@savedxlabelqty$ } }
        {unit-only}
          {
            \edef\xlabel{\noexpand$\noexpand\qtyPlain
              [evaluate-expression=false, round-mode=none,
               prefix-mode=extract-exponent,
               extract-mass-in-kilograms=true,
               print-zero-exponent=false,
               print-unity-mantissa=false]%
              {1e\nmp@xlabelexpuse}{\noexpand\nmp@savedxlabelunit}\noexpand$}%
          }
      }
  }

\cs_new_protected:Npn \__numodelplot_ylabel_emit_unscaled:
  {
    \str_case:Vn \g__numodelplot_axislabel_tl
      {
        {iso}
          { \def \ylabel { $\nmp@savedylabelqty \, /\, ( \si{\nmp@savedylabelunit} )$ } }
        {ieee}
          { \def \ylabel { $\nmp@savedylabelqty \,( \si{\nmp@savedylabelunit} )$ } }
        {brackets}
          { \def \ylabel { $\nmp@savedylabelqty \,[ \si{\nmp@savedylabelunit} ]$ } }
        {qty-only}
          { \def \ylabel { $\nmp@savedylabelqty$ } }
        {unit-only}
          { \def \ylabel { $\si{\nmp@savedylabelunit}$ } }
      }
  }

\cs_new_protected:Npn \__numodelplot_ylabel_emit_scaled:
  {
    \str_case:Vn \g__numodelplot_axislabel_tl
      {
        {iso}
          {
            \edef\ylabel{\noexpand$\noexpand\nmp@savedylabelqty
              \noexpand\,/\noexpand\,(\noexpand\qtyPlain
              [evaluate-expression=false, round-mode=none,
               prefix-mode=extract-exponent,
               extract-mass-in-kilograms=true,
               print-zero-exponent=false,
               print-unity-mantissa=false]%
              {1e\nmp@ylabelexpuse}{\noexpand\nmp@savedylabelunit})\noexpand$}%
          }
        {ieee}
          {
            \edef\ylabel{\noexpand$\noexpand\nmp@savedylabelqty
              \noexpand\,(\noexpand\qtyPlain
              [evaluate-expression=false, round-mode=none,
               prefix-mode=extract-exponent,
               extract-mass-in-kilograms=true,
               print-zero-exponent=false,
               print-unity-mantissa=false]%
              {1e\nmp@ylabelexpuse}{\noexpand\nmp@savedylabelunit})\noexpand$}%
          }
        {brackets}
          {
            \edef\ylabel{\noexpand$\noexpand\nmp@savedylabelqty
              \noexpand\,[\noexpand\qtyPlain
              [evaluate-expression=false, round-mode=none,
               prefix-mode=extract-exponent,
               extract-mass-in-kilograms=true,
               print-zero-exponent=false,
               print-unity-mantissa=false]%
              {1e\nmp@ylabelexpuse}{\noexpand\nmp@savedylabelunit}]\noexpand$}%
          }
        {qty-only}
          { \def \ylabel { $\nmp@savedylabelqty$ } }
        {unit-only}
          {
            \edef\ylabel{\noexpand$\noexpand\qtyPlain
              [evaluate-expression=false, round-mode=none,
               prefix-mode=extract-exponent,
               extract-mass-in-kilograms=true,
               print-zero-exponent=false,
               print-unity-mantissa=false]%
              {1e\nmp@ylabelexpuse}{\noexpand\nmp@savedylabelunit}\noexpand$}%
          }
      }
  }

\ExplSyntaxOff

\newcommand{\pznmpAppendXScaleTicks}[1]{%
  \pgfplotsset{numodel/axis/.append style={scaled x ticks=base 10:#1}}%
}
\newcommand{\pznmpAppendYScaleTicks}[1]{%
  \pgfplotsset{numodel/axis/.append style={scaled y ticks=base 10:#1}}%
}
\newcommand{\pznmpSuppressXScaleLabel}{%
  \pgfplotsset{numodel/axis/.append style={every x tick scale label/.style={opacity=0, inner sep=0pt, at={(0,0)}}}}%
}
\newcommand{\pznmpSuppressYScaleLabel}{%
  \pgfplotsset{numodel/axis/.append style={every y tick scale label/.style={opacity=0, inner sep=0pt, at={(0,0)}}}}%
}

\ExplSyntaxOn

\cs_new_protected:Npn \__numodelplot_suppress_xscale_label:
  {
    \str_if_eq:VnF \g__numodelplot_axislabel_tl {qty-only}
      { \pznmpSuppressXScaleLabel }
  }
\cs_new_protected:Npn \__numodelplot_suppress_yscale_label:
  {
    \str_if_eq:VnF \g__numodelplot_axislabel_tl {qty-only}
      { \pznmpSuppressYScaleLabel }
  }

\ExplSyntaxOff

\ExplSyntaxOn
\NewDocumentCommand{\xlabelbuild}{}{
  \let\nmp@savedxlabelqty\xlabelqty
  \let\nmp@savedxlabelunit\xlabelunit
  \pzuIfUnitNonEngTF{\xlabelunit}{
    \__numodelplot_xlabel_emit_unscaled:
    \def\nmp@xlabelexp{0}
  }{
    \edef\nmp@xmag{\fpeval{max(abs(\xmin),abs(\xmax))}}
    \edef\nmp@xlabelexp{\fpeval{
      \nmp@xmag > 0 ? 3*floor(ln(\nmp@xmag)/ln(10)/3) : 0
    }}
    \ifnum\nmp@xlabelexp=0\relax
      \__numodelplot_xlabel_emit_unscaled:
    \else
      \edef\nmp@xlabelexpuse{\nmp@xlabelexp}
      \__numodelplot_xlabel_emit_scaled:
      \edef\nmp@xlabelexpneg{\fpeval{-\nmp@xlabelexp}}
      \pznmpAppendXScaleTicks{\nmp@xlabelexpneg}
      \__numodelplot_suppress_xscale_label:
    \fi
  }
  \global\let\xlabelqty\undefined
  \global\let\xlabelunit\undefined
}

\NewDocumentCommand{\ylabelbuild}{}{
  \let\nmp@savedylabelqty\ylabelqty
  \let\nmp@savedylabelunit\ylabelunit
  \pzuIfUnitNonEngTF{\ylabelunit}{
    \__numodelplot_ylabel_emit_unscaled:
    \def\nmp@ylabelexp{0}
  }{
    \edef\nmp@ymag{\fpeval{max(abs(\ymin),abs(\ymax))}}
    \edef\nmp@ylabelexp{\fpeval{
      \nmp@ymag > 0 ? 3*floor(ln(\nmp@ymag)/ln(10)/3) : 0
    }}
    \ifnum\nmp@ylabelexp=0\relax
      \__numodelplot_ylabel_emit_unscaled:
    \else
      \edef\nmp@ylabelexpuse{\nmp@ylabelexp}
      \__numodelplot_ylabel_emit_scaled:
      \edef\nmp@ylabelexpneg{\fpeval{-\nmp@ylabelexp}}
      \pznmpAppendYScaleTicks{\nmp@ylabelexpneg}
      \__numodelplot_suppress_yscale_label:
    \fi
  }
  \global\let\ylabelqty\undefined
  \global\let\ylabelunit\undefined
}
\ExplSyntaxOff

\newcommand{\calcplotdims}{%
\edef\nmp@xlog{\fpeval{floor(ln(\xmax-\xmin)/ln(10))}}%
\edef\nmp@ylog{\fpeval{floor(ln(\ymax-\ymin)/ln(10))}}%
\edef\nmp@xS{\fpeval{(\xmax-\xmin) / (10^\nmp@xlog)}}%
\edef\nmp@yS{\fpeval{(\ymax-\ymin) / (10^\nmp@ylog)}}%
\edef\xcm{\fpeval{%
  \nmp@xS <= \xcmmax/10 ? 10 * \nmp@xS :%
  \nmp@xS <= \xcmmax/5  ?  5 * \nmp@xS :%
  \nmp@xS <= \xcmmax/4  ?  4 * \nmp@xS :%
  \nmp@xS <= \xcmmax/2  ?  2 * \nmp@xS :%
  \nmp@xS <= \xcmmax    ?      \nmp@xS :%
                        \nmp@xS / 2%
}}%
\edef\ycm{\fpeval{%
  \nmp@yS <= \ycmmax/10 ? 10 * \nmp@yS :%
  \nmp@yS <= \ycmmax/5  ?  5 * \nmp@yS :%
  \nmp@yS <= \ycmmax/4  ?  4 * \nmp@yS :%
  \nmp@yS <= \ycmmax/2  ?  2 * \nmp@yS :%
  \nmp@yS <= \ycmmax    ?      \nmp@yS :%
                        \nmp@yS / 2%
}}%
\edef\nmp@xtickdiv{\fpeval{\nmp@xS / \xcm}}%
\edef\nmp@ytickdiv{\fpeval{\nmp@yS / \ycm}}%
\edef\xtickdistance{\fpeval{\nmp@xtickdiv * 10^\nmp@xlog}}%
\edef\ytickdistance{\fpeval{\nmp@ytickdiv * 10^\nmp@ylog}}%
\edef\xmax{\fpeval{ceil(\xmax / \xtickdistance) * \xtickdistance}}%
\edef\ymax{\fpeval{ceil(\ymax / \ytickdistance) * \ytickdistance}}%
\edef\xmin{\fpeval{floor(\xmin / \xtickdistance) * \xtickdistance}}%
\edef\ymin{\fpeval{floor(\ymin / \ytickdistance) * \ytickdistance}}%
\edef\xcm{\fpeval{(\xmax - \xmin) / \xtickdistance}}%
\edef\ycm{\fpeval{(\ymax - \ymin) / \ytickdistance}}%
\edef\nmp@yStraddles{\fpeval{(\ymin<0 && \ymax>0) ? 1 : 0}}%
\edef\nmp@yAllNonPos{\fpeval{\ymax<=0 ? 1 : 0}}%
\edef\nmp@xStraddles{\fpeval{(\xmin<0 && \xmax>0) ? 1 : 0}}%
\edef\nmp@xAllNonPos{\fpeval{\xmax<=0 ? 1 : 0}}%
\ifnum\nmp@yStraddles=1
  \pgfplotsset{numodel/axis/.append style={axis x line*=middle}}%
\else
  \ifnum\nmp@yAllNonPos=1
    \pgfplotsset{numodel/axis/.append style={axis x line*=top}}%
  \fi
\fi
\ifnum\nmp@xStraddles=1
  \pgfplotsset{numodel/axis/.append style={axis y line*=middle}}%
\else
  \ifnum\nmp@xAllNonPos=1
    \pgfplotsset{numodel/axis/.append style={axis y line*=right}}%
  \fi
\fi
\edef\nmp@axisMoved{\fpeval{(\nmp@yStraddles || \nmp@yAllNonPos || \nmp@xStraddles || \nmp@xAllNonPos) ? 1 : 0}}%
\ifnum\nmp@axisMoved=1
\pgfplotsset{numodel/axis/.append style={%
  every x tick scale label/.style={at={(xticklabel cs:1.05)}, anchor=south west},%
  every y tick scale label/.style={at={(yticklabel cs:1.05)}, anchor=south east}%
}}%
\fi
\edef\nmp@fourQuad{\fpeval{(\nmp@xStraddles && \nmp@yStraddles) ? 1 : 0}}%
\ifnum\nmp@fourQuad=1
\pgfplotsset{numodel/axis/.append style={%
  xlabel style={at={(xticklabel cs:1.05)}, anchor=west},%
  ylabel style={at={(yticklabel cs:1.05)}, anchor=south, rotate=0}%
}}%
\fi
\edef\nmp@qIandII{\fpeval{(\nmp@xStraddles && \ymin>=0) ? 1 : 0}}%
\ifnum\nmp@qIandII=1
\pgfplotsset{numodel/axis/.append style={%
  ylabel style={at={(yticklabel cs:1.05)}, anchor=south, rotate=0}%
}}%
\fi
\edef\nmp@qIIandIII{\fpeval{(\nmp@yStraddles && \nmp@xAllNonPos) ? 1 : 0}}%
\ifnum\nmp@qIIandIII=1
\pgfplotsset{numodel/axis/.append style={%
  xlabel style={at={(xticklabel cs:-0.05)}, anchor=east}%
}}%
\fi
\edef\nmp@qIIIandIV{\fpeval{(\nmp@xStraddles && \nmp@yAllNonPos) ? 1 : 0}}%
\ifnum\nmp@qIIIandIV=1
\pgfplotsset{numodel/axis/.append style={%
  ylabel style={at={(yticklabel cs:-0.05)}, anchor=north, rotate=0}%
}}%
\fi
\edef\nmp@qIVandI{\fpeval{(\nmp@yStraddles && \xmin>=0) ? 1 : 0}}%
\ifnum\nmp@qIVandI=1
\pgfplotsset{numodel/axis/.append style={%
  xlabel style={at={(xticklabel cs:1.05)}, anchor=west}%
}}%
\fi
\ifdefined\xlabelqty\ifdefined\xlabelunit\xlabelbuild\fi\fi
\ifdefined\ylabelqty\ifdefined\ylabelunit\ylabelbuild\fi\fi
}

\newcommand{\drawplot}[1]{
  \calcplotdims
  \begin{tikzpicture}
    \begin{axis}[
      numodel/grid,
      numodel/ticks,
      numodel/axis,
      xlabel=\xlabel,
      ylabel=\ylabel,
      xmin=\xmin, xmax=\xmax,
      ymin=\ymin, ymax=\ymax,
      xtick distance=\xtickdistance,
      ytick distance=\ytickdistance,
      width=\xcm cm,
      height=\ycm cm,
      scale only axis
    ]
      #1
    \end{axis}
  \end{tikzpicture}
}

\makeatother
%    \end{macrocode}
%
% \Finale
\endinput
