summaryrefslogtreecommitdiff
path: root/graphics/pgf/contrib/yquant/tex/yquant-tools.tex
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2020-06-14 03:02:26 +0000
committerNorbert Preining <norbert@preining.info>2020-06-14 03:02:26 +0000
commit893b65e5d4c0b7cb8ebbfca9ee6b090b9a4a182c (patch)
treef6b0cff2cf2f8e30ef3edbfa6c7205e588e81515 /graphics/pgf/contrib/yquant/tex/yquant-tools.tex
parent2d0067edb74f15b431d7a1e3a42b4f525986cef0 (diff)
CTAN sync 202006140302
Diffstat (limited to 'graphics/pgf/contrib/yquant/tex/yquant-tools.tex')
-rw-r--r--graphics/pgf/contrib/yquant/tex/yquant-tools.tex326
1 files changed, 319 insertions, 7 deletions
diff --git a/graphics/pgf/contrib/yquant/tex/yquant-tools.tex b/graphics/pgf/contrib/yquant/tex/yquant-tools.tex
index fe1efecbf4..5f7efe5e03 100644
--- a/graphics/pgf/contrib/yquant/tex/yquant-tools.tex
+++ b/graphics/pgf/contrib/yquant/tex/yquant-tools.tex
@@ -21,11 +21,27 @@
\let\@eattoken= %
}
+% a bit faster than nested \@firstoftwo/\@secondoftwo
+% note \@thirdofthree is defined in the latex kernel already.
+\long\def\@firstofthree#1#2#3{#1}%
+\long\def\@secondofthree#1#2#3{#2}%
+\long\def\@firstoffour#1#2#3#4{#1}%
+\long\def\@secondoffour#1#2#3#4{#2}%
+\long\def\@thirdoffour#1#2#3#4{#3}%
+\long\def\@fourthoffour#1#2#3#4{#4}%
+\long\def\@thirdandfourthoffour#1#2#3#4{#3#4}%
+\long\def\@fifthoffive#1#2#3#4#5{#5}
+
% Loop #1 from min(#2, #3) to max(#2, #3), executing #4
\protected\def\yquant@for #1:=#2to#3#{%
\yquant@for@aux#1{#2}{#3}%
}
+% Loop #1 from max(#2, #3) down to min(#2, #3), executing #4
+\protected\def\yquant@fordown #1:=#2downto#3#{%
+ \yquant@fordown@aux#1{#2}{#3}%
+}
+
\long\def\yquant@for@aux#1#2#3#4{%
\ifnum#2<#3\relax%
\numdef#1{#2}%
@@ -37,6 +53,17 @@
\fi%
}
+\long\def\yquant@fordown@aux#1#2#3#4{%
+ \ifnum#2>#3\relax%
+ \numdef#1{#2}%
+ % to allow for things like \yquant@for \i := \i to ..., expand the boundaries
+ \expandafter\yquant@fordown@loop\expandafter#1\expandafter{\the\numexpr#3-1\relax}{#4}%
+ \else%
+ \numdef#1{#3}%
+ \expandafter\yquant@fordown@loop\expandafter#1\expandafter{\the\numexpr#2-1\relax}{#4}%
+ \fi%
+}
+
\long\def\yquant@for@loop#1#2#3{%
\loop%
\ifnum#1<#2\relax%
@@ -45,6 +72,14 @@
\repeat%
}
+\long\def\yquant@fordown@loop#1#2#3{%
+ \loop%
+ \ifnum#1>#2\relax%
+ #3%
+ \numdef#1{#1-1}%
+ \repeat%
+}
+
\def\yquant@for@break{%
\fi%
\iffalse%
@@ -219,7 +254,7 @@
\expandafter\iffalse@hidden%
\fi%
\repeat%
- \expandafter
+ \expandafter%
}%
\expandafter\count\expandafter2\expandafter=\the\count2\relax%
\ifnum\count0<\count2 %
@@ -235,15 +270,21 @@
\cslet{yquant@sort@item#2}\tmp%
}
+% Add an internal etoolbox list to the sorted items
+\def\yquant@sort@addlist#1{%
+ \forlistloop\yquant@sort@addlist@aux#1%
+}
+
+\protected\def\yquant@sort@addlist@aux#1{%
+ \csdef{yquant@sort@item\the\yquant@sort@count}{#1}%
+ \advance\yquant@sort@count by 1 %
+}
+
% Sorts an internal etoolbox list #1 using macro #2
\protected\def\yquant@sort@list#1#2{%
\begingroup%
\yquant@sort@count=0 %
- \def\do##1{%
- \csdef{yquant@sort@item\the\yquant@sort@count}{##1}%
- \advance\yquant@sort@count by 1 %
- }%
- \dolistloop#1%
+ \yquant@sort@addlist#1%
\yquant@sort#2%
\let#1=\empty%
\count0=0 %
@@ -268,7 +309,278 @@
\expandafter\expandafter\expandafter\do%
\expandafter\expandafter\expandafter{%
\csname yquant@sort@item\the\count0\endcsname%
- }
+ }%
\advance\count0 by 1 %
\repeat%
+}
+
+\begingroup
+\catcode`\|=3
+\gdef\yquant@list@delim{|}
+
+\protected\gdef\yquant@list@dequeue#1#2{%
+ \expandafter\ifblank\expandafter{#1}{%
+ \let#2=\empty%
+ }{%
+ \expandafter\yquant@list@dequeue@i#1\etb@lst@q@end{#1}{#2}\def%
+ }%
+}%
+
+\protected\gdef\yquant@list@dequeue@i#1|#2\etb@lst@q@end#3#4#5{%
+ \def#4{#1}%
+ #5#3{#2}%
+}
+
+\protected\gdef\yquant@list@gdequeue#1#2{%
+ \expandafter\ifblank\expandafter{#1}{%
+ \let#2=\empty%
+ }{%
+ \expandafter\yquant@list@dequeue@i#1\etb@lst@q@end{#1}{#2}\gdef%
+ }%
+}
+\endgroup
+
+\def\ifyquant@OR#1#2{%
+ #1%
+ \expandafter\@firstoftwo%
+ \else%
+ #2%
+ \expandafter\expandafter\expandafter\@firstoftwo%
+ \else%
+ \expandafter\expandafter\expandafter\@secondoftwo%
+ \fi%
+ \fi%
+}
+
+% #1 is a pgf soft path. We extract the maximum x position at the y position specified in #2 and assign it to \dimen0.
+\protected\def\yquant@softpath@extractmaxxat#1#2{%
+ \begingroup%
+ \dimen0=-16000pt %
+ \dimen2=#2 %
+ \let\pgfsyssoftpath@movetotoken=\yquant@softpath@extractmaxxat@moveto%
+ \let\pgfsyssoftpath@linetotoken=\yquant@softpath@extractmaxxat@lineto%
+ \let\pgfsyssoftpath@curvetosupportatoken=\yquant@softpath@extractmaxxat@curveto%
+ \let\pgfsyssoftpath@rectcornertoken=\yquant@softpath@extractmaxxat@rectto%
+ \let\pgfsyssoftpath@closepath=\@gobbletwo%
+ % the specialroundtoken (undocumented) is \@gobbletwo by default.
+ #1%
+ \expandafter%
+ \endgroup%
+ \expandafter\dimen\expandafter0\expandafter=\the\dimen0 %
+}
+
+\protected\def\yquant@softpath@extractmaxxat@update#1{%
+ \ifdim\dimen0<#1 %
+ \dimen0=#1 %
+ \fi%
+}
+
+\protected\def\yquant@softpath@extractmaxxat@moveto#1#2{%
+ \dimen4=#1 %
+ \dimen6=#2 %
+}
+
+\protected\def\yquant@softpath@extractmaxxat@lineto#1#2{%
+ \ifyquant@OR{\ifdim\dimen4>\dimen0 }{\ifdim#1>\dimen0 }{%
+ \ifdim\dimen6=\dimen2 %
+ \yquant@softpath@extractmaxxat@update{\dimen4}%
+ \else%
+ \ifdim\dimen6<\dimen2 %
+ \unless\ifdim#2<\dimen2 %
+ \expandafter\yquant@softpath@extractmaxxat@update\expandafter{\the\dimexpr%
+ \dimen4+% x0
+ \dimexpr#1-\dimen4\relax*% (x1-x0)
+ \dimexpr\dimen2-\dimen6\relax/\dimexpr#2-\dimen6\relax% (y-y0)/(y1-y0)
+ \relax}%
+ \fi%
+ \else%
+ \unless\ifdim#2>\dimen2 %
+ \expandafter\yquant@softpath@extractmaxxat@update\expandafter{\the\dimexpr%
+ \dimen4+% x0
+ \dimexpr#1-\dimen4\relax*% (x1-x0)
+ \dimexpr\dimen2-\dimen6\relax/\dimexpr#2-\dimen6\relax% (y-y0)/(y1-y0)
+ \relax}%
+ \fi%
+ \fi%
+ \fi%
+ }\relax%
+ \dimen4=#1 %
+ \dimen6=#2 %
+}
+
+\protected\def\yquant@softpath@extractmaxxat@curveto@checkx#1#2#3{%
+ % \dimen11 holds our only candidate for t. Is it within the curve?
+ \unless\ifdim\dimen11<0pt %
+ \unless\ifdim\dimen11>1pt %
+ % it is. \dimen4: x0, #1: xa, #2: xb, #3: x1
+ \begingroup%
+ \dimen12=\dimexpr1pt-\dimen11\relax% 1 - t
+ \dimen13=\dimexpr\dimen11*\dimen11/65535\relax% t^2
+ \dimen14=\dimexpr\dimen12*\dimen12/65535\relax% (1 - t)^2
+ \dimen255=\dimexpr\dimen13*\dimen11/65535*\dimexpr#3\relax/65535+% t^3 x1
+ 3\dimen13*\dimen12/65535*\dimexpr#2\relax/65535+% t^2(1 - t) xb
+ \dimen14*\dimen12/65535*\dimen4/65535+% (1 - t)^3 x0
+ 3\dimen11*\dimen14/65535*\dimexpr#1\relax/65535% 3t(1 - t)^2 xa
+ \relax%
+ \expandafter%
+ \endgroup%
+ \expandafter\yquant@softpath@extractmaxxat@update\expandafter{\the\dimen255}%
+ \fi%
+ \fi%
+}
+
+\protected\def\yquant@softpath@extractmaxxat@curveto#1#2\pgfsyssoftpath@curvetosupportbtoken#3#4\pgfsyssoftpath@curvetotoken#5#6{%
+ % There's really no good way to do this apart from solving the Bézier curve (a third-order polynomial). Let's do it. (Yes, this is inefficient, but if someone substitutes the rectangular box of a subcircuit by a more fancy design, this is not our fault).
+ % Parametrized by t, the x coordinates of the curve are
+ % x0 + 3 (xa - x0) t + 3 (x0 - 2xa + xb) t^2 + (3xa - 3xb + x1 - x0) t^3
+ % where x0 = \dimen4 (the moveto point), xa = #1, xb = #3, x1 = #5.
+ % Likewise for y:
+ % y0 = \dimen6 (the moveto point), ya = #2, yb = #4, y1 = #6.
+ % We first solve the third-order polynomial for t using the y value, then plug it back into the x value.
+ % TODO: this is accurate to approx. 3 digits. Can this be improved by reformulating Cardanos formula to involve less divisions?
+ \begingroup%
+ % We need so may dimensions that we break with TeX's convention for their use.
+ % for the multiplications with and divisions by dimensions, we exploit that eTeX fuses muldiv to 64 bits. Further note that each dimension has a scaling factor of 65535 for sp<->pt conversion. This is why don't factor out divisions (which would be more efficient, but not give the benefit of 64bit accuracy).
+ % a = 3(ya - yb) + (y1 - y0)
+ \dimen1=\dimexpr3\dimexpr#2-#4\relax+#6-\dimen6\relax%
+ \ifdim\dimen1=0pt %
+ % this is only a quadratic curve!
+ % b = 3(y0 - 2ya + yb)
+ \dimen3=\dimexpr3\dimexpr\dimen6-2\dimexpr#2\relax+#4\relax*65535\relax%
+ % c: 3(ya - y0)
+ \dimen5=\dimexpr3\dimexpr#2-\dimen6\relax*65535\relax%
+ % d: y0 - <desired y>
+ \dimen7=\dimexpr\dimexpr\dimen6-\dimen2\relax*65535\relax%
+ % check the discriminant of the equation
+ \dimen8=\dimexpr\dimen3*\dimen3/65535-4\dimen3*\dimen7/65535\relax%
+ \unless\ifdim\dimen8<0pt%
+ % there are two potential candidates, (-c +- sqrt(c^2 - 4b d))/2b
+ \pgfmathsqrt@{\the\dimen8\@gobbletwo}%
+ \dimen11=\dimexpr\dimexpr-\dimen5+\pgfmathresult pt\relax*65535/%
+ \dimexpr2\dimen3\relax\relax%
+ \yquant@softpath@extractmaxxat@curveto@checkx{#1}{#3}{#5}%
+ \dimen11=\dimexpr\dimexpr-\dimen5-\pgfmathresult pt\relax*65535/%
+ \dimexpr2\dimen3\relax\relax%
+ \yquant@softpath@extractmaxxat@curveto@checkx{#1}{#3}{#5}%
+ \fi%
+ \else%
+ % We will simplify by directly dividing all coefficients by a
+ % b = 3(y0 - 2ya + yb)
+ \dimen3=\dimexpr3\dimexpr\dimen6-2\dimexpr#2\relax+#4\relax*65535/\dimen1\relax%
+ % c: 3(ya - y0)
+ \dimen5=\dimexpr3\dimexpr#2-\dimen6\relax*65535/\dimen1\relax%
+ % d: y0 - <desired y>
+ \dimen7=\dimexpr\dimexpr\dimen6-\dimen2\relax*65535/\dimen1\relax%
+ % Note that now our a value (\dimen1) is no longer needed, it is one.
+ % check the discriminant of the equation
+ % Q = (3c - b^2)/9
+ \dimen8=\dimexpr\dimexpr3\dimen5-\dimen3*\dimen3/65535\relax/9\relax%
+ % R = (9bc - 27d - 2b^3)/54 = bc/6 - d/2 - b^3/27
+ \dimen9=\dimexpr\dimen3*\dimen5/393210-% 6*65535
+ .5\dimen7-%
+ \dimen3*\dimen3/65535*\dimen3/1769445% 27*65535
+ \relax%
+ % D = Q^3 + R^2
+ \dimen10=\dimexpr\dimen8*\dimen8/65535*\dimen8/65535+\dimen9*\dimen9/65535\relax%
+ \ifdim\dimen10>0pt %
+ % only one real root: y_1 = S + T - b/3a
+ % S = cbrt(R + sqrt(Q^3 + R^2))
+ % T = cbrt(R - sqrt(Q^3 + R^2))
+ \pgfmathsqrt@{\the\dimen10\@gobbletwo}%
+ \dimen12=\dimexpr\dimen9+\pgfmathresult pt\relax%
+ \dimen13=\dimexpr\dimen9-\pgfmathresult pt\relax%
+ \ifdim\dimen12>0pt %
+ \pgfmathpow@{\the\dimen12\@gobbletwo}{.3333333333}%
+ \dimen11=\pgfmathresult pt %
+ \else%
+ \pgfmathpow@{\the\dimexpr-\dimen12\relax\@gobbletwo}{.3333333333}%
+ \dimen11=-\pgfmathresult pt %
+ \fi%
+ \ifdim\dimen13>0pt %
+ \pgfmathpow@{\the\dimen13\@gobbletwo}{.3333333333}%
+ \dimen11=\dimexpr\dimen11+\pgfmathresult pt-.33333333333\dimen3\relax%
+ \else%
+ \pgfmathpow@{\the\dimexpr-\dimen13\relax\@gobbletwo}{.3333333333}%
+ \dimen11=\dimexpr\dimen11-\pgfmathresult pt-.33333333333\dimen3\relax%
+ \fi%
+ \yquant@softpath@extractmaxxat@curveto@checkx{#1}{#3}{#5}%
+ \else%
+ \ifdim\dimen10=0pt %
+ % easiest case, three real roots, two of which are equal:
+ % y_1 = 2cbrt(R) - b/3a
+ % y_2, x_3 = -cbrt(R) - b/3a
+ \ifdim\dimen9>0pt %
+ \pgfmathpow@{\the\dimen9\@gobbletwo}{.3333333333}%
+ \dimen15=\pgfmathresult pt %
+ \else%
+ \pgfmathpow@{\the\dimexpr-\dimen9\relax\@gobbletwo}{.3333333333}%
+ \dimen15=-\pgfmathresult pt %
+ \fi%
+ \dimen11=\dimexpr2\dimen15-.33333333333\dimen3\relax%
+ \yquant@softpath@extractmaxxat@curveto@checkx{#1}{#3}{#5}%
+ % check the next candidate
+ \dimen11=\dimexpr-\dimen15-.33333333333\dimen3\relax%
+ \yquant@softpath@extractmaxxat@curveto@checkx{#1}{#3}{#5}%
+ \else%
+ % nastiest case, three distinct real roots which we can find only by taking a complex-valued cube root.
+ % p + i q = cbrt(R + i sqrt(|D|))
+ \pgfmathsqrt@{\the\dimexpr-\dimen10\relax\@gobbletwo}%
+ \dimen10=\pgfmathresult pt %
+ % Let us first find the absolute value
+ \dimen12=\dimexpr\dimen9*\dimen9/65535+\dimen10*\dimen10/65535\relax%
+ \pgfmathpow@{\the\dimen12\@gobbletwo}{.1666666667}%
+ \dimen12=\pgfmathresult pt%
+ % then we need 1/3 the argument of R + i sqrt(|D|).
+ \pgfmathatantwo@{\the\dimen10\@gobbletwo}{\the\dimen9\@gobbletwo}%
+ \dimen13=.3333333333\dimexpr\pgfmathresult pt\relax%
+ % and then the real and imaginary parts as cosine and sine.
+ \pgfmathcos@{\the\dimen13\@gobbletwo}%
+ \dimen14=\dimexpr\pgfmathresult\dimen12\relax%
+ \pgfmathsin@{\the\dimen13\@gobbletwo}%
+ \dimen15=\dimexpr\pgfmathresult\dimen12\relax%
+ % Now the candidates are
+ % y_1 = 2p - b/3a
+ % y_2 = -p - sqrt(3)q - b/3a
+ % y_3 = -p + sqrt(3)q - b/3a
+ \dimen11=\dimexpr2\dimen14-.33333333333\dimen3\relax%
+ \yquant@softpath@extractmaxxat@curveto@checkx{#1}{#3}{#5}%
+ \dimen11=\dimexpr-\dimen14-1.732050808\dimen15-.33333333333\dimen3\relax%
+ \yquant@softpath@extractmaxxat@curveto@checkx{#1}{#3}{#5}%
+ \dimen11=\dimexpr-\dimen14+1.732050808\dimen15-.33333333333\dimen3\relax%
+ \yquant@softpath@extractmaxxat@curveto@checkx{#1}{#3}{#5}%
+ \fi%
+ \fi%
+ \fi%
+ % Now after all these calculations, \dimen0 was updated within the group. Make available outside.
+ \expandafter%
+ \endgroup%
+ \expandafter\dimen\expandafter0\expandafter=\the\dimen0 %
+ \dimen4=#5 %
+ \dimen6=#6 %
+}
+
+\protected\def\yquant@softpath@extractmaxxat@rectto#1#2\pgfsyssoftpath@rectsizetoken#3#4{%
+ % #1: lower left x, #2: lower left y, #3: width, #4: height
+ % note that neither width nor height need be positive!
+ \ifdim#4>0pt %
+ \unless\ifdim#2>\dimen2 %
+ \unless\ifdim\dimexpr#2+#4\relax<\dimen2 %
+ \ifdim#3>0pt %
+ \yquant@softpath@extractmaxxat@update{\dimexpr#1+#3\relax}%
+ \else%
+ \yquant@softpath@extractmaxxat@update{#1}%
+ \fi%
+ \fi%
+ \fi%
+ \else%
+ \unless\ifdim#2<\dimen2 %
+ \unless\ifdim\dimexpr#2+#4\relax>\dimen2 %
+ \ifdim#3>0pt %
+ \yquant@softpath@extractmaxxat@update{\dimexpr#1+#3\relax}%
+ \else%
+ \yquant@softpath@extractmaxxat@update{#1}%
+ \fi%
+ \fi%
+ \fi%
+ \fi%
} \ No newline at end of file