% Copyright 2019 by Till Tantau
%
% This file may be distributed and/or modified
%
% 1. under the LaTeX Project Public License and/or
% 2. under the GNU Public License.
%
% See the file doc/generic/pgf/licenses/LICENSE for more details.

\ProvidesFileRCS{pgfsyssoftpath.code.tex}


% This package provides the pgf system path abstraction layer. This
% layer is used to construct paths first ``abstractly.'' These
% abstract paths can still be modified, before they are ``made
% concrete.''
%
% An abstract path consists of a sequence of basic building blocks,
% each of which is a tuple consisting of two numbers (specifying a
% coordinate) and a token. Possible tokens are:
%
% 1. moveto
% 2. lineto
% 3. rectcorner
% 4. rectsize
% 5. curvetosupporta
% 6. curvetosupportb
% 7. curveto
% 8. closepath
% 9. specialround
%
% A curveto must always be preceded by a curvetosupporta and a
% curvetosupportb. A non-empty path must always begin with a
% moveto. The coordinates of a closepath are non-specified, currently,
% but they might be set to the coordinate of the path segment start,
% in the future.


% Access to the current path:
%
% Lots of stuff is added to the current path and it can get very long
% (containing literally tens of thousands of tokens). For such macros,
% adding things using a simple "g@addtomacro" takes more and more
% time, resulting in quadratic runtime. To avoid this, ideally, one
% would collect things in an array and then use a divide and conquer
% merger. A simple intermediate solution is the following, implemented
% here: We have the path and two buffers. New tokens are added to the
% first buffer, which is quite small. When it overflow, its contents
% is added to the second buffer, which is large. If that buffer
% overflow, the contents is finally added to the main path (which can
% have arbitrary length). Whenever the main path is set or read, the
% buffers are flushed.
%
% Because of this buffering, it is imperative that the main path is
% accessed only via appropriate interface macros.



% The current path
\let\pgfsyssoftpath@thepath=\pgfutil@empty
\let\pgfsyssoftpath@bigbuffer=\pgfutil@empty
\let\pgfsyssoftpath@smallbuffer=\pgfutil@empty
\newcount\pgfsyssoftpath@smallbuffer@items
\newcount\pgfsyssoftpath@bigbuffer@items

\def\pgfsyssoftpath@flushbuffers{%
  \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\gdef%
  \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\pgfsyssoftpath@thepath%
  \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{%
    \expandafter\expandafter\expandafter\pgfsyssoftpath@thepath%
    \expandafter\pgfsyssoftpath@bigbuffer\pgfsyssoftpath@smallbuffer}%
  \global\let\pgfsyssoftpath@smallbuffer=\pgfutil@empty
  \global\let\pgfsyssoftpath@bigbuffer=\pgfutil@empty
  \global\pgfsyssoftpath@bigbuffer@items0\relax%
  \global\pgfsyssoftpath@smallbuffer@items0\relax%
}

% Getting and setting the current path
\def\pgfsyssoftpath@getcurrentpath#1{%
  \pgfsyssoftpath@flushbuffers%
  \let#1=\pgfsyssoftpath@thepath%
}

\def\pgfsyssoftpath@setcurrentpath#1{%
  \global\let\pgfsyssoftpath@thepath=#1%
  \global\let\pgfsyssoftpath@smallbuffer=\pgfutil@empty
  \global\let\pgfsyssoftpath@bigbuffer=\pgfutil@empty
  \global\pgfsyssoftpath@bigbuffer@items0\relax%
  \global\pgfsyssoftpath@smallbuffer@items0\relax%
}


% Invoking the current path (slightly optimized)
\def\pgfsyssoftpath@invokecurrentpath{%
  \pgfsyssoftpath@thepath%
  \pgfsyssoftpath@bigbuffer%
  \pgfsyssoftpath@smallbuffer%
}

\def\pgfsyssoftpath@flushcurrentpath{%
  \pgfsyssoftpath@invokecurrentpath%
  \pgfsyssoftpath@setcurrentpath\pgfutil@empty%
}

% Add an item at the end
\def\pgfsyssoftpath@addtocurrentpath#1{%
  \global\advance\pgfsyssoftpath@smallbuffer@items by1\relax%
  \ifnum\pgfsyssoftpath@smallbuffer@items<40\relax% good number?
    \expandafter\gdef\expandafter\pgfsyssoftpath@smallbuffer\expandafter{\pgfsyssoftpath@smallbuffer#1}%
  \else%
    \pgfsyssoftpath@smalloverflow{#1}%
  \fi%
}
\def\pgfsyssoftpath@smalloverflow#1{%
  \global\advance\pgfsyssoftpath@bigbuffer@items by1\relax%
  \ifnum\pgfsyssoftpath@bigbuffer@items<30\relax% good number?
    \expandafter\expandafter\expandafter\gdef%
    \expandafter\expandafter\expandafter\pgfsyssoftpath@bigbuffer%
    \expandafter\expandafter\expandafter{\expandafter\pgfsyssoftpath@bigbuffer\pgfsyssoftpath@smallbuffer#1}%
    \global\let\pgfsyssoftpath@smallbuffer=\pgfutil@empty%
    \global\pgfsyssoftpath@smallbuffer@items0\relax%
  \else%
    \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\gdef%
    \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\pgfsyssoftpath@thepath%
    \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{%
      \expandafter\expandafter\expandafter\pgfsyssoftpath@thepath
      \expandafter\pgfsyssoftpath@bigbuffer\pgfsyssoftpath@smallbuffer#1}%
    \global\let\pgfsyssoftpath@smallbuffer=\pgfutil@empty
    \global\let\pgfsyssoftpath@bigbuffer=\pgfutil@empty
    \global\pgfsyssoftpath@bigbuffer@items0\relax%
    \global\pgfsyssoftpath@smallbuffer@items0\relax%
  \fi%
}



\def\pgfsyssoftpath@lastmoveto{{0pt}{0pt}}

\newif\ifpgfsyssoftpathmovetorelevant
\pgfsyssoftpathmovetorelevanttrue

% Add a moveto element to the current path
\def\pgfsyssoftpath@moveto#1#2{%
  \edef\pgfsyssoftpath@coord{{#1}{#2}}%
  \expandafter\pgfsyssoftpath@addtocurrentpath\expandafter{\expandafter\pgfsyssoftpath@movetotoken\pgfsyssoftpath@coord}%
  \ifpgfsyssoftpathmovetorelevant%
    \global\let\pgfsyssoftpath@lastmoveto\pgfsyssoftpath@coord%
  \fi%
}

% Add a lineto element to the current path
\def\pgfsyssoftpath@lineto#1#2{%
  \edef\pgfsyssoftpath@coord{{#1}{#2}}%
  \expandafter\pgfsyssoftpath@addtocurrentpath\expandafter{\expandafter\pgfsyssoftpath@linetotoken\pgfsyssoftpath@coord}%
}


% Add curveto elements to the current path
\def\pgfsyssoftpath@curveto#1#2#3#4#5#6{%
  \edef\pgfsyssoftpath@temp{{%
      \noexpand\pgfsyssoftpath@curvetosupportatoken{#1}{#2}%
      \noexpand\pgfsyssoftpath@curvetosupportbtoken{#3}{#4}%
      \noexpand\pgfsyssoftpath@curvetotoken{#5}{#6}%
    }}%
  \expandafter\pgfsyssoftpath@addtocurrentpath\pgfsyssoftpath@temp%
}


% Add rectangle elements to the current path
\def\pgfsyssoftpath@rect#1#2#3#4{%
  \edef\pgfsyssoftpath@temp{{%
    \noexpand\pgfsyssoftpath@rectcornertoken{#1}{#2}%
    \noexpand\pgfsyssoftpath@rectsizetoken{#3}{#4}%
  }}%
  \expandafter\pgfsyssoftpath@addtocurrentpath\pgfsyssoftpath@temp%
}

% Add closepath element to the current path
\def\pgfsyssoftpath@closepath{%
  \expandafter\pgfsyssoftpath@addtocurrentpath\expandafter{\expandafter\pgfsyssoftpath@closepathtoken\pgfsyssoftpath@lastmoveto}%
}

% Add special element to the current path
\def\pgfsyssoftpath@specialround#1#2{%
  \edef\pgfsyssoftpath@temp{{#1}{#2}}%
  \expandafter\pgfsyssoftpath@addtocurrentpath\expandafter{\expandafter\pgfsyssoftpath@specialroundtoken\pgfsyssoftpath@temp}%
}

% Marshallers
\def\pgfsyssoftpath@movetotoken#1#2{\pgfsys@moveto{#1}{#2}}
\def\pgfsyssoftpath@linetotoken#1#2{\pgfsys@lineto{#1}{#2}}
\def\pgfsyssoftpath@rectcornertoken#1#2#3#4#5{\pgfsys@rect{#1}{#2}{#4}{#5}} % #3 = \pgfsyssoftpath@rectsizetoken
\def\pgfsyssoftpath@curvetosupportatoken#1#2#3#4#5#6#7#8{\pgfsys@curveto{#1}{#2}{#4}{#5}{#7}{#8}}
\def\pgfsyssoftpath@closepathtoken#1#2{\pgfsys@closepath}
\let\pgfsyssoftpath@specialroundtoken=\pgfutil@gobbletwo
\def\pgfsyssoftpath@curvetosupportbtoken#1#2{curvetotokenb} % make sure this \ifx-equal only to itself
\def\pgfsyssoftpath@curvetotoken#1#2{curvetotoken}% make sure this \ifx-equal only to itself


\endinput

%%% Local Variables:
%%% mode: latex
%%% End:
