% pgflibrarypgfplots.autonode.code.tex -- autonomous PGFPlots label library.
% Copyright (C) 2026 Christophe Jorssen. LPPL 1.3c or later.
% Project library version 1.1.0, 2026-09-18.
%
% This file is intentionally written as generic TeX and follows PGFPlots'
% library naming convention. Load it with \usepgfplotslibrary{autonode} from
% LaTeX, plain TeX, or ConTeXt after the format's PGFPlots frontend is loaded.
% The numerical placement algorithms live in pgfplots-autonode.lua.
%
% Public entry points:
%   - /pgfplots/auto node placement
%   - /pgfplots/auto node algorithm=<greedy|repair|local-search|exact-small>
%   - /pgfplots/auto node debug=<false|summary|verbose|trace>
%   - \pgfplotsautonode[<keys>]{<text>}
%
% Design note:
% \pgfplotsautonode is meant to be used as a pgfplots trailing path command.
% Therefore its bracket argument is mandatory.  This avoids the fragile
% interaction between LaTeX optional-argument parsing and TikZ's path parser.

\expandafter\ifx\csname pgfplotsautonode@loaded\endcsname\relax
\else
  \expandafter\endinput
\fi
\expandafter\def\csname pgfplotsautonode@loaded\endcsname{1}

\expandafter\ifx\csname directlua\endcsname\relax
  \errmessage{pgfplots-autonode requires LuaTeX}
\fi

\expandafter\edef\csname pgfplotsautonode@oldatcatcode\endcsname{\the\catcode`\@}%
\catcode`\@=11\relax

\newcount\pgfplotsautonode@id
\newcount\pgfplotsautonode@debuglevel
\newcount\pgfplotsautonode@maxiterations
\newcount\pgfplotsautonode@exactmaxlabels
\newcount\pgfplotsautonode@exactmaxstates
\newcount\pgfplotsautonode@measureindex
\newif\ifpgfplotsautonode@sloped
\newif\ifpgfplotsautonode@allowupsidedown
\newif\ifpgfplotsautonode@allowoutside
\newif\ifpgfplotsautonode@showboundingboxes
\newif\ifpgfplotsautonode@showcandidates
\newif\ifpgfplotsautonode@showrejectedcandidates
\newif\ifpgfplotsautonode@placementenabled
\newif\ifpgfplotsautonode@missinghookwarned

\pgfplotsautonode@debuglevel=0\relax
\pgfplotsautonode@maxiterations=40\relax
\pgfplotsautonode@exactmaxlabels=10\relax
\pgfplotsautonode@exactmaxstates=50000\relax

\def\pgfplotsautonode@defaultcandidates{51}%
\def\pgfplotsautonode@defaultcandidatestrategy{around-preferred}%
\def\pgfplotsautonode@algorithm{repair}%
\def\pgfplotsautonode@bboxmode{axis-aligned}%
\def\pgfplotsautonode@failuremode{warn}%
\def\pgfplotsautonode@bordermargin{2pt}%
\def\pgfplotsautonode@overlaptolerance{0.2pt}%
\pgfplotsautonode@allowoutsidefalse
\pgfplotsautonode@allowupsidedownfalse
\pgfplotsautonode@showboundingboxesfalse
\pgfplotsautonode@showcandidatesfalse
\pgfplotsautonode@showrejectedcandidatesfalse
\pgfplotsautonode@placementenabledfalse
\pgfplotsautonode@missinghookwarnedfalse

% Load the Lua companion file. ConTeXt protects direct access to the kpathsea
% library and exposes its own resolver, while LaTeX and plain TeX expose kpse.
% The final fallback helps when the package is used from the current directory.
\directlua{
  local path
  if resolvers and resolvers.findfile then
    path = resolvers.findfile("pgfplots-autonode.lua", "lua")
        or resolvers.findfile("pgfplots-autonode.lua", "tex")
  elseif kpse and kpse.find_file then
    path = kpse.find_file("pgfplots-autonode.lua", "lua")
        or kpse.find_file("pgfplots-autonode.lua", "tex")
  end
  path = path or "pgfplots-autonode.lua"
  pgfplots_autonode = dofile(path)
}

% -----------------------------------------------------------------------------
% User keys for \pgfplotsautonode.
% -----------------------------------------------------------------------------
\pgfkeys{
  /pgfplots/autonode/.is family,
  /pgfplots/autonode/.cd,
  preferred pos/.store in=\pgfplotsautonode@preferredpos,
  min pos/.store in=\pgfplotsautonode@minpos,
  max pos/.store in=\pgfplotsautonode@maxpos,
  samples/.store in=\pgfplotsautonode@samples,
  candidates/.store in=\pgfplotsautonode@samples,
  candidate strategy/.is choice,
  candidate strategy/uniform/.code={\def\pgfplotsautonode@candidatestrategy{uniform}},
  candidate strategy/around preferred/.code={\def\pgfplotsautonode@candidatestrategy{around-preferred}},
  candidate strategy/around-preferred/.code={\def\pgfplotsautonode@candidatestrategy{around-preferred}},
  candidate strategy/adaptive/.code={\def\pgfplotsautonode@candidatestrategy{adaptive}},
  normal shift/.store in=\pgfplotsautonode@normalshift,
  clearance/.store in=\pgfplotsautonode@clearance,
  inner sep/.store in=\pgfplotsautonode@innersep,
  font/.store in=\pgfplotsautonode@font,
  node options/.code={\def\pgfplotsautonode@nodeopts{#1}},
  preferred weight/.store in=\pgfplotsautonode@preferredweight,
  overlap weight/.store in=\pgfplotsautonode@overlapweight,
  priority/.store in=\pgfplotsautonode@priority,
  sloped/.is if=pgfplotsautonode@sloped,
  sloped/.default=true,
  allow upside down/.is if=pgfplotsautonode@allowupsidedown,
  allow upside down/.default=true,
  default/.style={
    preferred pos=.5,
    min pos=0,
    max pos=1,
    samples=\pgfplotsautonode@defaultcandidates,
    candidate strategy=\pgfplotsautonode@defaultcandidatestrategy,
    normal shift=0pt,
    clearance=1pt,
    inner sep=1.5pt,
    sloped=false,
    allow upside down=false,
    font=,
    node options={fill=white,draw=none,text opacity=1},
    preferred weight=8,
    overlap weight=1000,
    priority=0
  },
  above/.style={normal shift=6pt},
  below/.style={normal shift=-6pt}
}

% -----------------------------------------------------------------------------
% pgfplots axis-level keys.
% -----------------------------------------------------------------------------
\pgfplotsset{
  auto node placement/.code={%
    \global\pgfplotsautonode@placementenabledtrue
    % The key is parsed before plot registration. Initialize immediately:
    % appending to `execute at begin axis` here can miss that hook when PGFPlots
    % has already executed it, leaking label IDs into the next axis.
    \pgfplotsautonode@beginaxis
    \pgfkeysalso{%
      after end axis/.append code={\pgfplotsautonode@flush}%
    }%
  },
  auto node debug/.is choice,
  auto node debug/false/.code={\pgfplotsautonode@debuglevel=0\relax},
  auto node debug/off/.code={\pgfplotsautonode@debuglevel=0\relax},
  auto node debug/summary/.code={\pgfplotsautonode@debuglevel=1\relax},
  auto node debug/verbose/.code={\pgfplotsautonode@debuglevel=2\relax},
  auto node debug/trace/.code={\pgfplotsautonode@debuglevel=3\relax},
  auto node debug/.default=summary,
  auto node max iterations/.code={\pgfplotsautonode@maxiterations=#1\relax},
  auto node algorithm/.is choice,
  auto node algorithm/greedy/.code={\def\pgfplotsautonode@algorithm{greedy}},
  auto node algorithm/repair/.code={\def\pgfplotsautonode@algorithm{repair}},
  auto node algorithm/local-search/.code={\def\pgfplotsautonode@algorithm{local-search}},
  auto node algorithm/exact-small/.code={\def\pgfplotsautonode@algorithm{exact-small}},
  auto node bbox mode/.is choice,
  auto node bbox mode/axis-aligned/.code={\def\pgfplotsautonode@bboxmode{axis-aligned}},
  auto node bbox mode/oriented/.code={\def\pgfplotsautonode@bboxmode{oriented}},
  auto node failure mode/.is choice,
  auto node failure mode/warn/.code={\def\pgfplotsautonode@failuremode{warn}},
  auto node failure mode/error/.code={\def\pgfplotsautonode@failuremode{error}},
  auto node failure mode/hide-low-priority/.code={\def\pgfplotsautonode@failuremode{hide-low-priority}},
  auto node failure mode/allow-minimal-overlap/.code={\def\pgfplotsautonode@failuremode{allow-minimal-overlap}},
  auto node candidates/.code={\def\pgfplotsautonode@defaultcandidates{#1}},
  auto node candidate strategy/.is choice,
  auto node candidate strategy/uniform/.code={\def\pgfplotsautonode@defaultcandidatestrategy{uniform}},
  auto node candidate strategy/around preferred/.code={\def\pgfplotsautonode@defaultcandidatestrategy{around-preferred}},
  auto node candidate strategy/around-preferred/.code={\def\pgfplotsautonode@defaultcandidatestrategy{around-preferred}},
  auto node candidate strategy/adaptive/.code={\def\pgfplotsautonode@defaultcandidatestrategy{adaptive}},
  auto node border margin/.code={\def\pgfplotsautonode@bordermargin{#1}},
  auto node overlap tolerance/.code={\def\pgfplotsautonode@overlaptolerance{#1}},
  auto node allow outside/.is if=pgfplotsautonode@allowoutside,
  auto node allow outside/.default=true,
  auto node show bounding boxes/.is if=pgfplotsautonode@showboundingboxes,
  auto node show bounding boxes/.default=true,
  auto node show candidates/.is if=pgfplotsautonode@showcandidates,
  auto node show candidates/.default=true,
  auto node show rejected candidates/.is if=pgfplotsautonode@showrejectedcandidates,
  auto node show rejected candidates/.default=true,
  auto node exact max labels/.code={\pgfplotsautonode@exactmaxlabels=#1\relax},
  auto node exact max states/.code={\pgfplotsautonode@exactmaxstates=#1\relax}
}

\def\pgfplotsautonode@luabool#1{\csname if#1\endcsname true\else false\fi}

\def\pgfplotsautonode@configurelua{%
  \pgf@xa=\pgfplotsautonode@bordermargin\relax
  \edef\pgfplotsautonode@bordermarginpt{\pgf@sys@tonumber\pgf@xa}%
  \pgf@xa=\pgfplotsautonode@overlaptolerance\relax
  \edef\pgfplotsautonode@overlaptolerancept{\pgf@sys@tonumber\pgf@xa}%
  \edef\pgfplotsautonode@allowoutsidebool{\ifpgfplotsautonode@allowoutside true\else false\fi}%
  \edef\pgfplotsautonode@showboundingboxesbool{\ifpgfplotsautonode@showboundingboxes true\else false\fi}%
  \edef\pgfplotsautonode@showcandidatesbool{\ifpgfplotsautonode@showcandidates true\else false\fi}%
  \edef\pgfplotsautonode@showrejectedcandidatesbool{\ifpgfplotsautonode@showrejectedcandidates true\else false\fi}%
  \directlua{pgfplots_autonode.configure({
    debug_level = tonumber("\the\pgfplotsautonode@debuglevel"),
    max_iterations = tonumber("\the\pgfplotsautonode@maxiterations"),
    algorithm = "\pgfplotsautonode@algorithm",
    bbox_mode = "\pgfplotsautonode@bboxmode",
    failure_mode = "\pgfplotsautonode@failuremode",
    border_margin = tonumber("\pgfplotsautonode@bordermarginpt"),
    overlap_tolerance = tonumber("\pgfplotsautonode@overlaptolerancept"),
    allow_outside = \pgfplotsautonode@allowoutsidebool,
    show_bounding_boxes = \pgfplotsautonode@showboundingboxesbool,
    show_candidates = \pgfplotsautonode@showcandidatesbool,
    show_rejected_candidates = \pgfplotsautonode@showrejectedcandidatesbool,
    exact_max_labels = tonumber("\the\pgfplotsautonode@exactmaxlabels"),
    exact_max_states = tonumber("\the\pgfplotsautonode@exactmaxstates")
  })}%
}

\def\pgfplotsautonode@beginaxis{%
  \global\pgfplotsautonode@missinghookwarnedfalse
  \global\pgfplotsautonode@id=0\relax
  \directlua{pgfplots_autonode.reset()}%
  \pgfplotsautonode@configurelua
}

% Pass the visible axis rectangle to Lua in the same physical canvas coordinate
% system as the sampled candidates.  rel axis cs:0,0 and rel axis cs:1,1 are the
% plot-area corners, before tick labels and axis descriptions are added.
\def\pgfplotsautonode@setaxisrect{%
  \pgf@process{\pgfplotspointrelaxisxy{0}{0}}%
  \pgf@xa=\pgf@x\relax
  \pgf@ya=\pgf@y\relax
  \pgf@process{\pgfpointtransformed{\pgfpoint{\pgf@xa}{\pgf@ya}}}%
  \edef\pgfplotsautonode@rectleft{\pgf@sys@tonumber\pgf@x}%
  \edef\pgfplotsautonode@rectbottom{\pgf@sys@tonumber\pgf@y}%
  \pgf@process{\pgfplotspointrelaxisxy{1}{1}}%
  \pgf@xa=\pgf@x\relax
  \pgf@ya=\pgf@y\relax
  \pgf@process{\pgfpointtransformed{\pgfpoint{\pgf@xa}{\pgf@ya}}}%
  \edef\pgfplotsautonode@rectright{\pgf@sys@tonumber\pgf@x}%
  \edef\pgfplotsautonode@recttop{\pgf@sys@tonumber\pgf@y}%
  \directlua{pgfplots_autonode.set_axis_rect(
    tonumber("\pgfplotsautonode@rectleft"),
    tonumber("\pgfplotsautonode@rectright"),
    tonumber("\pgfplotsautonode@rectbottom"),
    tonumber("\pgfplotsautonode@recttop")
  )}%
}

\def\pgfplotsautonode@flush{%
  % PGFPlots can execute `execute at begin axis` while it is still processing
  % later axis options.  Synchronize once more here so the solver observes the
  % final axis-local values independently of key order.
  \pgfplotsautonode@configurelua
  \pgfplotsautonode@setaxisrect
  \pgfplotsautonode@measureall
  \directlua{pgfplots_autonode.solve_and_emit()}%
  \global\pgfplotsautonode@placementenabledfalse
}

% Measure after plot visualization, outside the active plot path.  Interrupt
% the enclosing picture before opening the scratch picture: PGF then restores
% the real font selector and the nested picture cannot capture its own
% \pgf@selectfont as \pgf@selectfontorig.
\def\pgfplotsautonode@measurelabel#1{%
  \begingroup
    \edef\pgfplotsautonode@measureopts{\csname pgfplotsautonode@options@#1\endcsname}%
    \edef\pgfplotsautonode@measurefont{\csname pgfplotsautonode@font@#1\endcsname}%
    \edef\pgfplotsautonode@measureinnersep{\csname pgfplotsautonode@innersep@#1\endcsname}%
    \setbox0=\hbox{%
      \pgfinterruptpicture
      \tikzpicture
        \edef\pgfplotsautonode@measurepath{%
          \noexpand\path node[font={\pgfplotsautonode@measurefont},inner sep=\pgfplotsautonode@measureinnersep,\pgfplotsautonode@measureopts,rotate=0]
            at (0pt,0pt)
            {\expandafter\noexpand\csname pgfplotsautonode@content@#1\endcsname};%
        }%
        \pgfplotsautonode@measurepath
        \xdef\pgfplotsautonode@localxmin{\pgf@sys@tonumber\pgf@picminx}%
        \xdef\pgfplotsautonode@localxmax{\pgf@sys@tonumber\pgf@picmaxx}%
        \xdef\pgfplotsautonode@localymin{\pgf@sys@tonumber\pgf@picminy}%
        \xdef\pgfplotsautonode@localymax{\pgf@sys@tonumber\pgf@picmaxy}%
      \endtikzpicture
      \endpgfinterruptpicture
    }%
    \directlua{pgfplots_autonode.set_label_geometry(
      tonumber("#1"),
      tonumber("\pgfplotsautonode@localxmin"),
      tonumber("\pgfplotsautonode@localxmax"),
      tonumber("\pgfplotsautonode@localymin"),
      tonumber("\pgfplotsautonode@localymax")
    )}%
  \endgroup
}
\def\pgfplotsautonode@measureall{%
  \begingroup
    \pgfplotsautonode@measureindex=1\relax
    \loop\ifnum\pgfplotsautonode@measureindex<\numexpr\pgfplotsautonode@id+1\relax
      \expandafter\pgfplotsautonode@measurelabel\expandafter{\the\pgfplotsautonode@measureindex}%
      \advance\pgfplotsautonode@measureindex by1\relax
    \repeat
  \endgroup
}

% Drawing callback emitted by Lua.
% #1 label id, #2 x in pt, #3 y in pt, #4 rotation angle in degrees.
\def\pgfplotsautonodedraw#1#2#3#4{%
  \begingroup
    \edef\pgfplotsautonode@drawopts{\csname pgfplotsautonode@options@#1\endcsname}%
    \edef\pgfplotsautonode@drawfont{\csname pgfplotsautonode@font@#1\endcsname}%
    \edef\pgfplotsautonode@drawinnersep{\csname pgfplotsautonode@innersep@#1\endcsname}%
    \edef\pgfplotsautonode@next{%
      \endgroup
      \noexpand\pgfscope
        \noexpand\pgftransformreset
        \noexpand\path node[font={\pgfplotsautonode@drawfont},inner sep=\pgfplotsautonode@drawinnersep,\pgfplotsautonode@drawopts,rotate=#4]%
          at (#2pt,#3pt) {\expandafter\noexpand\csname pgfplotsautonode@content@#1\endcsname};%
      \noexpand\endpgfscope
    }%
  \pgfplotsautonode@next
}

% Debug visualisations.  These are intentionally simple and independent of the
% final node style so that they do not affect normal documents.
\def\pgfplotsautonodedrawbbox#1#2#3#4#5{%
  \pgfscope
    \pgftransformreset
    \path[draw=orange!80!black,densely dashed,line width=.25pt]
      (#2pt,#3pt) rectangle (#4pt,#5pt);%
  \endpgfscope
}

\def\pgfplotsautonodedrawcandidate#1#2#3{%
  \pgfscope
    \pgftransformreset
    \def\pgfplotsautonode@state{#1}%
    \def\pgfplotsautonode@valid{valid}%
    \ifx\pgfplotsautonode@state\pgfplotsautonode@valid
      \path[draw=green!50!black,fill=green!50!black] (#2pt,#3pt) circle[radius=.65pt];%
    \else
      \path[draw=red!70!black] (#2pt,#3pt) ++(-.8pt,-.8pt) -- ++(1.6pt,1.6pt)
        ++(-1.6pt,0pt) -- ++(1.6pt,-1.6pt);%
    \fi
  \endpgfscope
}

% Public macro used as a pgfplots trailing path command:
%   \addplot {x^2} \pgfplotsautonode[sloped,preferred pos=.5] {$x^2$};
\def\pgfplotsautonode[#1]#2{%
  \pgfextra{\pgfplotsautonode@register{#1}{#2}}%
}

\def\pgfplotsautonode@samplecandidate#1#2{%
  \pgf@process{\pgfplotspointplotattime{#1}}%
  \pgf@xa=\pgf@x\relax
  \pgf@ya=\pgf@y\relax
  \pgf@process{\pgfpointtransformed{\pgfpoint{\pgf@xa}{\pgf@ya}}}%
  \edef\pgfplotsautonode@canvasx{\pgf@sys@tonumber\pgf@x}%
  \edef\pgfplotsautonode@canvasy{\pgf@sys@tonumber\pgf@y}%
  \directlua{pgfplots_autonode.add_candidate(
    tonumber("\pgfplotsautonode@idstr"),
    tonumber("#1"),
    tonumber("\pgfplotsautonode@canvasx"),
    tonumber("\pgfplotsautonode@canvasy"),
    tonumber("#2")
  )}%
}

% Read the surveyed vertices of the current PGFPlots path.  PGFPlots uses
% these same serialized coordinates for \pgfplotspointplotattime.  Forward its
% own segment-length metric so Lua's normalized positions agree with that API.
\def\pgfplotsautonode@pathstart{%
  \let\pgfplotsautonode@previouspoint\relax
}
\def\pgfplotsautonode@pathcoord{%
  \ifx\pgfplots@current@point@x\pgfutil@empty
    \let\pgfplotsautonode@previouspoint\relax
    \directlua{pgfplots_autonode.add_path_break(tonumber("\pgfplotsautonode@idstr"))}%
  \else
    \pgfplotsplothandlerserializepointto{\pgfplotsautonode@currentpoint}%
    \ifx\pgfplotsautonode@previouspoint\relax
      \def\pgfplotsautonode@segmentlength{0}%
    \else
      \pgfplotsplothandlersurveydifflen{\pgfplotsautonode@previouspoint}{\pgfplotsautonode@currentpoint}%
      \pgfplotscoordmath{default}{tofixed}{\pgfmathresult}%
      \edef\pgfplotsautonode@segmentlength{\pgfmathresult}%
    \fi
    \let\pgfplotsautonode@previouspoint\pgfplotsautonode@currentpoint
    \pgf@process{\pgfplotsaxisvisphasegetpoint}%
    \pgf@xa=\pgf@x\relax
    \pgf@ya=\pgf@y\relax
    \pgf@process{\pgfpointtransformed{\pgfpoint{\pgf@xa}{\pgf@ya}}}%
    \edef\pgfplotsautonode@canvasx{\pgf@sys@tonumber\pgf@x}%
    \edef\pgfplotsautonode@canvasy{\pgf@sys@tonumber\pgf@y}%
    \directlua{pgfplots_autonode.add_path_point(
      tonumber("\pgfplotsautonode@idstr"),
      tonumber("\pgfplotsautonode@canvasx"),
      tonumber("\pgfplotsautonode@canvasy"),
      tonumber("\pgfplotsautonode@segmentlength")
    )}%
  \fi
}
\def\pgfplotsautonode@collectpath{%
  \begingroup
    \if1\b@pgfplots@LUA@visualization@enabled
      \ifx\pgfplots@plot@timer@args\relax
        \expandafter\gdef\expandafter\pgfplots@plot@timer@args\expandafter{%
          \pgfplotsutil@directlua{pgfplots.texGetSurveyedCoordsToPgfplots()}%
        }%
      \fi
      \let\pgfplotsaxisdeserializedatapointfrom@private=\pgfplotsaxisdeserializedatapointfrom@private@nonLUA
    \else
      \global\let\pgfplots@plot@timer@args=\pgfplots@stored@current@data
    \fi
    \let\pgfplots@coord@stream@start\pgfplotsautonode@pathstart
    \let\pgfplots@coord@stream@coord\pgfplotsautonode@pathcoord
    \let\pgfplots@coord@stream@end\relax
    \expandafter\pgfplots@coord@stream@foreach@NORMALIZED
      \expandafter{\pgfplots@plot@timer@args}%
  \endgroup
}

\long\def\pgfplotsautonode@register#1#2{%
  \ifpgfplotsautonode@placementenabled\else
    \ifpgfplotsautonode@missinghookwarned\else
      \global\pgfplotsautonode@missinghookwarnedtrue
      \immediate\write16{pgfplots-autonode warning: label registered without
        `auto node placement'; no automatic label can be rendered}%
    \fi
  \fi
  \begingroup
    \pgfkeys{/pgfplots/autonode/.cd,default,#1}%
    \global\advance\pgfplotsautonode@id by1\relax
    \edef\pgfplotsautonode@idstr{\the\pgfplotsautonode@id}%
    % Store label content and TikZ node options without expanding the content.
    \expandafter\gdef\csname pgfplotsautonode@content@\pgfplotsautonode@idstr\endcsname{#2}%
    \expandafter\global\expandafter\let\csname pgfplotsautonode@options@\pgfplotsautonode@idstr\endcsname\pgfplotsautonode@nodeopts
    \expandafter\global\expandafter\let\csname pgfplotsautonode@font@\pgfplotsautonode@idstr\endcsname\pgfplotsautonode@font
    \expandafter\global\expandafter\let\csname pgfplotsautonode@innersep@\pgfplotsautonode@idstr\endcsname\pgfplotsautonode@innersep
    \pgf@xa=\pgfplotsautonode@normalshift\relax
    \edef\pgfplotsautonode@normalshiftpt{\pgf@sys@tonumber\pgf@xa}%
    \pgf@xa=\pgfplotsautonode@clearance\relax
    \edef\pgfplotsautonode@clearancept{\pgf@sys@tonumber\pgf@xa}%
    \pgfmathparse{\pgfplotsautonode@preferredpos}%
    \let\pgfplotsautonode@preferrednumeric\pgfmathresult
    \pgfmathparse{\pgfplotsautonode@minpos}%
    \let\pgfplotsautonode@minnumeric\pgfmathresult
    \pgfmathparse{\pgfplotsautonode@maxpos}%
    \let\pgfplotsautonode@maxnumeric\pgfmathresult
    \pgfmathtruncatemacro\pgfplotsautonode@samplesnumeric{\pgfplotsautonode@samples}%
    \edef\pgfplotsautonode@slopedbool{\ifpgfplotsautonode@sloped true\else false\fi}%
    \edef\pgfplotsautonode@allowupsidedownbool{\ifpgfplotsautonode@allowupsidedown true\else false\fi}%
    \directlua{pgfplots_autonode.add_label(
      tonumber("\pgfplotsautonode@idstr"),
      0,
      0,
      tonumber("\pgfplotsautonode@preferrednumeric"),
      tonumber("\pgfplotsautonode@normalshiftpt"),
      tonumber("\pgfplotsautonode@clearancept"),
      \pgfplotsautonode@slopedbool,
      \pgfplotsautonode@allowupsidedownbool,
      tonumber("\pgfplotsautonode@preferredweight"),
      tonumber("\pgfplotsautonode@overlapweight"),
      tonumber("\pgfplotsautonode@priority")
    )}%
    \pgfplotsautonode@setaxisrect
    \pgfplotsautonode@collectpath
    % Lua clips the surveyed path in canvas coordinates and requests positions
    % only on its visible portions.  Final samples still use PGFPlots' own
    % point-at-time function to preserve its path interpolation.
    \directlua{pgfplots_autonode.emit_visible_candidate_positions(
      tonumber("\pgfplotsautonode@idstr"),
      tonumber("\pgfplotsautonode@preferrednumeric"),
      tonumber("\pgfplotsautonode@minnumeric"),
      tonumber("\pgfplotsautonode@maxnumeric"),
      tonumber("\pgfplotsautonode@samplesnumeric"),
      "\pgfplotsautonode@candidatestrategy"
    )}%
  \endgroup
}

\catcode`\@=\csname pgfplotsautonode@oldatcatcode\endcsname\relax
\endinput
