\ProvidesPackageRCS[v\pgfversion] $Header: /cvsroot/pgf/pgf/generic/pgf/basiclayer/pgfbasesnakes.code.tex,v 1.6 2005/10/25 08:18:14 tantau Exp $ % Copyright 2005 by Till Tantau . % % 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% \let\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