% Copyright 2013 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{pgfmodulenonlineartransformations.code.tex} % % This file defines commands for nonlinear coordinate systems. This is % needed, for instance, for bend arrow heads. % % % Non-linear coordinate systems transform coordinates, in addition to % the normal linear transformations, in a, well, non-linear way. An % example are polar coordinates. % % Like the linear transformations, non-linear transformations are % applied on a low level and they are integrated into the whole path % construction mechanism. So, while \pgfpointpolar also "does % something nonlinear", it just provides a way of computing a % coordinate. In contrast, installing a polar transformation will % transform everything being drawn using the \pgfpath... commands an % will even turn a straight line into a circle. % % Since computing non-linear transformations is expensive, they are % only applied "if really necessary". % Return a transformed point % % #1 = a point % % Description: % % Applies the current transformation and the current nonlinear % transformation to the given point and returns the result in \pgf@x/y. \def\pgfpointtransformednonlinear#1{% \pgf@process{% #1% \pgf@pos@transform@glob% \pgf@nlt@list% } }% % Approximate the nonlinear translation locally % % Description: % % Does a "local synchronization" of the nonlinear transformation with % the linear transformation regarding the translation part of the % current nonlinear transformation. The *linear* transformation will be % updated so that it translates things to the point to which the % nonlinear transformation transforms the origin. The nonlinear % transformation will be reset. % % The net effect is that you can now draw things near the origin as % before, but with just a linear transformation in force. Note that % the coordinate system will not be rotated, only a shift is done (use % the "real" local sync for a full sync). \def\pgfapproximatenonlineartranslation{% \ifx\pgf@nlt@list\pgfutil@empty% \else% \pgfpointtransformednonlinear{\pgfpointorigin}% \pgf@pt@x=\pgf@x% \pgf@pt@y=\pgf@y% \let\pgf@nlt@list\pgfutil@empty% \let\pgf@nlt@moveto\pgf@lt@moveto% \let\pgf@nlt@lineto\pgf@lt@lineto% \let\pgf@nlt@curveto\pgf@lt@curveto% \let\pgf@nlt@closepath\pgf@lt@closepath% \fi% }% % Local sync translation % % Description: % % Does a "complete local synchronization" of the nonlinear % transformation with the linear transformation. The *linear* % transformation will be updated so that it transforms things to the % point to which the nonlinear transformation transforms the % origin. The nonlinear transformation will be reset. % % The net effect is that you can now draw things near the origin as % before, but with just a linear transformation in force. \def\pgfapproximatenonlineartransformation{% \ifx\pgf@nlt@list\pgfutil@empty% \else% \pgfpointtransformednonlinear{\pgfpoint{1pt}{0pt}}% \pgf@xa\pgf@x \pgf@ya\pgf@y \pgfpointtransformednonlinear{\pgfpoint{0pt}{1pt}}% \pgf@xb\pgf@x \pgf@yb\pgf@y \pgfpointtransformednonlinear{\pgfpointorigin}% \advance\pgf@xa by-\pgf@x \advance\pgf@xb by-\pgf@x \advance\pgf@ya by-\pgf@y \advance\pgf@yb by-\pgf@y \pgfsettransformentries{\pgf@sys@tonumber\pgf@xa}{\pgf@sys@tonumber\pgf@ya}{\pgf@sys@tonumber\pgf@xb}{\pgf@sys@tonumber\pgf@yb}{\pgf@x}{\pgf@y}% \let\pgf@nlt@list\pgfutil@empty% \let\pgf@nlt@moveto\pgf@lt@moveto% \let\pgf@nlt@lineto\pgf@lt@lineto% \let\pgf@nlt@curveto\pgf@lt@curveto% \let\pgf@nlt@closepath\pgf@lt@closepath% \fi% }% % Adds a non-linear transformation to the current list of % transformations. % % #1 = code % % Description: % % When the code #1 is called, \pgf@x and \pgf@y will be set to some % values, let us call this the point p. The nonlinear transformation % will now map this point to a new point f(p). The coordinates of this % point should be returned in \pgf@x and \pgf@y. Furthermore, consider % the two "unit tangents" (going in $x$-direction and in % $y$-direction) at point p. The nonlinear transformation will map % these to new tangents, t_x and t_y. The (not necessarily normalized) % vectors t_x and t_y should be returned in pgf@xa and -ya and pgf@xb % and pgf@yb. The macro should not do any global assignments except to % \pgf@x and \pgf@y, but may do arbitrary local assignments. % % The effect of installing a new transformation is the following: As % long as there is at least one nonlinear transformation installed, % whenever the path construction commands add a curve or line to the % softpath, the line or curve will be "bend" by applying all of the % nonlinear transformation installed. In particular, straight lines % will be replaced by curves. % % Note that the normal linear transformation are always applied before % any nonlinear transformations are applied. \def\pgftransformnonlinear#1{% \expandafter\def\expandafter\pgf@nlt@list\expandafter{\pgf@nlt@list#1}% \let\pgf@nlt@moveto\pgf@nlt@moveto@nlt \let\pgf@nlt@lineto\pgf@nlt@lineto@nlt \let\pgf@nlt@curveto\pgf@nlt@curveto@nlt \let\pgf@nlt@closepath\pgf@nlt@closepath@nlt }% \def\pgf@nlt@moveto@nlt#1#2{% {% \pgfutil@tempdima#1% \pgfutil@tempdimb#2% \pgf@x\pgfutil@tempdima% \pgf@y\pgfutil@tempdimb% \xdef\pgf@nlt@last@moveto@orig{{\the\pgf@x}{\the\pgf@y}}% \pgf@nlt@list% \xdef\pgf@nlt@last@moveto@trans{{\the\pgf@x}{\the\pgf@y}}% \xdef\pgf@nlt@last@moveto@xaxis{{\the\pgf@xa}{\the\pgf@ya}}% \xdef\pgf@nlt@last@moveto@yaxis{{\the\pgf@xb}{\the\pgf@yb}}% \pgf@protocolsizes{\pgf@x}{\pgf@y}% \pgfsyssoftpath@moveto{\the\pgf@x}{\the\pgf@y}% \global\let\pgf@nlt@last@coord@orig\pgf@nlt@last@moveto@orig% \global\let\pgf@nlt@last@coord@trans\pgf@nlt@last@moveto@trans% \global\let\pgf@nlt@last@coord@xaxis\pgf@nlt@last@moveto@xaxis% \global\let\pgf@nlt@last@coord@yaxis\pgf@nlt@last@moveto@yaxis% }% }% \def\pgf@nlt@lineto@nlt#1#2{% {% \edef\pgf@temp{\pgf@xc\the#1\pgf@yc\the#2}% \pgf@temp% \expandafter\pgfqpoint\pgf@nlt@last@coord@orig% \expandafter\pgf@nlt@set@temps\pgf@nlt@last@coord@trans% % Test, whether the points are quite near: \pgf@xa\pgf@x\advance\pgf@xa by-\pgf@xc% \pgf@ya\pgf@y\advance\pgf@ya by-\pgf@yc% \pgfutil@tempswafalse% \ifdim\pgf@xa<0.1pt\relax\ifdim\pgf@xa>-0.1pt\relax\ifdim\pgf@ya<0.1pt\relax\ifdim\pgf@ya>-0.1pt\relax\pgfutil@tempswatrue\fi\fi\fi\fi% \ifpgfutil@tempswa% \pgf@x\pgf@xc\pgf@y\pgf@yc% \xdef\pgf@nlt@last@coord@orig{{\the\pgf@x}{\the\pgf@y}}% \pgf@process{\pgf@nlt@list}% \xdef\pgf@nlt@last@coord@trans{{\the\pgf@x}{\the\pgf@y}}% \xdef\pgf@nlt@last@coord@xaxis{{\the\pgf@xa}{\the\pgf@ya}}% \xdef\pgf@nlt@last@coord@yaxis{{\the\pgf@xb}{\the\pgf@yb}}% \pgf@protocolsizes{\pgf@x}{\pgf@y}% \pgfsyssoftpath@lineto{\the\pgf@x}{\the\pgf@y}% \else% % Compute support points \pgf@xb=.333333\pgf@x% \advance\pgf@xb by.666666\pgf@xc% \pgf@yb=.333333\pgf@y% \advance\pgf@yb by.666666\pgf@yc% \pgf@xa=.333333\pgf@xc% \advance\pgf@xa by.666666\pgf@x% \pgf@ya=.333333\pgf@yc% \advance\pgf@ya by.666666\pgf@y% \pgf@nlt@inner@curve% \fi% }% }% \def\pgf@nlt@set@temps#1#2{% \pgfutil@tempdima#1\pgfutil@tempdimb#2% }% \def\pgf@nlt@curveto@nlt#1#2#3#4#5#6{% {% \edef\pgf@temp{\pgf@xa\the#1\pgf@ya\the#2\pgf@xb\the#3\pgf@yb\the#4\pgf@xc\the#5\pgf@yc\the#6}% \pgf@temp% \pgf@nlt@inner@curve% }% }% \newdimen\pgftransformnonlinearflatness \pgftransformnonlinearflatness=5pt \def\pgfsettransformnonlinearflatness#1{\pgfmathsetlength\pgftransformnonlinearflatness{#1}}% % Compute a curve from \pgf@nlt@last@coord@orig to \pgf@xc/\pgf@yc via the % controls \pgf@xa/\pgf@ya and \pgf@xb/\pgf@yb. \def\pgf@nlt@inner@curve{% \expandafter\pgfqpoint\pgf@nlt@last@coord@orig% % Save delta of supports: \pgfutil@tempdima\pgf@xa\advance\pgfutil@tempdima by-\pgf@xb \pgfutil@tempdimb\pgf@ya\advance\pgfutil@tempdimb by-\pgf@yb \begingroup % Replace supports by relative supports \advance\pgf@xa by-\pgf@x% \advance\pgf@ya by-\pgf@y% \advance\pgf@xb by-\pgf@xc% \advance\pgf@yb by-\pgf@yc% % % Now, test whether the flatness is satisfied: % \pgfutil@tempswafalse \ifdim\pgfutil@tempdima>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgfutil@tempdima<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgfutil@tempdimb>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgfutil@tempdimb<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgf@xa>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgf@xa<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgf@ya>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgf@ya<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgf@xb>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgf@xb<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgf@yb>\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifdim\pgf@yb<-\pgftransformnonlinearflatness\pgfutil@tempswatrue\fi% \ifpgfutil@tempswa% \endgroup% Undo the adjustments... {% \edef\pgf@orig@xya{\pgf@xa\the\pgf@xa\pgf@ya\the\pgf@ya} \edef\pgf@orig@xyb{\pgf@xb\the\pgf@xb\pgf@yb\the\pgf@yb} \edef\pgf@orig@xyc{\pgf@xc\the\pgf@xc\pgf@yc\the\pgf@yc} \pgfpointcurveattime{.5}% {\expandafter\pgfqpoint\pgf@nlt@last@coord@orig}% {\pgf@x\pgf@xa\pgf@y\pgf@ya}% {\pgf@x\pgf@xb\pgf@y\pgf@yb}% {\pgf@x\pgf@xc\pgf@y\pgf@yc}% % First new curve: % Start is at last@coord@orig, % first support is at last@coord@orig*t + s*(original xa/ya) % second support is at xb/yb % target is at x/y {% % Target: \pgf@xc\pgf@x\pgf@yc\pgf@y % First support: \expandafter\pgfqpoint\pgf@nlt@last@coord@orig% \pgf@orig@xya% \pgf@xa\pgf@time@s\pgf@xa\advance\pgf@xa by\pgf@time@t\pgf@x% \pgf@ya\pgf@time@s\pgf@ya\advance\pgf@ya by\pgf@time@t\pgf@y% \pgf@nlt@inner@curve% }% % Second new curve: % Start is at (new) last@coord@orig % first support is at xa/ya % second support is at t*(original xb/yb) + s*(original xc/yc) % target is at original xc/yc {% \pgf@orig@xyb% \pgf@orig@xyc% \pgf@xb\pgf@time@t\pgf@xb\advance\pgf@xb by\pgf@time@s\pgf@xc% \pgf@yb\pgf@time@t\pgf@yb\advance\pgf@yb by\pgf@time@s\pgf@yc% \pgf@nlt@inner@curve% }% }% \else% \endgroup% Snap back... \pgf@nlt@do@inner@curve% \fi% }% \def\pgf@nlt@do@inner@curve{% \pgf@process{\pgf@x\pgf@xa\pgf@y\pgf@ya\pgf@nlt@list}% \pgf@xa\pgf@x\pgf@ya\pgf@y% \pgf@process{\pgf@x\pgf@xb\pgf@y\pgf@yb\pgf@nlt@list}% \pgf@xb\pgf@x\pgf@yb\pgf@y% \xdef\pgf@nlt@last@coord@orig{{\the\pgf@xc}{\the\pgf@yc}}% \pgf@process{\pgf@x\pgf@xc\pgf@y\pgf@yc\pgf@nlt@list}% \pgf@xc\pgf@x\pgf@yc\pgf@y% \xdef\pgf@nlt@last@coord@trans{{\the\pgf@xc}{\the\pgf@yc}}% \pgf@protocolsizes{\pgf@xa}{\pgf@ya}% \pgf@protocolsizes{\pgf@xb}{\pgf@yb}% \pgf@protocolsizes{\pgf@xc}{\pgf@yc}% \pgfsyssoftpath@curveto{\the\pgf@xa}{\the\pgf@ya}{\the\pgf@xb}{\the\pgf@yb}{\the\pgf@xc}{\the\pgf@yc}% }% \def\pgf@nlt@closepath@nlt{% {% \expandafter\pgfqpoint\pgf@nlt@last@moveto@orig% \expandafter\pgf@nlt@set@temps\pgf@nlt@last@coord@orig% \advance\pgfutil@tempdima by-\pgf@x% \advance\pgfutil@tempdimb by-\pgf@y% \pgfutil@tempswatrue% \ifdim\pgfutil@tempdima<0.01pt\relax\ifdim\pgfutil@tempdima>-0.01pt\relax\ifdim\pgfutil@tempdimb<0.01pt\relax\ifdim\pgfutil@tempdimb>-0.01pt\relax\pgfutil@tempswafalse\fi\fi\fi\fi% \ifpgfutil@tempswa% \pgf@nlt@lineto@nlt{\pgf@x}{\pgf@y}% \fi% \pgfsyssoftpath@closepath% }% }% \endinput