% \iffalse meta-comment % %% File: l3draw-points.dtx % % Copyright (C) 2018-2024 The LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % http://www.latex-project.org/lppl.txt % % This file is part of the "l3experimental bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/latex3/latex3 % % for those people who are interested. % %<*driver> \RequirePackage{expl3} \documentclass[full]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3draw-points} package\\ Calculating points^^A % } % % \author{^^A % The \LaTeX{} Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2024-03-14} % % \maketitle % % \begin{implementation} % % \section{\pkg{l3draw-points} implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=draw> % \end{macrocode} % % This sub-module covers more-or-less the same ideas as % \texttt{pgfcorepoints.code.tex}, though the approach taken to returning % values is different: point expressions here are processed by expansion % and return a co-ordinate pair in the form |{|\meta{x}|}{|\meta{y}|}|. % Equivalents of following \pkg{pgf} functions are deliberately omitted: % \begin{itemize} % \item \cs{pgfpointorigin}: Can be given explicitly as |0pt,0pt|. % \item \cs{pgfpointadd}, \cs{pgfpointdiff}, % \cs{pgfpointscale}: Can be given explicitly. % \item \cs{pgfextractx}, \cs{pgfextracty}: Available by applying % \cs{use_i:nn}/\cs{use_ii:nn} or similar to the \texttt{e}-type % expansion of a point expression. % \item \cs{pgfgetlastxy}: Unused in the entire \pkg{pgf} core, may be % emulated by \texttt{e}-type expansion of a point expression, then using % the result. % \end{itemize} % In addition, equivalents of the following \emph{may} be added in future but % are currently absent: % \begin{itemize} % \item \cs{pgfpointcylindrical}, \cs{pgfpointspherical}: The usefulness % of these commands is not currently clear. % \item \cs{pgfpointborderrectangle}, \cs{pgfpointborderellipse}: To be % revisited once the semantics and use cases are clear. % \item \cs{pgfqpoint}, \cs{pgfqpointscale}, \cs{pgfqpointpolar}, % \cs{pgfqpointxy}, \cs{pgfqpointxyz}: The expandable approach taken in % the code here, along with the absolute requirement for \eTeX{}, means % it is likely many use cases for these commands may be covered in other % ways. This may be revisited as higher-level structures are constructed. % \end{itemize} % % \subsection{Support functions} % % \begin{macro}[EXP]{\@@_point_process:nn} % \begin{macro}[EXP]{\@@_point_process_auxi:nn, \@@_point_process_auxi:en} % \begin{macro}[EXP]{\@@_point_process_auxii:nw} % \begin{macro}[EXP]{\@@_point_process:nnn} % \begin{macro}[EXP]{\@@_point_process_auxiii:nnn, \@@_point_process_auxiii:een} % \begin{macro}[EXP]{\@@_point_process_auxiv:nw} % \begin{macro}[EXP]{\@@_point_process:nnnn} % \begin{macro}[EXP] % {\@@_point_process_auxv:nnnn, \@@_point_process_auxv:eeen} % \begin{macro}[EXP]{\@@_point_process_auxvi:nw} % \begin{macro}[EXP]{\@@_point_process:nnnnn} % \begin{macro}[EXP] % {\@@_point_process_auxvii:nnnnn, \@@_point_process_auxvii:eeeen} % \begin{macro}[EXP]{\@@_point_process_auxviii:nw} % Execute whatever code is passed to extract the $x$ and $y$ co-ordinates. % The first argument here should itself absorb two arguments. There is % also a version to deal with two co-ordinates: common enough to justify a % separate function. % \begin{macrocode} \cs_new:Npn \@@_point_process:nn #1#2 { \@@_point_process_auxi:en { \draw_point:n {#2} } {#1} } \cs_new:Npn \@@_point_process_auxi:nn #1#2 { \@@_point_process_auxii:nw {#2} #1 \s_@@_stop } \cs_generate_variant:Nn \@@_point_process_auxi:nn { e } \cs_new:Npn \@@_point_process_auxii:nw #1 #2 , #3 \s_@@_stop { #1 {#2} {#3} } \cs_new:Npn \@@_point_process:nnn #1#2#3 { \@@_point_process_auxiii:een { \draw_point:n {#2} } { \draw_point:n {#3} } {#1} } \cs_new:Npn \@@_point_process_auxiii:nnn #1#2#3 { \@@_point_process_auxiv:nw {#3} #1 \s_@@_mark #2 \s_@@_stop } \cs_generate_variant:Nn \@@_point_process_auxiii:nnn { ee } \cs_new:Npn \@@_point_process_auxiv:nw #1 #2 , #3 \s_@@_mark #4 , #5 \s_@@_stop { #1 {#2} {#3} {#4} {#5} } \cs_new:Npn \@@_point_process:nnnn #1#2#3#4 { \@@_point_process_auxv:eeen { \draw_point:n {#2} } { \draw_point:n {#3} } { \draw_point:n {#4} } {#1} } \cs_new:Npn \@@_point_process_auxv:nnnn #1#2#3#4 { \@@_point_process_auxvi:nw {#4} #1 \s_@@_mark #2 \s_@@_mark #3 \s_@@_stop } \cs_generate_variant:Nn \@@_point_process_auxv:nnnn { eee } \cs_new:Npn \@@_point_process_auxvi:nw #1 #2 , #3 \s_@@_mark #4 , #5 \s_@@_mark #6 , #7 \s_@@_stop { #1 {#2} {#3} {#4} {#5} {#6} {#7} } \cs_new:Npn \@@_point_process:nnnnn #1#2#3#4#5 { \@@_point_process_auxvii:eeeen { \draw_point:n {#2} } { \draw_point:n {#3} } { \draw_point:n {#4} } { \draw_point:n {#5} } {#1} } \cs_new:Npn \@@_point_process_auxvii:nnnnn #1#2#3#4#5 { \@@_point_process_auxviii:nw {#5} #1 \s_@@_mark #2 \s_@@_mark #3 \s_@@_mark #4 \s_@@_stop } \cs_generate_variant:Nn \@@_point_process_auxvii:nnnnn { eeee } \cs_new:Npn \@@_point_process_auxviii:nw #1 #2 , #3 \s_@@_mark #4 , #5 \s_@@_mark #6 , #7 \s_@@_mark #8 , #9 \s_@@_stop { #1 {#2} {#3} {#4} {#5} {#6} {#7} {#8} {#9} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Basic points} % % \begin{macro}[EXP]{\draw_point:n} % \begin{macro}[EXP]{\@@_point_to_dim:n, \@@_point_to_dim:e} % \begin{macro}[EXP]{\@@_point_to_dim:w} % Co-ordinates are always returned as two dimensions. % \begin{macrocode} \cs_new:Npn \draw_point:n #1 { \@@_point_to_dim:e { \fp_eval:n {#1} } } \cs_new:Npn \@@_point_to_dim:n #1 { \@@_point_to_dim:w #1 } \cs_generate_variant:Nn \@@_point_to_dim:n { e } \cs_new:Npn \@@_point_to_dim:w ( #1 , ~ #2 ) { #1pt , #2pt } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Polar co-ordinates} % % \begin{macro}[EXP]{\draw_point_polar:nn} % \begin{macro}[EXP]{\draw_point_polar:nnn} % \begin{macro}[EXP]{\@@_draw_polar:nnn, \@@_draw_polar:enn} % Polar co-ordinates may have either one or two lengths, so there is a need % to do a simple split before the calculation. As the angle gets used twice, % save on any expression evaluation there and force expansion. % \begin{macrocode} \cs_new:Npn \draw_point_polar:nn #1#2 { \draw_point_polar:nnn {#1} {#1} {#2} } \cs_new:Npn \draw_point_polar:nnn #1#2#3 { \@@_draw_polar:enn { \fp_eval:n {#3} } {#1} {#2} } \cs_new:Npn \@@_draw_polar:nnn #1#2#3 { \draw_point:n { cosd(#1) * (#2) , sind(#1) * (#3) } } \cs_generate_variant:Nn \@@_draw_polar:nnn { e } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Point expression arithmetic} % % These functions all take point expressions as arguments. % % \begin{macro}[EXP]{\draw_point_unit_vector:n} % \begin{macro}[EXP]{\@@_point_unit_vector:nn} % \begin{macro}[EXP]{\@@_point_unit_vector:nnn, \@@_point_unit_vector:enn} % The outcome is the normalised vector from $(0,0)$ in the direction of % the point, \emph{i.e.} % \[ % P_{x} = \frac{x}{\sqrt{x^{2} + y^{2}}} \quad % P_{y} = \frac{y}{\sqrt{x^{2} + y^{2}}} % \] % except where the length is zero, in which case a vertical vector is % returned. % \begin{macrocode} \cs_new:Npn \draw_point_unit_vector:n #1 { \@@_point_process:nn { \@@_point_unit_vector:nn } {#1} } \cs_new:Npn \@@_point_unit_vector:nn #1#2 { \@@_point_unit_vector:nnn { \fp_eval:n { (sqrt(#1 * #1 + #2 * #2)) } } {#1} {#2} } \cs_new:Npn \@@_point_unit_vector:nnn #1#2#3 { \fp_compare:nNnTF {#1} = \c_zero_fp { 0pt, 1pt } { \draw_point:n { ( #2 , #3 ) / #1 } } } \cs_generate_variant:Nn \@@_point_unit_vector:nnn { e } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Intersection calculations} % % \begin{macro}[EXP]{\draw_point_intersect_lines:nnnn} % \begin{macro}[EXP]{\@@_point_intersect_lines:nnnnnn} % \begin{macro}[EXP]{\@@_point_intersect_lines:nnnnnnnn} % \begin{macro}[EXP] % {\@@_point_intersect_lines_aux:nnnnnn, \@@_point_intersect_lines_aux:eeeeee} % The intersection point~$P$ between a line joining points $(x_{1}, y_{1})$ % and $(x_{2}, y_{2})$ with a second line joining points $(x_{3}, y_{3})$ % and $(x_{4}, y_{4})$ can be calculated using the formulae % \[ % P_{x} = % \frac{(x_{1}y_{2} - y_{1}x_{2})(x_{3} - x_{4}) % - (x_{3}y_{4} - y_{3}x_{4})(x_{1} - x_{2})} % {(x_{1} - x_{2})(y_{3} - y_{4}) - (y_{1} - y_{2})(x_{3} - x_{4})} % \] % and % \[ % P_{y} = % \frac{(x_{1}y_{2} - y_{1}x_{2})(y_{3} - y_{5}) % - (x_{3}y_{4} - y_{3}x_{4})(y_{1} - y_{2})} % {(x_{1} - x_{2})(y_{3} - y_{4}) - (y_{1} - y_{2})(x_{3} - x_{4})} % \] % The work therefore comes down to expanding the incoming data, then % pre-calculating as many parts as possible before the final work to find % the intersection. (Expansion and argument re-ordering is much less work % than additional floating point calculations.) % \begin{macrocode} \cs_new:Npn \draw_point_intersect_lines:nnnn #1#2#3#4 { \@@_point_process:nnnnn { \@@_point_intersect_lines:nnnnnnnn } {#1} {#2} {#3} {#4} } % \end{macrocode} % At this stage we have all of the information we need, fully expanded: % \begin{enumerate}[label = \#\arabic*, font = \ttfamily] % \item $x_{1}$ % \item $y_{1}$ % \item $x_{2}$ % \item $y_{2}$ % \item $x_{3}$ % \item $y_{3}$ % \item $x_{4}$ % \item $y_{4}$ % \end{enumerate} % so now just have to do all of the calculation. % \begin{macrocode} \cs_new:Npn \@@_point_intersect_lines:nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_point_intersect_lines_aux:eeeeee { \fp_eval:n { #1 * #4 - #2 * #3 } } { \fp_eval:n { #5 * #8 - #6 * #7 } } { \fp_eval:n { #1 - #3 } } { \fp_eval:n { #5 - #7 } } { \fp_eval:n { #2 - #4 } } { \fp_eval:n { #6 - #8 } } } \cs_new:Npn \@@_point_intersect_lines_aux:nnnnnn #1#2#3#4#5#6 { \draw_point:n { ( #2 * #3 - #1 * #4 , #2 * #5 - #1 * #6 ) / ( #4 * #5 - #6 * #3 ) } } \cs_generate_variant:Nn \@@_point_intersect_lines_aux:nnnnnn { eeeeee } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\draw_point_intersect_circles:nnnnn} % \begin{macro}[EXP]{\@@_point_intersect_circles_auxi:nnnnnnn} % \begin{macro}[EXP] % { % \@@_point_intersect_circles_auxii:nnnnnnn, % \@@_point_intersect_circles_auxii:eennnnn, % \@@_point_intersect_circles_auxiii:nnnnnnn, % \@@_point_intersect_circles_auxiii:eennnnn % } % \begin{macro}[EXP] % { % \@@_point_intersect_circles_auxiv:nnnnnnnn, % \@@_point_intersect_circles_auxiv:ennnnnnn % } % \begin{macro}[EXP] % { % \@@_point_intersect_circles_auxv:nnnnnnnnn, % \@@_point_intersect_circles_auxv:eennnnnnn % } % \begin{macro}[EXP] % { % \@@_point_intersect_circles_auxvi:nnnnnnnn, % \@@_point_intersect_circles_auxvi:ennnnnnn % } % \begin{macro}[EXP] % { % \@@_point_intersect_circles_auxvii:nnnnnnn, % \@@_point_intersect_circles_auxvii:eeennnn % } % Another long expansion chain to get the values in the right places. % We have two circles, the first with center $(a, b)$ and radius~$r$, % the second with center $(c, d)$ and radius~$s$. We use the intermediate % values % \begin{align*} % e &= c - a \\ % f &= d - b \\ % p &= \sqrt{e^{2} + f^{2}} \\ % k &= \frac{p^{2} + r^{2} - s^{2}}{2p} % \end{align*} % in either % \begin{align*} % P_{x} &= a + \frac{ek}{p} + \frac{f}{p}\sqrt{r^{2} - k^{2}} \\ % P_{y} &= b + \frac{fk}{p} - \frac{e}{p}\sqrt{r^{2} - k^{2}} % \end{align*} % or % \begin{align*} % P_{x} &= a + \frac{ek}{p} - \frac{f}{p}\sqrt{r^{2} - k^{2}} \\ % P_{y} &= b + \frac{fk}{p} + \frac{e}{p}\sqrt{r^{2} - k^{2}} % \end{align*} % depending on which solution is required. The rest of the work is simply % forcing the appropriate expansion and shuffling arguments. % \begin{macrocode} \cs_new:Npn \draw_point_intersect_circles:nnnnn #1#2#3#4#5 { \@@_point_process:nnn { \@@_point_intersect_circles_auxi:nnnnnnn {#2} {#4} {#5} } {#1} {#3} } \cs_new:Npn \@@_point_intersect_circles_auxi:nnnnnnn #1#2#3#4#5#6#7 { \@@_point_intersect_circles_auxii:eennnnn { \fp_eval:n {#1} } { \fp_eval:n {#2} } {#4} {#5} {#6} {#7} {#3} } % \end{macrocode} % At this stage we have all of the information we need, fully expanded: % \begin{enumerate}[label = \#\arabic*, font = \ttfamily] % \item $r$ % \item $s$ % \item $a$ % \item $b$ % \item $c$ % \item $d$ % \item $n$ % \end{enumerate} % Once we evaluate $e$ and $f$, the co-ordinate $(c,d)$ is no longer % required: handy as we will need various intermediate values in the % following. % \begin{macrocode} \cs_new:Npn \@@_point_intersect_circles_auxii:nnnnnnn #1#2#3#4#5#6#7 { \@@_point_intersect_circles_auxiii:eennnnn { \fp_eval:n { #5 - #3 } } { \fp_eval:n { #6 - #4 } } {#1} {#2} {#3} {#4} {#7} } \cs_generate_variant:Nn \@@_point_intersect_circles_auxii:nnnnnnn { ee } \cs_new:Npn \@@_point_intersect_circles_auxiii:nnnnnnn #1#2#3#4#5#6#7 { \@@_point_intersect_circles_auxiv:ennnnnnn { \fp_eval:n { sqrt( #1 * #1 + #2 * #2 ) } } {#1} {#2} {#3} {#4} {#5} {#6} {#7} } \cs_generate_variant:Nn \@@_point_intersect_circles_auxiii:nnnnnnn { ee } % \end{macrocode} % We now have $p$: we pre-calculate $1/p$ as it is needed a few times and % is relatively expensive. We also need $r^{2}$ twice so deal with that % here too. % \begin{macrocode} \cs_new:Npn \@@_point_intersect_circles_auxiv:nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_point_intersect_circles_auxv:eennnnnnn { \fp_eval:n { 1 / #1 } } { \fp_eval:n { #4 * #4 } } {#1} {#2} {#3} {#5} {#6} {#7} {#8} } \cs_generate_variant:Nn \@@_point_intersect_circles_auxiv:nnnnnnnn { e } \cs_new:Npn \@@_point_intersect_circles_auxv:nnnnnnnnn #1#2#3#4#5#6#7#8#9 { \@@_point_intersect_circles_auxvi:ennnnnnn { \fp_eval:n { 0.5 * #1 * ( #2 + #3 * #3 - #6 * #6 ) } } {#1} {#2} {#4} {#5} {#7} {#8} {#9} } \cs_generate_variant:Nn \@@_point_intersect_circles_auxv:nnnnnnnnn { ee } % \end{macrocode} % We now have all of the intermediate values we require, with one division % carried out up-front to avoid doing this expensive step twice: % \begin{enumerate}[label = \#\arabic*, font = \ttfamily] % \item $k$ % \item $1/p$ % \item $r^{2}$ % \item $e$ % \item $f$ % \item $a$ % \item $b$ % \item $n$ % \end{enumerate} % There are some final pre-calculations, $k/p$, % $\frac{\sqrt{r^{2} - k^{2}}}{p}$ and the usage of $n$, then we % can yield a result. % \begin{macrocode} \cs_new:Npn \@@_point_intersect_circles_auxvi:nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_point_intersect_circles_auxvii:eeennnn { \fp_eval:n { #1 * #2 } } { \int_if_odd:nTF {#8} { 1 } { -1 } } { \fp_eval:n { sqrt ( #3 - #1 * #1 ) * #2 } } {#4} {#5} {#6} {#7} } \cs_generate_variant:Nn \@@_point_intersect_circles_auxvi:nnnnnnnn { e } \cs_new:Npn \@@_point_intersect_circles_auxvii:nnnnnnn #1#2#3#4#5#6#7 { \draw_point:n { #6 + #4 * #1 + #2 * #3 * #5 , #7 + #5 * #1 + -1 * #2 * #3 * #4 } } \cs_generate_variant:Nn \@@_point_intersect_circles_auxvii:nnnnnnn { eee } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\draw_point_intersect_line_circle:nnnnn} % \begin{macro}[EXP]{\@@_point_intersect_line_circle_auxi:nnnnnnnn} % \begin{macro}[EXP] % { % \@@_point_intersect_line_circle_auxii:nnnnnnnn, % \@@_point_intersect_line_circle_auxii:ennnnnnn, % \@@_point_intersect_line_circle_auxiii:nnnnnnnn, % \@@_point_intersect_line_circle_auxiii:eeennnnn % } % \begin{macro}[EXP] % { % \@@_point_intersect_line_circle_auxiv:nnnnnnnn, % \@@_point_intersect_line_circle_auxiv:eennnnnn % } % \begin{macro}[EXP] % { % \@@_point_intersect_line_circle_auxv:nnnnn, % \@@_point_intersect_line_circle_auxv:ennnn % } % The intersection points~$P_{1}$ and~$P_{2}$ between % a line joining points $(x_{1}, y_{1})$ and $(x_{2}, y_{2})$ % and a circle with center $(x_{3}, y_{3})$ and radius~$r$. % We use the intermediate % values % \begin{align*} % a &= (x_{2} - x_{1})^{2} + (y_{2} - y_{1})^{2} \\ % b &= 2 \times ((x_{2} - x_{1}) \times (x_{1} - x_{3}) + (y_{2} - y_{1}) \times (y_{1} - y_{3})) \\ % c &= x_{3}^{2} + y_{3}^{2} + x_{1}^{2} + y_{1}^{2} % - 2\times(x_{3} \times x_{1} + y_{3} \times y_{1}) - r^{2} \\ % d &= b^{2} - 4\times a \times c \\ % \mu_{1} &= \frac{-b + \sqrt{d}}{2 \times a}\\ % \mu_{2} &= \frac{-b - \sqrt{d}}{2 \times a} % \end{align*} % in either % \begin{align*} % P_{1x} &= x_{1} + \mu_{1}\times (x_{2} - x_{1}) \\ % P_{1y} &= y_{1} + \mu_{1}\times (y_{2} - y_{1}) % \end{align*} % or % \begin{align*} % P_{2x} &= x_{1} + \mu_{2}\times (x_{2} - x_{1}) \\ % P_{2y} &= y_{1} + \mu_{2}\times (y_{2} - y_{1}) % \end{align*} % depending on which solution is required. The rest of the work is simply % forcing the appropriate expansion and shuffling arguments. % \begin{macrocode} \cs_new:Npn \draw_point_intersect_line_circle:nnnnn #1#2#3#4#5 { \@@_point_process:nnnn { \@@_point_intersect_line_circle_auxi:nnnnnnnn {#4} {#5} } {#1} {#2} {#3} } \cs_new:Npn \@@_point_intersect_line_circle_auxi:nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_point_intersect_line_circle_auxii:ennnnnnn { \fp_eval:n {#1} } {#3} {#4} {#5} {#6} {#7} {#8} {#2} } % \end{macrocode} % At this stage we have all of the information we need, fully expanded: % \begin{enumerate}[label = \#\arabic*, font = \ttfamily] % \item $r$ % \item $x_{1}$ % \item $y_{1}$ % \item $x_{2}$ % \item $y_{2}$ % \item $x_{3}$ % \item $y_{3}$ % \item $n$ % \end{enumerate} % Once we evaluate $a$, $b$ and $c$, the co-ordinate $(x_{3},y_{3})$ % and $r$ are no longer required: handy as we will need various % intermediate values in the following. % \begin{macrocode} \cs_new:Npn \@@_point_intersect_line_circle_auxii:nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_point_intersect_line_circle_auxiii:eeennnnn { \fp_eval:n { (#4-#2)*(#4-#2)+(#5-#3)*(#5-#3) } } { \fp_eval:n { 2*((#4-#2)*(#2-#6)+(#5-#3)*(#3-#7)) } } { \fp_eval:n { (#6*#6+#7*#7)+(#2*#2+#3*#3)-(2*(#6*#2+#7*#3))-(#1*#1) } } {#2} {#3} {#4} {#5} {#8} } \cs_generate_variant:Nn \@@_point_intersect_line_circle_auxii:nnnnnnnn { e } % \end{macrocode} % then we can get $d = b^{2} - 4\times a \times c$ and the usage of $n$. % \begin{macrocode} \cs_new:Npn \@@_point_intersect_line_circle_auxiii:nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_point_intersect_line_circle_auxiv:eennnnnn { \fp_eval:n { #2 * #2 - 4 * #1 * #3 } } { \int_if_odd:nTF {#8} { 1 } { -1 } } {#1} {#2} {#4} {#5} {#6} {#7} } \cs_generate_variant:Nn \@@_point_intersect_line_circle_auxiii:nnnnnnnn { eee } % \end{macrocode} % We now have all of the intermediate values we require, with one division % carried out up-front to avoid doing this expensive step twice: % \begin{enumerate}[label = \#\arabic*, font = \ttfamily] % \item $a$ % \item $b$ % \item $c$ % \item $d$ % \item $\pm$(the usage of $n$) % \item $x_{1}$ % \item $y_{1}$ % \item $x_{2}$ % \item $y_{2}$ % \end{enumerate} % There are some final pre-calculations, % $\mu = \frac{-b \pm \sqrt{d}}{2 \times a}$ % then, we can yield a result. % \begin{macrocode} \cs_new:Npn \@@_point_intersect_line_circle_auxiv:nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_point_intersect_line_circle_auxv:ennnn { \fp_eval:n { (-1 * #4 + #2 * sqrt(#1)) / (2 * #3) } } {#5} {#6} {#7} {#8} } \cs_generate_variant:Nn \@@_point_intersect_line_circle_auxiv:nnnnnnnn { ee } \cs_new:Npn \@@_point_intersect_line_circle_auxv:nnnnn #1#2#3#4#5 { \draw_point:n { #2 + #1 * (#4 - #2), #3 + #1 * (#5 - #3) } } \cs_generate_variant:Nn \@@_point_intersect_line_circle_auxv:nnnnn { e } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Interpolation on a line (vector) or arc} % % \begin{macro}[EXP]{\draw_point_interpolate_line:nnn} % \begin{macro}[EXP] % { % \@@_point_interpolate_line_aux:nnnnn, % \@@_point_interpolate_line_aux:ennnn, % } % \begin{macro}[EXP] % { % \@@_point_interpolate_line_aux:nnnnnn, % \@@_point_interpolate_line_aux:ennnnn, % } % Simple maths after expansion. % \begin{macrocode} \cs_new:Npn \draw_point_interpolate_line:nnn #1#2#3 { \@@_point_process:nnn { \@@_point_interpolate_line_aux:ennnn { \fp_eval:n {#1} } } {#2} {#3} } \cs_new:Npn \@@_point_interpolate_line_aux:nnnnn #1#2#3#4#5 { \@@_point_interpolate_line_aux:ennnnn { \fp_eval:n { 1 - #1 } } {#1} {#2} {#3} {#4} {#5} } \cs_generate_variant:Nn \@@_point_interpolate_line_aux:nnnnn { e } \cs_new:Npn \@@_point_interpolate_line_aux:nnnnnn #1#2#3#4#5#6 { \draw_point:n { #2 * #3 + #1 * #5 , #2 * #4 + #1 * #6 } } \cs_generate_variant:Nn \@@_point_interpolate_line_aux:nnnnnn { e } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\draw_point_interpolate_distance:nnn} % \begin{macro}[EXP]{\@@_point_interpolate_distance:nnnnn} % \begin{macro}[EXP] % { % \@@_point_interpolate_distance:nnnnnn, % \@@_point_interpolate_distance:ennnnn, % } % Same idea but using the normalised length to obtain the scale factor. % The start point is needed twice, so we force evaluation, but the end % point is needed only the once. % \begin{macrocode} \cs_new:Npn \draw_point_interpolate_distance:nnn #1#2#3 { \@@_point_process:nn { \@@_point_interpolate_distance:nnnn {#1} {#3} } {#2} } \cs_new:Npn \@@_point_interpolate_distance:nnnn #1#2#3#4 { \@@_point_process:nn { \@@_point_interpolate_distance:ennnn { \fp_eval:n {#1} } {#3} {#4} } { \draw_point_unit_vector:n { ( #2 ) - ( #3 , #4 ) } } } \cs_new:Npn \@@_point_interpolate_distance:nnnnn #1#2#3#4#5 { \draw_point:n { #2 + #1 * #4 , #3 + #1 * #5 } } \cs_generate_variant:Nn \@@_point_interpolate_distance:nnnnn { e } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\draw_point_interpolate_arcaxes:nnnnnn} % \begin{macro}[EXP]{\@@_point_interpolate_arcaxes_auxi:nnnnnnnnn} % \begin{macro}[EXP] % { % \@@_point_interpolate_arcaxes_auxii:nnnnnnnnn, % \@@_point_interpolate_arcaxes_auxii:ennnnnnnn % } % \begin{macro}[EXP] % { % \@@_point_interpolate_arcaxes_auxiii:nnnnnnn, % \@@_point_interpolate_arcaxes_auxiii:ennnnnn % } % \begin{macro}[EXP] % { % \@@_point_interpolate_arcaxes_auxiv:nnnnnnnn, % \@@_point_interpolate_arcaxes_auxiv:eennnnnn % } % Finding a point on an ellipse arc is relatively easy: find the correct % angle between the two given, use the sine and cosine of that angle, % apply to the axes. We just have to work a bit with the co-ordinate % expansion. % \begin{macrocode} \cs_new:Npn \draw_point_interpolate_arcaxes:nnnnnn #1#2#3#4#5#6 { \@@_point_process:nnnn { \@@_point_interpolate_arcaxes_auxi:nnnnnnnnn {#1} {#5} {#6} } {#2} {#3} {#4} } \cs_new:Npn \@@_point_interpolate_arcaxes_auxi:nnnnnnnnn #1#2#3#4#5#6#7#8#9 { \@@_point_interpolate_arcaxes_auxii:ennnnnnnn { \fp_eval:n {#1} } {#2} {#3} {#4} {#5} {#6} {#7} {#8} {#9} } % \end{macrocode} % At this stage, the three co-ordinate pairs are fully expanded but somewhat % re-ordered: % \begin{enumerate}[label = \#\arabic*, font = \ttfamily] % \item $p$ % \item $\theta_{1}$ % \item $\theta_{2}$ % \item $x_{c}$ % \item $y_{c}$ % \item $x_{a1}$ % \item $y_{a1}$ % \item $x_{a2}$ % \item $y_{a2}$ % \end{enumerate} % We are now in a position to find the target angle, and from that % the sine and cosine required. % \begin{macrocode} \cs_new:Npn \@@_point_interpolate_arcaxes_auxii:nnnnnnnnn #1#2#3#4#5#6#7#8#9 { \@@_point_interpolate_arcaxes_auxiii:ennnnnn { \fp_eval:n { #1 * (#3) + ( 1 - #1 ) * (#2) } } {#4} {#5} {#6} {#7} {#8} {#9} } \cs_generate_variant:Nn \@@_point_interpolate_arcaxes_auxii:nnnnnnnnn { e } \cs_new:Npn \@@_point_interpolate_arcaxes_auxiii:nnnnnnn #1#2#3#4#5#6#7 { \@@_point_interpolate_arcaxes_auxiv:eennnnnn { \fp_eval:n { cosd (#1) } } { \fp_eval:n { sind (#1) } } {#2} {#3} {#4} {#5} {#6} {#7} } \cs_generate_variant:Nn \@@_point_interpolate_arcaxes_auxiii:nnnnnnn { e } \cs_new:Npn \@@_point_interpolate_arcaxes_auxiv:nnnnnnnn #1#2#3#4#5#6#7#8 { \draw_point:n { #3 + #1 * #5 + #2 * #7 , #4 + #1 * #6 + #2 * #8 } } \cs_generate_variant:Nn \@@_point_interpolate_arcaxes_auxiv:nnnnnnnn { ee } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\draw_point_interpolate_curve:nnnnn} % \begin{macro}[EXP]{\draw_point_interpolate_curve_auxi:nnnnnnnnn} % \begin{macro}[EXP] % { % \draw_point_interpolate_curve_auxii:nnnnnnnnn, % \draw_point_interpolate_curve_auxii:ennnnnnnn, % } % \begin{macro}[EXP] % { % \draw_point_interpolate_curve_auxiii:nnnnnn, % \draw_point_interpolate_curve_auxiii:ennnnn, % } % \begin{macro}[EXP]{\draw_point_interpolate_curve_auxiv:nnnnnn} % \begin{macro}[EXP] % { % \draw_point_interpolate_curve_auxv:nnw, % \draw_point_interpolate_curve_auxv:eew, % } % \begin{macro}[EXP]{\draw_point_interpolate_curve_auxvi:n} % \begin{macro}[EXP]{\draw_point_interpolate_curve_auxvii:nnnnnnnn} % \begin{macro}[EXP] % { % \draw_point_interpolate_curve_auxviii:nnnnnn, % \draw_point_interpolate_curve_auxviii:eennnn, % } % Here we start with a proportion of the curve ($p$) and four points % \begin{enumerate} % \item The initial point $(x_{1},y_{1})$ % \item The first control point $(x_{2},y_{2})$ % \item The second control point $(x_{3},y_{3})$ % \item The final point $(x_{4},y_{4})$ % \end{enumerate} % The first phase is to expand out all of these values. % \begin{macrocode} \cs_new:Npn \draw_point_interpolate_curve:nnnnnn #1#2#3#4#5 { \@@_point_process:nnnnn { \@@_point_interpolate_curve_auxi:nnnnnnnnn {#1} } {#2} {#3} {#4} {#5} } \cs_new:Npn \@@_point_interpolate_curve_auxi:nnnnnnnnn #1#2#3#4#5#6#7#8#9 { \@@_point_interpolate_curve_auxii:ennnnnnnn { \fp_eval:n {#1} } {#2} {#3} {#4} {#5} {#6} {#7} {#8} {#9} } % \end{macrocode} % At this stage, everything is fully expanded and back in the input order. % The approach to finding the required point is iterative. We carry out % three phases. In phase one, we need all of the input co-ordinates % \begin{align*} % x_{1}' &= (1 - p)x_{1} + px_{2} \\ % y_{1}' &= (1 - p)y_{1} + py_{2} \\ % x_{2}' &= (1 - p)x_{2} + px_{3} \\ % y_{2}' &= (1 - p)y_{2} + py_{3} \\ % x_{3}' &= (1 - p)x_{3} + px_{4} \\ % y_{3}' &= (1 - p)y_{3} + py_{4} % \end{align*} % In the second stage, we can drop the final point % \begin{align*} % x_{1}'' &= (1 - p)x_{1}' + px_{2}' \\ % y_{1}'' &= (1 - p)y_{1}' + py_{2}' \\ % x_{2}'' &= (1 - p)x_{2}' + px_{3}' \\ % y_{2}'' &= (1 - p)y_{2}' + py_{3}' % \end{align*} % and for the final stage only need one set of calculations % \begin{align*} % P_{x} &= (1 - p)x_{1}'' + px_{2}'' \\ % P_{y} &= (1 - p)y_{1}'' + py_{2}'' % \end{align*} % Of course, this does mean a lot of calculations and expansion! % \begin{macrocode} \cs_new:Npn \@@_point_interpolate_curve_auxii:nnnnnnnnn #1#2#3#4#5#6#7#8#9 { \@@_point_interpolate_curve_auxiii:ennnnn { \fp_eval:n { 1 - #1 } } {#1} { {#2} {#3} } { {#4} {#5} } { {#6} {#7} } { {#8} {#9} } } \cs_generate_variant:Nn \@@_point_interpolate_curve_auxii:nnnnnnnnn { e } % \begin{macrocode} % We need to do the first cycle, but haven't got enough arguments to keep % everything in play at once. So here we use a bit of argument re-ordering % and a single auxiliary to get the job done. % \begin{macrocode} \cs_new:Npn \@@_point_interpolate_curve_auxiii:nnnnnn #1#2#3#4#5#6 { \@@_point_interpolate_curve_auxiv:nnnnnn {#1} {#2} #3 #4 \@@_point_interpolate_curve_auxiv:nnnnnn {#1} {#2} #4 #5 \@@_point_interpolate_curve_auxiv:nnnnnn {#1} {#2} #5 #6 \prg_do_nothing: \@@_point_interpolate_curve_auxvi:n { {#1} {#2} } } \cs_generate_variant:Nn \@@_point_interpolate_curve_auxiii:nnnnnn { e } \cs_new:Npn \@@_point_interpolate_curve_auxiv:nnnnnn #1#2#3#4#5#6 { \@@_point_interpolate_curve_auxv:eew { \fp_eval:n { #1 * #3 + #2 * #5 } } { \fp_eval:n { #1 * #4 + #2 * #6 } } } \cs_new:Npn \@@_point_interpolate_curve_auxv:nnw #1#2#3 \prg_do_nothing: #4#5 { #3 \prg_do_nothing: #4 { #5 {#1} {#2} } } \cs_generate_variant:Nn \@@_point_interpolate_curve_auxv:nnw { ee } % \begin{macrocode} % Get the arguments back into the right places and to the second and % third cycles directly. % \begin{macrocode} \cs_new:Npn \@@_point_interpolate_curve_auxvi:n #1 { \@@_point_interpolate_curve_auxvii:nnnnnnnn #1 } \cs_new:Npn \@@_point_interpolate_curve_auxvii:nnnnnnnn #1#2#3#4#5#6#7#8 { \@@_point_interpolate_curve_auxviii:eeeenn { \fp_eval:n { #1 * #5 + #2 * #3 } } { \fp_eval:n { #1 * #6 + #2 * #4 } } { \fp_eval:n { #1 * #7 + #2 * #5 } } { \fp_eval:n { #1 * #8 + #2 * #6 } } {#1} {#2} } \cs_new:Npn \@@_point_interpolate_curve_auxviii:nnnnnn #1#2#3#4#5#6 { \draw_point:n { #5 * #3 + #6 * #1 , #5 * #4 + #6 * #2 } } \cs_generate_variant:Nn \@@_point_interpolate_curve_auxviii:nnnnnn { eeee } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Vector support} % % As well as co-ordinates relative to the drawing % % \begin{variable} % { % \l_@@_xvec_x_dim, % \l_@@_xvec_y_dim, % \l_@@_yvec_x_dim, % \l_@@_yvec_y_dim, % \l_@@_zvec_x_dim, % \l_@@_zvec_y_dim % } % Base vectors to map to the underlying two-dimensional drawing space. % \begin{macrocode} \dim_new:N \l_@@_xvec_x_dim \dim_new:N \l_@@_xvec_y_dim \dim_new:N \l_@@_yvec_x_dim \dim_new:N \l_@@_yvec_y_dim \dim_new:N \l_@@_zvec_x_dim \dim_new:N \l_@@_zvec_y_dim % \end{macrocode} % \end{variable} % % \begin{macro}{\draw_xvec:n, \draw_yvec:n, \draw_zvec:n} % \begin{macro}{\@@_vec:nn} % \begin{macro}{\@@_vec:nnn} % Calculate the underlying position and store it. % \begin{macrocode} \cs_new_protected:Npn \draw_xvec:n #1 { \@@_vec:nn { x } {#1} } \cs_new_protected:Npn \draw_yvec:n #1 { \@@_vec:nn { y } {#1} } \cs_new_protected:Npn \draw_zvec:n #1 { \@@_vec:nn { z } {#1} } \cs_new_protected:Npn \@@_vec:nn #1#2 { \@@_point_process:nn { \@@_vec:nnn {#1} } {#2} } \cs_new_protected:Npn \@@_vec:nnn #1#2#3 { \dim_set:cn { l_@@_ #1 vec_x_dim } {#2} \dim_set:cn { l_@@_ #1 vec_y_dim } {#3} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % Initialise the vectors. % \begin{macrocode} \draw_xvec:n { 1cm , 0cm } \draw_yvec:n { 0cm , 1cm } \draw_zvec:n { -0.385cm , -0.385cm } % \end{macrocode} % % \begin{macro}[EXP]{\draw_point_vec:nn} % \begin{macro}[EXP]{\@@_point_vec:nn, \@@_point_vec:ee} % \begin{macro}[EXP]{\draw_point_vec:nnn} % \begin{macro}[EXP]{\@@_point_vec:nnn, \@@_point_vec:eee} % Force a single evaluation of each factor, then use these to work out the % underlying point. % \begin{macrocode} \cs_new:Npn \draw_point_vec:nn #1#2 { \@@_point_vec:ee { \fp_eval:n {#1} } { \fp_eval:n {#2} } } \cs_new:Npn \@@_point_vec:nn #1#2 { \draw_point:n { #1 * \l_@@_xvec_x_dim + #2 * \l_@@_yvec_x_dim , #1 * \l_@@_xvec_y_dim + #2 * \l_@@_yvec_y_dim } } \cs_generate_variant:Nn \@@_point_vec:nn { ee } \cs_new:Npn \draw_point_vec:nnn #1#2#3 { \@@_point_vec:eee { \fp_eval:n {#1} } { \fp_eval:n {#2} } { \fp_eval:n {#3} } } \cs_new:Npn \@@_point_vec:nnn #1#2#3 { \draw_point:n { #1 * \l_@@_xvec_x_dim + #2 * \l_@@_yvec_x_dim + #3 * \l_@@_zvec_x_dim , #1 * \l_@@_xvec_y_dim + #2 * \l_@@_yvec_y_dim + #3 * \l_@@_zvec_y_dim } } \cs_generate_variant:Nn \@@_point_vec:nnn { eee } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\draw_point_vec_polar:nn} % \begin{macro}[EXP]{\draw_point_vec_polar:nnn} % \begin{macro}[EXP]{\@@_point_vec_polar:nnn, \@@_point_vec_polar:enn} % Much the same as the core polar approach. % \begin{macrocode} \cs_new:Npn \draw_point_vec_polar:nn #1#2 { \draw_point_vec_polar:nnn {#1} {#1} {#2} } \cs_new:Npn \draw_point_vec_polar:nnn #1#2#3 { \@@_draw_vec_polar:enn { \fp_eval:n {#3} } {#1} {#2} } \cs_new:Npn \@@_draw_vec_polar:nnn #1#2#3 { \draw_point:n { cosd(#1) * (#2) * \l_@@_xvec_x_dim , sind(#1) * (#3) * \l_@@_yvec_y_dim } } \cs_generate_variant:Nn \@@_draw_vec_polar:nnn { e } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \subsection{Transformations} % % \begin{macro}[EXP]{\draw_point_transform:n} % \begin{macro}[EXP]{\@@_point_transform:nn} % Applies a transformation matrix to a point: see \texttt{l3draw-transforms} % for the business end. Where possible, we avoid the relatively expensive % multiplication step. % \begin{macrocode} \cs_new:Npn \draw_point_transform:n #1 { \@@_point_process:nn { \@@_point_transform:nn } {#1} } \cs_new:Npn \@@_point_transform:nn #1#2 { \bool_if:NTF \l_@@_matrix_active_bool { \draw_point:n { ( \l_@@_matrix_a_fp * #1 + \l_@@_matrix_c_fp * #2 + \l_@@_xshift_dim ) , ( \l_@@_matrix_b_fp * #1 + \l_@@_matrix_d_fp * #2 + \l_@@_yshift_dim ) } } { \draw_point:n { (#1, #2) + ( \l_@@_xshift_dim , \l_@@_yshift_dim ) } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[EXP]{\@@_point_transform_noshift:n} % \begin{macro}[EXP]{\@@_point_transform_noshift:nn} % A version with no shift: used for internal purposes. % \begin{macrocode} \cs_new:Npn \@@_point_transform_noshift:n #1 { \@@_point_process:nn { \@@_point_transform_noshift:nn } {#1} } \cs_new:Npn \@@_point_transform_noshift:nn #1#2 { \bool_if:NTF \l_@@_matrix_active_bool { \draw_point:n { ( \l_@@_matrix_a_fp * #1 + \l_@@_matrix_c_fp * #2 ) , ( \l_@@_matrix_b_fp * #1 + \l_@@_matrix_d_fp * #2 ) } } { \draw_point:n { (#1, #2) } } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex