You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
tex-compiler/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarycalc.code.tex

618 lines
16 KiB

% Copyright 2006 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{tikzlibrarycalc.code.tex}
%
%
% Part I: The let path command
%
%
%
% Syntax: let \p{name1} = (coord), \p{name2} = (coord), ... in ...
%
% Currently (this may get more fancy in the future), the (coord)s are
% evaluated one by one. If the first evaluates to, say, (10pt,20pt),
% the macro \p{name1} is set to "10pt,20pt" (without parentheses), the
% macro \x{name1} is set to "10pt" and the macro \y{name1} is set to
% "20pt".
%
% If you use a number for {name}, you need no parentheses, so you
% could write:
%
% \draw let
% \p1 = (1,1),
% \p2 = ($ 2.5*(3,2) $)
% in
% (\x1,\x2) -- (\y1,\y2);
\def\tikz@let@command et{%
\let\p=\tikz@cc@dop%
\let\x=\tikz@cc@dox%
\let\y=\tikz@cc@doy%
\let\n=\tikz@cc@don%
\pgfutil@ifnextchar i{\tikz@cc@stop@let}{\tikz@cc@handle@line}%
}%
\def\tikz@cc@handle@line{%
\pgfutil@ifnextchar\p{%
\tikz@cc@handle@coor%
}{%
\pgfutil@ifnextchar\n{%
\tikz@cc@handle@num%
}{%
\pgfutil@ifnextchar i{%
\tikz@cc@stop@let
}{%
\tikzerror{``\string\p'' or ``\string\n'' expected}%
}%
}%
}%
}%
\def\tikz@cc@handle@num\n#1#2=#3{%
\pgfmathparse{#3}%
\expandafter\edef\csname tikz@cc@n@#1\endcsname{\pgfmathresult\ifpgfmathunitsdeclared pt\fi}
\pgfutil@ifnextchar,{\tikz@cc@handle@nextline}{\tikz@cc@stop@let}%
}%
\def\tikz@cc@handle@coor\p#1#2={%
\def\tikz@cc@coord@name{#1}%
\tikz@scan@one@point\tikz@cc@dolet%
}%
\def\tikz@cc@dolet#1{%
\pgf@process{#1}%
\expandafter\edef\csname tikz@cc@p@\tikz@cc@coord@name\endcsname{\the\pgf@x,\the\pgf@y}%
\expandafter\edef\csname tikz@cc@x@\tikz@cc@coord@name\endcsname{\the\pgf@x}%
\expandafter\edef\csname tikz@cc@y@\tikz@cc@coord@name\endcsname{\the\pgf@y}%
\pgfutil@ifnextchar,{\tikz@cc@handle@nextline}{\tikz@cc@stop@let}%
}%
\def\tikz@cc@handle@nextline,{%
\tikz@cc@handle@line%
}%
\def\tikz@cc@stop@let in{%
\tikz@scan@next@command%
}%
\def\tikz@cc@dop#1{\csname tikz@cc@p@#1\endcsname}%
\def\tikz@cc@dox#1{\csname tikz@cc@x@#1\endcsname}%
\def\tikz@cc@doy#1{\csname tikz@cc@y@#1\endcsname}%
\def\tikz@cc@don#1{\csname tikz@cc@n@#1\endcsname}%
%
%
% Part II: The ($...$) parser
%
%
\def\tikz@parse@calculator#1(${%$
\def\tikz@cc@command{#1}%
\begingroup%
%
% Parse main computation. It's a series of optional factors in front
% of coordinates.
%
\pgf@xa=0pt% We accumulate the result in here.
\pgf@ya=0pt%
\tikz@cc@parse+%
}%
\def\tikz@cc@parse{%
\pgfutil@ifnextchar${%$
% Ok, we found the end...
\tikz@cc@end%
}
{\pgfutil@ifnextchar+{%
% Ok, we found a coordinate...
\tikz@cc@add%
}{%
\pgfutil@ifnextchar-{%
\tikz@cc@sub%
}{%
\tikzerror{+ or - expected}%
\tikz@cc@end$%$
}%
}%
}%
}%
%
% The end is reached with $
%
\def\tikz@cc@end$#1){%$
\xdef\tikz@marshal{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
\endgroup%
\expandafter\tikz@cc@command\expandafter{\tikz@marshal}%
}%
%
% Another coordinate with +/-, possibly with a factor
%
\def\tikz@cc@add+{%
\def\tikz@cc@factor{1}%
\tikz@cc@factororcoordinate%
}%
\def\tikz@cc@sub-{%
\def\tikz@cc@factor{-1}%
\tikz@cc@factororcoordinate%
}%
%
% Check for a factor: If we see a (, its a coordinate...
%
\def\tikz@cc@factororcoordinate{%
\pgfutil@ifnextchar({%)
% Ok, found coordinate
\tikz@cc@coordinate%
}{%
\tikz@cc@parse@factor%
}%
}%
%
% ... otherwise it's a factor. It ends at ...*(
%
\def\tikz@cc@parse@factor#1*({%
\pgfmathparse{#1*\tikz@cc@factor}%
\let\tikz@cc@factor=\pgfmathresult%
\tikz@cc@coordinate(%)
}%
\def\tikz@cc@coordinate{%
\tikz@scan@absolute\tikz@cc@after@coordinate%
}%
\def\tikz@cc@after@coordinate#1{%
\pgf@process{#1}%
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
\tikz@cc@mid@checks%
}%
%
% A coordinate can be followed by !...!(...)
%
\def\tikz@cc@mid@checks{%
\ifnum\the\catcode`\!=\active\relax
\expandafter\tikz@cc@mid@checks@active
\else
\expandafter\tikz@cc@mid@checks@nonactive
\fi
}%
\def\tikz@cc@mid@checks@nonactive{%
\pgfutil@ifnextchar!{%
\tikz@cc@mid@nonactive%
}{%
\advance\pgf@xa by\tikz@cc@factor\pgf@xb
\advance\pgf@ya by\tikz@cc@factor\pgf@yb
\tikz@cc@parse%
}%
}%
\def\tikz@cc@mid@nonactive!{%
\pgfutil@ifnextchar({%
\tikz@scan@one@point\tikz@cc@project%
}{%
\tikz@cc@mid@num@nonactive%
}%
}%
\begingroup
\catcode`\!=\active
\gdef\tikz@cc@mid@checks@active{%
\pgfutil@ifnextchar!{%
\tikz@cc@mid@active%
}{%
\advance\pgf@xa by\tikz@cc@factor\pgf@xb
\advance\pgf@ya by\tikz@cc@factor\pgf@yb
\tikz@cc@parse%
}%
}%
\gdef\tikz@cc@mid@active!{%
\pgfutil@ifnextchar({%
\tikz@scan@one@point\tikz@cc@project%
}{%
\tikz@cc@mid@num@active%
}%
}%
\endgroup
%
% Partway case: (coord a)!number!(coord b)
%
% Returns the position that is at <number> fraction on the way from a
% to b. This, (a)!0!(b) is (a), (a)!.5!(b) is the middle and (a)!1!(b)
% is (b)
%
\def\tikz@cc@mid@num@nonactive#1!{\tikz@cc@mid@num{#1}}%
\begingroup
\catcode`\!=\active
\gdef\tikz@cc@mid@num@active#1!{\tikz@cc@mid@num{#1}}%
\endgroup
\def\tikz@cc@mid@num#1{%
\pgfmathparse{#1}%
\ifpgfmathunitsdeclared%
\let\tikz@cc@mid@unit=\pgfmathresult%
\expandafter\tikz@cc@scan@rot\expandafter\tikz@cc@after@unit%
\else%
\let\tikz@cc@mid@factor=\pgfmathresult%
\pgfmathparse{1-\tikz@cc@mid@factor}%
\let\tikz@cc@mid@factor@one=\pgfmathresult%
\expandafter\tikz@cc@scan@rot\expandafter\tikz@cc@after@num%
\fi%
}%
\def\tikz@cc@after@num#1{%
\pgf@process{#1}%
\pgf@xb=\tikz@cc@mid@factor@one\pgf@xb%
\pgf@yb=\tikz@cc@mid@factor@one\pgf@yb%
\advance\pgf@xb by\tikz@cc@mid@factor\pgf@x%
\advance\pgf@yb by\tikz@cc@mid@factor\pgf@y%
\tikz@cc@mid@checks%
}%
%
% Distance case: (coord a)!dimension!(coord b)
%
% Returns the position that is at <dimension> removed from (coord a)
% in the direction of (coord b).
%
\def\tikz@cc@after@unit#1{%
\pgf@process{#1}%
\advance\pgf@x by-\pgf@xb%
\advance\pgf@y by-\pgf@yb%
\pgf@process{\pgfpointnormalised{}}%
\advance\pgf@xb by\tikz@cc@mid@unit\pgf@x%
\advance\pgf@yb by\tikz@cc@mid@unit\pgf@y%
\tikz@cc@mid@checks%
}%
%
% Projection case: (a)!(p)!(b)
%
% Projection of p on line from a to b
%
\def\tikz@cc@project#1{%
\pgf@process{#1}%
% Save in c
\pgf@xc=\pgf@x%
\pgf@yc=\pgf@y%
\begingroup
\ifnum\the\catcode`\!=\active
\def\tikz@next{%
\endgroup
\expandafter\tikz@cc@scan@rot\expandafter\tikz@cc@after@project
\tikz@cc@scan@ex@active}%
\else
\def\tikz@next{%
\endgroup
\expandafter\tikz@cc@scan@rot\expandafter\tikz@cc@after@project
\tikz@cc@scan@ex@nonactive}%
\fi
\tikz@next%
}%
\def\tikz@cc@scan@ex@nonactive!{}%
\begingroup
\catcode`\!=\active
\gdef\tikz@cc@scan@ex@active!{}%
\endgroup
\def\tikz@cc@after@project#1{%
\pgf@process{#1}%
% Ok, now we need to project (xc,yc) on the line (xb,xc) to (x,y)
\advance\pgf@x by-\pgf@xb%
\advance\pgf@y by-\pgf@yb%
\advance\pgf@xc by-\pgf@xb%
\advance\pgf@yc by-\pgf@yb%
\pgf@process{\pgfpointnormalised{}}%
% Scalar product
\pgf@xc=\pgf@sys@tonumber{\pgf@xc}\pgf@x%
\advance\pgf@xc by\pgf@sys@tonumber{\pgf@yc}\pgf@y%
% and add
\advance\pgf@xb by\pgf@sys@tonumber{\pgf@xc}\pgf@x%
\advance\pgf@yb by\pgf@sys@tonumber{\pgf@xc}\pgf@y%
\tikz@cc@mid@checks%
}%
%
% Rotational scanner: radius:(x)
%
\def\tikz@cc@scan@rot#1{%
\pgfutil@ifnextchar({%)
\tikz@scan@one@point#1% normal
}%
{%
\def\tikz@cc@scan@rot@cmd{#1}%
\ifnum\the\catcode`\:=\active\relax
\expandafter\tikz@cc@scan@one@rot@active%
\else
\expandafter\tikz@cc@scan@one@rot@nonactive%
\fi
}%
}%
\def\tikz@cc@scan@one@rot@nonactive#1:{%
\def\tikz@cc@scan@rot@angle{#1}%
\tikz@scan@one@point\tikz@cc@handle@rot%
}%
\begingroup
\catcode`\:=\active
\gdef\tikz@cc@scan@one@rot@active#1:{%
\def\tikz@cc@scan@rot@angle{#1}%
\tikz@scan@one@point\tikz@cc@handle@rot%
}%
\endgroup
\def\tikz@cc@handle@rot#1{%
\pgf@process{#1}%
% Ok, now we need to rotate x/y around xb/xb by ...rot@angle
{%
\pgftransformreset%
% Save them...
\pgf@xc=\pgf@x%
\pgf@yc=\pgf@y%
\pgftransformshift{\pgfqpoint{\pgf@xb}{\pgf@yb}}%
\pgftransformrotate{\tikz@cc@scan@rot@angle}%
\pgftransformshift{\pgfqpoint{-\pgf@xb}{-\pgf@yb}}%
\pgfpointtransformed{\pgfqpoint{\pgf@xc}{\pgf@yc}}%
\expandafter
}%
\edef\tikz@marshal{\noexpand\tikz@cc@scan@rot@cmd{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}}%
\tikz@marshal%
}%
%
%
% Part III: Calculation coordinate systems
%
%
% Tangent cs: Keys are a node and a point. Depending on the type of
% node, the appropriate tangent computation should be done.
\tikzdeclarecoordinatesystem{tangent}
{%
\tikzset{cs/.cd,#1}%
\expandafter\ifx\csname tikz@tangent@\tikz@cs@type\endcsname\relax%
\tikzerror{I do not know how to compute the tangent to
a \tikz@cs@type}%
\pgfpointorigin%
\else%
\expandafter\tikz@scan@one@point\expandafter\tikz@lib@do@tangent\tikz@cs@point%
\fi%
}%
\tikzset{cs/node/.code=\tikz@cs@unpack{\tikz@cs@node}{\tikz@cs@type}{#1}}%
\tikzset{cs/point/.store in=\tikz@cs@point}%
\def\tikz@lib@do@tangent{\csname tikz@tangent@\tikz@cs@type\endcsname}%
\def\tikz@tangent@coordinate#1{%
\pgfpointanchor{\tikz@cs@node}{center}%
}%
\def\tikz@tangent@circle#1{%
{%
% Step 1: Compute the transformed position of the input:
\pgf@process{\pgfpointtransformed{#1}}%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
%
% Step 2: Compute vector from center of circle to transformed #1
%
\pgf@process{\pgfpointtransformed{\pgfpointanchor{\tikz@cs@node}{center}}}%
\advance\pgf@xa by-\pgf@x%
\advance\pgf@ya by-\pgf@y%
%
% Step 2: Reset transformations, they distract...
%
\pgftransformreset%
%
% Step 3: Transform to the center of the circle.
%
\pgftransformshift{\pgfpointanchor{\tikz@cs@node}{center}}%
%
% Step 4: Compute the radius
%
\pgf@process{\pgfpointanchor{\tikz@cs@node}{east}}%
\pgf@xc=\pgf@x%
%
% Now, (xa,ya) is a point. Compute the tangent from this point to
% a circle around the origin of radius xc.
%
% acos(radius/veclen(xa,ya)) is the angle of the tangent.
\pgfmathparse{veclen(\the\pgf@xa,\the\pgf@ya)}
\pgfmathparse{acos(\the\pgf@xc/\pgfmathresult)}
\ifnum\pgfkeysvalueof{/tikz/cs/solution}>1\relax%
\pgfmathparse{0-\pgfmathresult}%
\fi%
\let\tikz@lib@temp=\pgfmathresult%
%
% Now \pgfmathparse contains the desired angle. Use this to
% compute the correct position on the circle...
%
% But, first, rotate to the point.
\pgf@process{\pgfpointnormalised{\pgfqpoint{\pgf@xa}{\pgf@ya}}}%
\pgf@ya=-\pgf@y%
\pgftransformcm{\pgf@sys@tonumber{\pgf@x}}{\pgf@sys@tonumber{\pgf@y}}{\pgf@sys@tonumber{\pgf@ya}}{\pgf@sys@tonumber{\pgf@x}}{\pgfpointorigin}%
% Finally, rotate...
\pgf@process{\pgfpointtransformed{\pgfpointpolar{\tikz@lib@temp}{\the\pgf@xc}}}%
%
% Ok, undo transformations...
}%
% \pgf@x, \pgf@y have been smuggled outside by \pgf@process
{%
\pgftransforminvert%
\pgf@process{\pgfpointtransformed{}}%
}%
}%
% Implementation of intersections
\def\tikz@intersect@circle@and@circle{%
{%
\pgftransformreset% transformations only confuse us, here...
%
% Compute origin and radius of first circle
%
\pgf@process{\pgfpointanchor{\tikz@cs@node@a}{center}}%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\pgf@process{\pgfpointanchor{\tikz@cs@node@a}{east}}%
\advance\pgf@x by-\pgf@xa%
\pgf@xc=\pgf@x% ok, pgf@xc is first radius, (xa,ya) is center
%
% Compute origin and radius of second circle
%
\pgf@process{\pgfpointanchor{\tikz@cs@node@b}{center}}%
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
\pgf@process{\pgfpointanchor{\tikz@cs@node@b}{east}}%
\advance\pgf@x by-\pgf@xb%
\pgf@yc=\pgf@x% \pgf@yc is second radius, (xb,yb) is center
%
\pgf@process{%
\pgfpointintersectionofcircles{\pgfqpoint{\pgf@xa}{\pgf@ya}}{\pgfqpoint{\pgf@xb}{\pgf@yb}}{\pgf@xc}{\pgf@yc}{\pgfkeysvalueof{/tikz/cs/solution}}%
}%
}%
% \pgf@x, \pgf@y have been smuggled outside by \pgf@process,
% reinstall transformations...
{%
\pgftransforminvert%
\pgf@process{\pgfpointtransformed{}}%
}%
}%
\def\tikz@intersect@line@and@circle{%
{%
%
% Step 1: Get line
%
\expandafter\tikz@scan@one@point\expandafter\tikz@parse@line\tikz@cs@line@a%
\pgf@process{\pgfpointtransformed{}}%
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
\pgf@process{\pgfpointtransformed{\pgfqpoint{\pgf@xc}{\pgf@yc}}}%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
%
% Step 2: Subtract center of circle
%
\pgf@process{\pgfpointtransformed{\pgfpointanchor{\tikz@cs@node@b}{center}}}%
\advance\pgf@xa by-\pgf@x%
\advance\pgf@ya by-\pgf@y%
\advance\pgf@xb by-\pgf@x%
\advance\pgf@yb by-\pgf@y%
%
% Step 3: Reset transformations, they distract...
%
\pgftransformreset%
%
% Step 4: Transform to the center of the circle.
%
\pgftransformshift{\pgfpointanchor{\tikz@cs@node@b}{center}}%
%
% Step 5: Compute the radius
%
\pgf@process{\pgfpointanchor{\tikz@cs@node@b}{east}}%
\edef\tikz@lib@saved@radius{\pgf@sys@tonumber{\pgf@x}}%
%
% Step 6: Compute projection of origin on line (xa,ya) -- (xb,yb),
% store in (xa,ya)
\pgf@x=\pgf@xb%
\pgf@y=\pgf@yb%
\advance\pgf@x by-\pgf@xa%
\advance\pgf@y by-\pgf@ya%
\pgf@process{\pgfpointnormalised{}}%
% Scalar product
\pgf@xc=\pgf@sys@tonumber{\pgf@xa}\pgf@x%
\advance\pgf@xc by\pgf@sys@tonumber{\pgf@ya}\pgf@y%
\pgf@xc=-\pgf@xc%
% and add
\advance\pgf@xa by\pgf@sys@tonumber{\pgf@xc}\pgf@x%
\advance\pgf@ya by\pgf@sys@tonumber{\pgf@xc}\pgf@y%
%
% Now, we have a triangle with a right angle at (xa,ya). The
% second point of the triangle is the origin. The third point is
% sought.
% Save x/y
\pgf@xc=\pgf@x%
\pgf@yc=\pgf@y%
% Square radius
\pgf@xb=\tikz@lib@saved@radius pt%
%
% First, make numbers smaller, in case they are too large
%
\c@pgf@counta=1\relax%
\loop%
\ifdim\pgf@xb>50pt%
\multiply\c@pgf@counta by2\relax%
\divide\pgf@xa by2\relax%
\divide\pgf@ya by2\relax%
\divide\pgf@xb by2\relax%
\repeat%
\pgf@xb=\pgf@sys@tonumber{\pgf@xb}\pgf@xb%
% Subtract xa^2 + ya^2
\pgf@yb=\pgf@sys@tonumber{\pgf@xa}\pgf@xa%
\advance\pgf@xb by-\pgf@yb%
\pgf@yb=\pgf@sys@tonumber{\pgf@ya}\pgf@ya%
\advance\pgf@xb by-\pgf@yb%
% Square root
\ifdim\pgf@xb<0pt%
\pgf@xb=0pt%
\fi%
\pgfmathsqrt@{\pgf@sys@tonumber{\pgf@xb}}%
\pgfmathmultiply@{\pgfmathresult}{\the\c@pgf@counta}%
\multiply\pgf@xa by\c@pgf@counta\relax%
\multiply\pgf@ya by\c@pgf@counta\relax%
\ifnum\pgfkeysvalueof{/tikz/cs/solution}>1\relax%
\pgfmathsubtract{0}{\pgfmathresult}%
\fi%
% Ok, now add things...
\advance\pgf@xa by \pgfmathresult\pgf@xc%
\advance\pgf@ya by \pgfmathresult\pgf@yc%
\pgf@process{\pgfpointtransformed{\pgfqpoint{\pgf@xa}{\pgf@ya}}}%
% Ok, undo transformations...
}%
% \pgf@x, \pgf@y have been smuggled outside by \pgf@process
{%
\pgftransforminvert%
\pgf@process{\pgfpointtransformed{}}%
}%
}%
\def\tikz@intersect@circle@and@line{%
% Swap
{%
\let\tikz@cs@node@b=\tikz@cs@node@a%
\let\tikz@cs@line@a=\tikz@cs@line@b%
\tikz@intersect@line@and@circle%
}%
}%