% 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{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 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 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% }% }%