\ProvidesPackageRCS[v\pgfversion] $Header: /cvsroot/pgf/pgf/generic/pgf/basiclayer/pgfbasesnakes.code.tex,v 1.5 2005/10/10 08:38:29 tantau Exp $

% Copyright 2005 by Till Tantau <tantau@cs.tu-berlin.de>.
%
% This program can be redistributed and/or modified under the terms
% of the GNU Public License, version 2.


\newdimen\pgfsnakeremainingdistance
\newdimen\pgfsnakecompleteddistance


% Creates a new pgf snake
%
% #1 = snake name
% #1 = initial state
% #3= states of the snake
%
%
% This command declares a new snake for later use. The second
% parameter specifies the states of the snake, see also the
% description of pgfpathsnake.
%
% Inside the code of #3 the command \state may be used. This command
% will only be defined while #3 is executed.
%
% Example:
%
% \pgfdeclaresnake{zig zag}{one zig zag}
% {
%   \state{one zig zag}[width=10pt]
%   {
%     \pgfpathlineto{\pgfpoint{2.5pt}{2.5pt}}
%     \pgfpathlineto{\pgfpoint{7.5pt}{-2.5pt}}
%     \pgfpathlineto{\pgfpoint{10pt}{0pt}}
%   }
%   \state{final}
%   {
%     \pgfpathlineto{\pgfpoint{\pgfsnakeremainingdistance}{0pt}}
%   }
% }

\long\def\pgfdeclaresnake#1#2#3{%
  \def\pgf@snake@name{#1}%
  \@namedef{pgf@snake@@#1@initial}{#2}%
  \let\pgf@orig@state=\state%
  \let\state=\pgf@snake@state
  #3
  \let\state=\pgf@orig@state%
}


% Sets the additional transformation applied to every segment of a snake
%
% #1 = transformation
%
% Example:
%
% \pgfsetsnakesegmenttransformation{\pgftransformyshift{5pt}}

\def\pgfsetsnakesegmenttransformation#1{\def\pgf@snakeadditionaltransform{#1}}
\pgfsetsnakesegmenttransformation{}


% Declares a new state
%
% #1 = state name
% #2 = options
% #3 = path element
%
% Description:
%
% When a snake is drawn and the current state is #1, the following
% happens. First, the options are executed, which will possible change
% the state. If that does not happen, the path element is added to the
% path and the coordinate system is translated by the path element's
% width (which is specified using the width option).
%
% Example:
%
% \state{initial}[width=10pt]
% {
%   \pgfpathlineto{\pgfpoint{2.5pt}{2.5pt}}
%   \pgfpathlineto{\pgfpoint{7.5pt}{-2.5pt}}
%   \pgfpathlineto{\pgfpoint{10pt}{0pt}}
% }

\def\pgf@snake@state#1{\@ifnextchar[{\pgf@@snake@start#1}{\pgf@@snake@start#1[]}}%}
\def\pgf@@snake@start#1[#2]#3{%
  \@namedef{pgf@snake@@\pgf@snake@name @#1@options}{#2}%
  \@namedef{pgf@snake@@\pgf@snake@name @#1@code}{#3}%
}



% Use multiple snakes
%
% #1 = list of snake names/length pairs
% #2 = point to which the snake leads
%
% This operation uses the snakes in parameter #1 to get to the point
% #4. The parameter #1 should contain pairs consisting of a snake name
% and a length for which this snake should be used. When the length is
% computed, the dimensions \pgfsnakeremainingdistance and
% \pgfsnakecompleteddistance will have been set to the length of the
% total distance to #2 that has already been covered/that still needs
% to be covered.
%
% Example:
%
% \pgfpathsnakesto{{lineto}{1cm},{zig zag}{\pgfsnakeremainingdistance}}{\pgfpoint{2cm}{3cm}}

\def\pgfpathsnakesto#1#2{%
  \begingroup%
    % compute target vector
    \pgfpointtransformed{#2}%
    \advance\pgf@x by-\pgf@path@lastx%
    \advance\pgf@y by-\pgf@path@lasty%
    % Ok, now normalize the vector...
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%
    \pgf@process{\pgfpointnormalised{}}%
    % ok, now computer length (arghh...)
    % In theory that's easy: divide the larger of the values x or y by
    % the normalizes x or y. Well...
    %
    % First, make xa and ya positive:
    \ifdim\pgf@xa<0pt%
      \pgf@xa=-\pgf@xa%
    \fi%
    \ifdim\pgf@ya<0pt%
      \pgf@ya=-\pgf@ya%
    \fi%
    % Now do division:
    \ifdim\pgf@xa>\pgf@ya%
      \c@pgf@counta=\pgf@x%
      \ifnum\c@pgf@counta=0\relax%
      \else%
        \divide\c@pgf@counta by 255\relax%
        \pgf@xa=16\pgf@xa\relax%
        \divide\pgf@xa by\c@pgf@counta%
        \pgf@xa=16\pgf@xa\relax%
      \fi%
    \else%
      \c@pgf@counta=\pgf@y%
      \ifnum\c@pgf@counta=0\relax%
      \else%
        \divide\c@pgf@counta by 255\relax%
        \pgf@ya=16\pgf@ya\relax%
        \divide\pgf@ya by\c@pgf@counta%
        \pgf@xa=16\pgf@ya\relax%
      \fi%
    \fi%
    % Make positive:
    \ifdim\pgf@xa<0pt%
      \pgf@xa=-\pgf@xa%
    \fi%
    % Ok, now we draw things...
    \edef\pgf@list{#1}%
    \pgfsnakeremainingdistance=\pgf@xa%
    \pgfsnakecompleteddistance=0pt%
    \pgf@xb=\pgf@x%
    \pgf@yb=\pgf@y%
    \@for\pgf@temp:=\pgf@list\do{%
      \ifx\pgf@temp\@empty%
      \else%
        \expandafter\pgf@snake@invoke\pgf@temp%
      \fi%
    }%
  \endgroup%
}

\def\pgf@snake@invoke#1#2{%
  \setlength\pgf@xa{#2}%
  {%
    \edef\pgf@marshal{\noexpand\pgfpathsnakealongvector{#1}{\the\pgf@xa}{\noexpand\pgfpoint{\the\pgf@xb}{\the\pgf@yb}}}%
    \pgf@marshal%
  }%
  \advance\pgfsnakecompleteddistance by\pgf@xa%
  \advance\pgfsnakeremainingdistance by-\pgf@xa%
}

% Use a snake
%
% #1 = snake name
% #2 = point to which the snake leads
%
% This operation mainly computes \pgfpathsnakealongvector for a vector
% appropriately choosen. See \pgfpathsnakealongvector for details.
%
% Example:
%
% \pgfpathsnaketo{zig zag}{\pgfpoint{2cm}{3cm}}

\def\pgfpathsnaketo#1#2{\pgfpathsnakesto{{#1}{\pgfsnakeremainingdistance}}{#2}}



% Use a snake
%
% #1 = snake name
% #2 = length of the snake
% #3 = vector along which the snake grows, should have unit length.
%
% This command draws a snake (more precisely, it adds a snake to the
% path). This works as follows:
%
% First, the coordinate system is transformed such that the vector #3
% points to the right.
%
% Next, the state `initial' of the snake is entered. Unless the
% options of this state cause it to switch to another state, the path
% element is added to the path. Then, the coordinate system is
% translated by the width of the path element as specified in the
% width option of the path element. The dimensions
% \pgfsnakeremainingdistance and \pgfsnakecompleteddistance are
% updated. 
%
% The process ends when the state `final' is entered. The code of the
% final state is executed and the process stops.
%
% Example:
%
% \pgfpathsnakealongvector{zig zag}{100pt}{\pgfpolar{30}{1pt}}

\def\pgfpathsnakealongvector#1#2#3{%
  \@ifundefined{pgf@snake@@#1@initial}{\PackageError{pgf}{Undefined snake ``#1''}{}}
  { 
  \begingroup% keep things local
    \pgftransformreset%
    \pgf@pt@x=\pgf@path@lastx% evil trickery to transform to the last point
    \pgf@pt@y=\pgf@path@lasty%
    \pgf@process{#3}%
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%
    \pgf@xb=-\pgf@y%
    \pgf@yb=\pgf@x%
    \pgftransformcm
      {\pgf@sys@tonumber{\pgf@xa}}{\pgf@sys@tonumber{\pgf@ya}}
      {\pgf@sys@tonumber{\pgf@xb}}{\pgf@sys@tonumber{\pgf@yb}}
      {\pgfpointorigin}%
    % Now, setup the automaton
    \expandafter\let\expandafter\pgf@snake@current@state\expandafter=\csname pgf@snake@@#1@initial\endcsname%
    \def\pgf@snake@name{#1}%  
    \pgfsnakecompleteddistance=0pt%  
    \setlength\pgfsnakeremainingdistance{#2}%
    \pgf@snake@run%
    % Last step:
    {%
      \pgftransformxshift{\pgfsnakecompleteddistance}%
      \pgf@snakeadditionaltransform%
      \csname pgf@snake@@#1@final@code\endcsname%
    }%
  \endgroup%
  }%
}

\def\pgf@final@text{final}

\def\pgf@snake@run{%
  \let\pgf@snake@next=\pgf@snake@do@state%
  \ifx\pgf@snake@current@state\pgf@final@text%
    \let\pgf@snake@next=\relax%
  \fi%
  \pgf@snake@next%  
}

\def\pgf@snake@do@state{%
  \let\pgf@snake@next=\relax%
  \let\pgf@snake@next@state=\pgf@snake@current@state%
  % execute options
  \def\pgf@marshal{\setkeys{pgfsnake}}%
  \expandafter\expandafter\expandafter\pgf@marshal
  \expandafter\expandafter\expandafter{\csname pgf@snake@@\pgf@snake@name @\pgf@snake@current@state @options\endcsname}%
  \ifx\pgf@snake@next\relax%
    \pgf@snake@next=\pgf@snake@do@code%
  \fi%
  \pgf@snake@next%
}

\def\pgf@snake@do@code{%
  % Ok, execute code:
  {%
    \pgftransformxshift{\pgfsnakecompleteddistance}%
    \pgf@snakeadditionaltransform%
    \csname pgf@snake@@\pgf@snake@name @\pgf@snake@current@state @code\endcsname%
  }%
  % next, do transformation and update
  \setlength{\pgf@xa}{\pgf@snake@width}%
  \advance\pgfsnakeremainingdistance by-\pgf@xa%
  \advance\pgfsnakecompleteddistance by\pgf@xa%
  % Next iteration:
  \let\pgf@snake@current@state=\pgf@snake@next@state%
  \pgf@snake@run 
}

\define@key{pgfsnake}{width}{%
  \def\pgf@snake@width{#1}%
  \pgf@snake@switch@if#1 to final\pgf@stop%
}
\define@key{pgfsnake}{switch if less than}{%
  \pgf@snake@switch@if#1\pgf@stop%
}
\define@key{pgfsnake}{next state}{%
  \def\pgf@snake@next@state{#1}%
}

\def\pgf@snake@switch@if#1to #2\pgf@stop{%
  \ifx\pgf@snake@next\relax%
    \setlength\pgf@x{#1}%
    \ifdim\pgfsnakeremainingdistance<\pgf@x%
      \def\pgf@snake@current@state{#2}%
      \let\pgf@snake@next=\pgf@snake@run%
    \fi%
  \fi%
}



% lineto snake
%
% This snake simply adds a straight line. This snake is mainly useful
% in conjunction with other snakes.

\pgfdeclaresnake{lineto}{final}
{
  \state{final}
  { \pgfpathlineto{\pgfpoint{\pgfsnakeremainingdistance}{0pt}} }
}



% moveto snake
%
% This snake simply jumps to the end. This snake, too, is mainly
% useful in conjunction with other snakes.

\pgfdeclaresnake{moveto}{final}
{
  \state{final}
  { \pgfpathmoveto{\pgfpoint{\pgfsnakeremainingdistance}{0pt}} }
}



\endinput
