summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3experimental/l3draw/l3draw-paths.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3experimental/l3draw/l3draw-paths.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3experimental/l3draw/l3draw-paths.dtx901
1 files changed, 901 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/l3experimental/l3draw/l3draw-paths.dtx b/Master/texmf-dist/source/latex/l3experimental/l3draw/l3draw-paths.dtx
new file mode 100644
index 00000000000..2fa882e7f1a
--- /dev/null
+++ b/Master/texmf-dist/source/latex/l3experimental/l3draw/l3draw-paths.dtx
@@ -0,0 +1,901 @@
+% \iffalse meta-comment
+%
+%% File: l3draw-paths.dtx Copyright(C) 2018 The LaTeX3 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}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3draw-paths} package\\ Drawing paths^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released 2018/02/21}
+%
+% \maketitle
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3draw-paths} implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<@@=draw>
+% \end{macrocode}
+%
+% This sub-module covers more-or-less the same ideas as
+% \texttt{pgfcorepathconstruct.code.tex}, though using the expandable FPU
+% means that the implementation often varies. At present, equivalents of the
+% following are currently absent:
+% \begin{itemize}
+% \item \cs{pgfpatharcto}, \cs{pgfpatharctoprecomputed}: These are
+% extremely specialised and are very complex in implementation. If the
+% functionality is required, it is likely that it will be set up from
+% scratch here.
+% \item \cs{pgfpathparabola}: Seems to be unused other than defining
+% a Ti\emph{k}Z interface, which itself is then not used further.
+% \item \cs{pgfpathsine}, \cs{pgfpathcosine}: Need to see exactly how
+% these need to work, in particular whether a wider input range is
+% needed and what approximation to make.
+% \item \cs{pgfpathcurvebetweentime}, \cs{pgfpathcurvebetweentimecontinue}:
+% These don't seem to be used at all.
+% \end{itemize}
+%
+% \begin{variable}
+% {\l_@@_path_tmp_tl, \l_@@_path_tmpa_fp, \l_@@_path_tmpb_fp}
+% Scratch space.
+% \begin{macrocode}
+\tl_new:N \l_@@_path_tmp_tl
+\fp_new:N \l_@@_path_tmpa_fp
+\fp_new:N \l_@@_path_tmpb_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Tracking paths}
+%
+% \begin{variable}{\g_@@_path_lastx_dim, \g_@@_path_lasty_dim}
+% The last point visited on a path.
+% \begin{macrocode}
+\dim_new:N \g_@@_path_lastx_dim
+\dim_new:N \g_@@_path_lasty_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \g_@@_path_xmax_dim,
+% \g_@@_path_xmin_dim,
+% \g_@@_path_ymax_dim,
+% \g_@@_path_ymin_dim
+% }
+% The limiting size of a path.
+% \begin{macrocode}
+\dim_new:N \g_@@_path_xmax_dim
+\dim_new:N \g_@@_path_xmin_dim
+\dim_new:N \g_@@_path_ymax_dim
+\dim_new:N \g_@@_path_ymin_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_path_update_limits:nn}
+% \begin{macro}{\@@_path_reset_limits:}
+% Track the limits of a path and (perhaps) of the picture as a whole.
+% (At present the latter is always true: that will change as more complex
+% functionality is added.)
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_path_update_limits:nn #1#2
+ {
+ \dim_gset:Nn \g_@@_path_xmax_dim
+ { \dim_max:nn \g_@@_path_xmax_dim {#1} }
+ \dim_gset:Nn \g_@@_path_xmin_dim
+ { \dim_min:nn \g_@@_path_xmin_dim {#1} }
+ \dim_gset:Nn \g_@@_path_ymax_dim
+ { \dim_max:nn \g_@@_path_ymax_dim {#2} }
+ \dim_gset:Nn \g_@@_path_ymin_dim
+ { \dim_min:nn \g_@@_path_ymin_dim {#2} }
+ \bool_if:NT \l_@@_update_bb_bool
+ {
+ \dim_gset:Nn \g_@@_xmax_dim
+ { \dim_max:nn \g_@@_xmax_dim {#1} }
+ \dim_gset:Nn \g_@@_xmin_dim
+ { \dim_min:nn \g_@@_xmin_dim {#1} }
+ \dim_gset:Nn \g_@@_ymax_dim
+ { \dim_max:nn \g_@@_ymax_dim {#2} }
+ \dim_gset:Nn \g_@@_ymin_dim
+ { \dim_min:nn \g_@@_ymin_dim {#2} }
+ }
+ }
+\cs_new_protected:Npn \@@_path_reset_limits:
+ {
+ \dim_gset:Nn \g_@@_path_xmax_dim { -\c_max_dim }
+ \dim_gset:Nn \g_@@_path_xmin_dim { \c_max_dim }
+ \dim_gset:Nn \g_@@_path_ymax_dim { -\c_max_dim }
+ \dim_gset:Nn \g_@@_path_ymin_dim { \c_max_dim }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_path_update_last:nn}
+% A simple auxiliary to avoid repetition.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_path_update_last:nn #1#2
+ {
+ \dim_gset:Nn \g_@@_path_lastx_dim {#1}
+ \dim_gset:Nn \g_@@_path_lasty_dim {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Corner arcs}
+%
+% At the level of path \emph{construction}, rounded corners are handled
+% by inserting a marker into the path: that is then picked up once the
+% full path is constructed. Thus we need to set up the appropriate
+% data structures here, such that this can be applied every time it is
+% relevant.
+%
+% \begin{variable}{\l_@@_corner_xarc_dim, \l_@@_corner_yarc_dim}
+% The two arcs in use.
+% \begin{macrocode}
+\dim_new:N \l_@@_corner_xarc_dim
+\dim_new:N \l_@@_corner_yarc_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_corner_arc_bool}
+% A flag to speed up the repeated checks.
+% \begin{macrocode}
+\bool_new:N \l_@@_corner_arc_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\draw_path_corner_arc:n}
+% \begin{macro}{\@@_path_corner_arc:nn}
+% Calculate the arcs, check they are non-zero.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_corner_arc:n #1
+ {
+ \@@_point_process:nn { \@@_path_corner_arc:nn } {#1}
+ }
+\cs_new_protected:Npn \@@_path_corner_arc:nn #1#2
+ {
+ \dim_set:Nn \l_@@_corner_xarc_dim {#1}
+ \dim_set:Nn \l_@@_corner_yarc_dim {#2}
+ \bool_lazy_and:nnTF
+ { \dim_compare_p:nNn \l_@@_corner_xarc_dim = { 0pt } }
+ { \dim_compare_p:nNn \l_@@_corner_yarc_dim = { 0pt } }
+ { \bool_set_false:N \l_@@_corner_arc_bool }
+ { \bool_set_true:N \l_@@_corner_arc_bool }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_path_mark_corner:}
+% Mark up corners for arc post-processing.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_path_mark_corner:
+ {
+ \bool_if:NT \l_@@_corner_arc_bool
+ {
+ \@@_softpath_roundpoint:VV
+ \l_@@_corner_xarc_dim
+ \l_@@_corner_yarc_dim
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Basic path constructions}
+%
+% \begin{macro}{\draw_path_moveto:n, \draw_path_lineto:n}
+% \begin{macro}{\@@_path_moveto:nn, \@@_path_lineto:nn}
+% \begin{macro}{\draw_path_curveto:nnn}
+% \begin{macro}{\@@_path_curveto:nnnnnn}
+% At present, stick to purely linear transformation support and skip the
+% soft path business: that will likely need to be revisited later.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_moveto:n #1
+ {
+ \@@_point_process:nn
+ { \@@_path_moveto:nn }
+ { \draw_point_transform:n {#1} }
+ }
+\cs_new_protected:Npn \@@_path_moveto:nn #1#2
+ {
+ \@@_path_update_limits:nn {#1} {#2}
+ \@@_softpath_moveto:nn {#1} {#2}
+ \@@_path_update_last:nn {#1} {#2}
+ }
+\cs_new_protected:Npn \draw_path_lineto:n #1
+ {
+ \@@_point_process:nn
+ { \@@_path_lineto:nn }
+ { \draw_point_transform:n {#1} }
+ }
+\cs_new_protected:Npn \@@_path_lineto:nn #1#2
+ {
+ \@@_path_mark_corner:
+ \@@_path_update_limits:nn {#1} {#2}
+ \@@_softpath_lineto:nn {#1} {#2}
+ \@@_path_update_last:nn {#1} {#2}
+ }
+\cs_new_protected:Npn \draw_path_curveto:nnn #1#2#3
+ {
+ \@@_point_process:nnn
+ {
+ \@@_point_process:nn
+ {
+ \@@_path_mark_corner:
+ \@@_path_curveto:nnnnnn
+ }
+ { \draw_point_transform:n {#1} }
+ }
+ { \draw_point_transform:n {#2} }
+ { \draw_point_transform:n {#3} }
+ }
+\cs_new_protected:Npn \@@_path_curveto:nnnnnn #1#2#3#4#5#6
+ {
+ \@@_path_update_limits:nn {#1} {#2}
+ \@@_path_update_limits:nn {#3} {#4}
+ \@@_path_update_limits:nn {#5} {#6}
+ \@@_softpath_curveto:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6}
+ \@@_path_update_last:nn {#5} {#6}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\draw_path_close:}
+% A simple wrapper.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_close:
+ {
+ \@@_path_mark_corner:
+ \@@_softpath_closepath:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Computed curves}
+%
+% More complex operations need some calculations. To assist with those, various
+% constants are pre-defined.
+%
+% \begin{macro}{\draw_path_curveto:nn}
+% \begin{macro}{\@@_path_curveto:nnnn}
+% \begin{variable}{\c_@@_path_curveto_a_fp, \c_@@_path_curveto_b_fp}
+% A quadratic curve with one control point $(x_{\mathrm{c}},
+% y_{\mathrm{c}})$. The two required control points are then
+% \[
+% x_{1} = \frac{1}{3}x_{\mathrm{s}} + \frac{2}{3}x_{\mathrm{c}}
+% \quad
+% y_{1} = \frac{1}{3}y_{\mathrm{s}} + \frac{2}{3}y_{\mathrm{c}}
+% \]
+% and
+% \[
+% x_{2} = \frac{1}{3}x_{\mathrm{e}} + \frac{2}{3}x_{\mathrm{c}}
+% \quad
+% x_{2} = \frac{1}{3}y_{\mathrm{e}} + \frac{2}{3}y_{\mathrm{c}}
+% \]
+% using the start (last) point $(x_{\mathrm{s}}, y_{\mathrm{s}})$
+% and the end point $(x_{\mathrm{s}}, y_{\mathrm{s}})$.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_curveto:nn #1#2
+ {
+ \@@_point_process:nnn
+ { \@@_path_curveto:nnnn }
+ { \draw_point_transform:n {#1} }
+ { \draw_point_transform:n {#2} }
+ }
+\cs_new_protected:Npn \@@_path_curveto:nnnn #1#2#3#4
+ {
+ \fp_set:Nn \l_@@_path_tmpa_fp { \c_@@_path_curveto_b_fp * #1 }
+ \fp_set:Nn \l_@@_path_tmpb_fp { \c_@@_path_curveto_b_fp * #2 }
+ \use:x
+ {
+ \@@_path_mark_corner:
+ \@@_path_curveto:nnnnnn
+ {
+ \fp_to_dim:n
+ {
+ \c_@@_path_curveto_a_fp * \g_@@_path_lastx_dim
+ + \l_@@_path_tmpa_fp
+ }
+ }
+ {
+ \fp_to_dim:n
+ {
+ \c_@@_path_curveto_a_fp * \g_@@_path_lasty_dim
+ + \l_@@_path_tmpb_fp
+ }
+ }
+ {
+ \fp_to_dim:n
+ { \c_@@_path_curveto_a_fp * #3 + \l_@@_path_tmpa_fp }
+ }
+ {
+ \fp_to_dim:n
+ { \c_@@_path_curveto_a_fp * #4 + \l_@@_path_tmpb_fp }
+ }
+ {#3}
+ {#4}
+ }
+ }
+\fp_const:Nn \c_@@_path_curveto_a_fp { 1 / 3 }
+\fp_const:Nn \c_@@_path_curveto_b_fp { 2 / 3 }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\draw_path_arc:nnn}
+% \begin{macro}{\draw_path_arc:nnnn}
+% \begin{macro}{\@@_path_arc:nnnn}
+% \begin{macro}{\@@_path_arc:nnNnn}
+% \begin{macro}
+% {
+% \@@_path_arc_auxi:nnnnNnn,
+% \@@_path_arc_auxi:fnnnNnn,
+% \@@_path_arc_auxi:fnfnNnn
+% }
+% \begin{macro}{\@@_path_arc_auxii:nnnNnnnn}
+% \begin{macro}{\@@_path_arc_auxiii:nn}
+% \begin{macro}{\@@_path_arc_auxiv:nnnn}
+% \begin{macro}{\@@_path_arc_auxv:nn, \@@_path_arc_auxvi:nn}
+% \begin{macro}{\@@_path_arc_add:nnnn}
+% \begin{variable}{\l_@@_path_arc_delta_fp, \l_@@_path_arc_start_fp}
+% \begin{variable}{\c_@@_path_arc_90_fp,\c_@@_path_arc_60_fp}
+% Drawing an arc means dividing the total curve required into sections:
+% using Bézier curves we can cover at most $90^{\circ}$ at once. To allow
+% for later manipulations, we aim to have roughly equal last segments to
+% the line, with the split set at a final part of $115^{\circ}$.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_arc:nnn #1#2#3
+ { \draw_path_arc:nnnn {#1} {#2} {#3} {#3} }
+\cs_new_protected:Npn \draw_path_arc:nnnn #1#2#3#4
+ {
+ \use:x
+ {
+ \@@_path_arc:nnnn
+ { \fp_eval:n {#1} }
+ { \fp_eval:n {#2} }
+ { \fp_to_dim:n {#3} }
+ { \fp_to_dim:n {#4} }
+ }
+ }
+\cs_new_protected:Npn \@@_path_arc:nnnn #1#2#3#4
+ {
+ \fp_compare:nNnTF {#1} > {#2}
+ { \@@_path_arc:nnNnn {#1} {#2} - {#3} {#4} }
+ { \@@_path_arc:nnNnn {#1} {#2} + {#3} {#4} }
+ }
+\cs_new_protected:Npn \@@_path_arc:nnNnn #1#2#3#4#5
+ {
+ \fp_set:Nn \l_@@_path_arc_start_fp {#1}
+ \fp_set:Nn \l_@@_path_arc_delta_fp { abs( #1 - #2 ) }
+ \fp_while_do:nNnn { \l_@@_path_arc_delta_fp } > { 90 }
+ {
+ \fp_compare:nNnTF \l_@@_path_arc_delta_fp > { 115 }
+ {
+ \@@_path_arc_auxi:ffnnNnn
+ { \fp_to_decimal:N \l_@@_path_arc_start_fp }
+ { \fp_eval:n { \l_@@_path_arc_start_fp #3 90 } }
+ { 90 } {#2}
+ #3 {#4} {#5}
+ }
+ {
+ \@@_path_arc_auxi:ffnnNnn
+ { \fp_to_decimal:N \l_@@_path_arc_start_fp }
+ { \fp_eval:n { \l_@@_path_arc_start_fp #3 60 } }
+ { 60 } {#2}
+ #3 {#4} {#5}
+ }
+ }
+ \@@_path_mark_corner:
+ \@@_path_arc_auxi:fnfnNnn
+ { \fp_to_decimal:N \l_@@_path_arc_start_fp }
+ {#2}
+ { \fp_eval:n { abs( \l_@@_path_arc_start_fp - #2 ) } }
+ {#2}
+ #3 {#4} {#5}
+ }
+% \end{macrocode}
+% The auxiliary is responsible for calculating the required points.
+% The \enquote{magic} number required to determine the length of the
+% control vectors is well-established for a right-angle:
+% $\frac{4}{3}(\sqrt{2} - 1) = 0.552\,284\,75$. For other cases, we follow
+% the calculation used by \pkg{pgf} but with the second common case of
+% $60^{\circ}$ pre-calculated for speed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_path_arc_auxi:nnnnNnn #1#2#3#4#5#6#7
+ {
+ \use:x
+ {
+ \@@_path_arc_auxii:nnnNnnnn
+ {#1} {#2} {#4} #5 {#6} {#7}
+ {
+ \fp_to_dim:n
+ {
+ \cs_if_exist_use:cF
+ { c_@@_path_arc_ #3 _fp }
+ { 4/3 * tand( 0.25 * #3 ) }
+ * #6
+ }
+ }
+ {
+ \fp_to_dim:n
+ {
+ \cs_if_exist_use:cF
+ { c_@@_path_arc_ #3 _fp }
+ { 4/3 * tand( 0.25 * #3 ) }
+ * #7
+ }
+ }
+ }
+ }
+\cs_generate_variant:Nn \@@_path_arc_auxi:nnnnNnn { fnf , ff }
+% \end{macrocode}
+% We can now calculate the required points. As everything here is
+% non-expandable, that is best done by using \texttt{x}-type expansion
+% to build up the tokens. The three points are calculated out-of-order,
+% since finding the second control point needs the position of the end
+% point. Once the points are found, fire-off the fundamental path
+% operation and update the record of where we are up to. The final
+% point has to be
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_path_arc_auxii:nnnNnnnn #1#2#3#4#5#6#7#8
+ {
+ \tl_clear:N \l_@@_path_tmp_tl
+ \@@_point_process:nn
+ { \@@_path_arc_auxiii:nn }
+ {
+ \@@_point_transform_noshift:n
+ { \draw_point_polar:nnn { #1 #4 90 } {#7} {#8} }
+ }
+ \@@_point_process:nn
+ {
+ \@@_point_process:nn
+ { \@@_path_arc_auxiv:nnnn }
+ {
+ \draw_point_transform:n
+ { \draw_point_polar:nnn {#1} {#5} {#6} }
+ }
+ }
+ {
+ \draw_point_transform:n
+ { \draw_point_polar:nnn {#2} {#5} {#6} }
+ }
+ \@@_point_process:nn
+ { \@@_path_arc_auxv:nn }
+ {
+ \@@_point_transform_noshift:n
+ { \draw_point_polar:nnn { #2 #4 -90 } {#7} {#8} }
+ }
+ \exp_after:wN \@@_path_curveto:nnnnnn \l_@@_path_tmp_tl
+ \fp_set:Nn \l_@@_path_arc_delta_fp { abs ( #2 - #3 ) }
+ \fp_set:Nn \l_@@_path_arc_start_fp {#2}
+ }
+% \end{macrocode}
+% The first control point.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_path_arc_auxiii:nn #1#2
+ {
+ \@@_path_arc_aux_add:nn
+ { \g_@@_path_lastx_dim + #1 }
+ { \g_@@_path_lasty_dim + #2 }
+ }
+% \end{macrocode}
+% The end point: simple arithmetic.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_path_arc_auxiv:nnnn #1#2#3#4
+ {
+ \@@_path_arc_aux_add:nn
+ { \g_@@_path_lastx_dim - #1 + #3 }
+ { \g_@@_path_lasty_dim - #2 + #4 }
+ }
+% \end{macrocode}
+% The second control point: extract the last point, do some
+% rearrangement and record.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_path_arc_auxv:nn #1#2
+ {
+ \exp_after:wN \@@_path_arc_auxvi:nn
+ \l_@@_path_tmp_tl {#1} {#2}
+ }
+\cs_new_protected:Npn \@@_path_arc_auxvi:nn #1#2#3#4#5#6
+ {
+ \tl_set:Nn \l_@@_path_tmp_tl { {#1} {#2} }
+ \@@_path_arc_aux_add:nn
+ { #5 + #3 }
+ { #6 + #4 }
+ \tl_put_right:Nn \l_@@_path_tmp_tl { {#3} {#4} }
+ }
+\cs_new_protected:Npn \@@_path_arc_aux_add:nn #1#2
+ {
+ \tl_put_right:Nx \l_@@_path_tmp_tl
+ { { \fp_to_dim:n {#1} } { \fp_to_dim:n {#2} } }
+ }
+\fp_new:N \l_@@_path_arc_delta_fp
+\fp_new:N \l_@@_path_arc_start_fp
+\fp_const:cn { c_@@_path_arc_90_fp } { 4/3 * (sqrt(2) - 1) }
+\fp_const:cn { c_@@_path_arc_60_fp } { 4/3 * tand(15) }
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\draw_path_arc_axes:nnnn}
+% A simple wrapper.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_arc_axes:nnnn #1#2#3#4
+ {
+ \draw_transform_triangle:nnn { 0cm , 0cm } {#3} {#4}
+ \draw_path_arc:nnn {#1} {#2} { 1pt }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\draw_path_ellipse:nnn}
+% \begin{macro}{\@@_path_ellipse:nnnnnn}
+% \begin{macro}[EXP]
+% {
+% \@@_path_ellipse_arci:nnnnnn ,
+% \@@_path_ellipse_arcii:nnnnnn ,
+% \@@_path_ellipse_arciii:nnnnnn ,
+% \@@_path_ellipse_arciv:nnnnnn
+% }
+% \begin{variable}{\c_@@_path_ellipse_fp}
+% Drawing an ellipse is an optimised version of drawing an arc, in particular
+% reusing the same constant. We need to deal with the ellipse in four parts
+% and also deal with moving to the right place, closing it and ending up
+% back at the center. That is handled on a per-arc basis, each in a
+% separate auxiliary for readability.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_ellipse:nnn #1#2#3
+ {
+ \@@_point_process:nnn
+ {
+ \@@_point_process:nn
+ { \@@_path_ellipse:nnnnnn }
+ { \draw_point_transform:n {#1} }
+ }
+ { \@@_point_transform_noshift:n {#2} }
+ { \@@_point_transform_noshift:n {#3} }
+ }
+\cs_new_protected:Npn \@@_path_ellipse:nnnnnn #1#2#3#4#5#6
+ {
+ \use:x
+ {
+ \@@_path_moveto:nn
+ { \fp_to_dim:n { #1 + #3 } } { \fp_to_dim:n { #2 + #4 } }
+ \@@_path_ellipse_arci:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6}
+ \@@_path_ellipse_arcii:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6}
+ \@@_path_ellipse_arciii:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6}
+ \@@_path_ellipse_arciv:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6}
+ }
+ \@@_softpath_closepath:
+ \@@_path_moveto:nn {#1} {#2}
+ }
+\cs_new:Npn \@@_path_ellipse_arci:nnnnnn #1#2#3#4#5#6
+ {
+ \@@_path_curveto:nnnnnn
+ { \fp_to_dim:n { #1 + #3 + #5 * \c_@@_path_ellipse_fp } }
+ { \fp_to_dim:n { #2 + #4 + #6 * \c_@@_path_ellipse_fp } }
+ { \fp_to_dim:n { #1 + #3 * \c_@@_path_ellipse_fp + #5 } }
+ { \fp_to_dim:n { #2 + #4 * \c_@@_path_ellipse_fp + #6 } }
+ { \fp_to_dim:n { #1 + #5 } }
+ { \fp_to_dim:n { #2 + #6 } }
+ }
+\cs_new:Npn \@@_path_ellipse_arcii:nnnnnn #1#2#3#4#5#6
+ {
+ \@@_path_curveto:nnnnnn
+ { \fp_to_dim:n { #1 - #3 * \c_@@_path_ellipse_fp + #5 } }
+ { \fp_to_dim:n { #2 - #4 * \c_@@_path_ellipse_fp + #6 } }
+ { \fp_to_dim:n { #1 - #3 + #5 * \c_@@_path_ellipse_fp } }
+ { \fp_to_dim:n { #2 - #4 + #6 * \c_@@_path_ellipse_fp } }
+ { \fp_to_dim:n { #1 - #3 } }
+ { \fp_to_dim:n { #2 - #4 } }
+ }
+\cs_new:Npn \@@_path_ellipse_arciii:nnnnnn #1#2#3#4#5#6
+ {
+ \@@_path_curveto:nnnnnn
+ { \fp_to_dim:n { #1 - #3 - #5 * \c_@@_path_ellipse_fp } }
+ { \fp_to_dim:n { #2 - #4 - #6 * \c_@@_path_ellipse_fp } }
+ { \fp_to_dim:n { #1 - #3 * \c_@@_path_ellipse_fp - #5 } }
+ { \fp_to_dim:n { #2 - #4 * \c_@@_path_ellipse_fp - #6 } }
+ { \fp_to_dim:n { #1 - #5 } }
+ { \fp_to_dim:n { #2 - #6 } }
+ }
+\cs_new:Npn \@@_path_ellipse_arciv:nnnnnn #1#2#3#4#5#6
+ {
+ \@@_path_curveto:nnnnnn
+ { \fp_to_dim:n { #1 + #3 * \c_@@_path_ellipse_fp - #5 } }
+ { \fp_to_dim:n { #2 + #4 * \c_@@_path_ellipse_fp - #6 } }
+ { \fp_to_dim:n { #1 + #3 - #5 * \c_@@_path_ellipse_fp } }
+ { \fp_to_dim:n { #2 + #4 - #6 * \c_@@_path_ellipse_fp } }
+ { \fp_to_dim:n { #1 + #3 } }
+ { \fp_to_dim:n { #2 + #4 } }
+ }
+\fp_const:Nn \c_@@_path_ellipse_fp { \fp_use:c { c_@@_path_arc_90_fp } }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\draw_path_circle:nn}
+% A shortcut.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_circle:nn #1#2
+ { \draw_path_ellipse:nnn {#1} { #2 , 0pt } { 0pt , #2 } }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Rectangles}
+%
+% \begin{macro}{\draw_path_rectangle:nn}
+% \begin{macro}{\@@_path_rectangle:nnnn, \@@_path_rectangle_rounded:nnnn}
+% Building a rectangle can be a single operation, or for rounded versions will
+% involve step-by-step construction.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_rectangle:nn #1#2
+ {
+ \@@_point_process:nnn
+ {
+ \bool_if:NTF \l_@@_corner_arc_bool
+ { \@@_path_rectangle_rounded:nnnn }
+ { \@@_path_rectangle:nnnn }
+ }
+ { \draw_point_transform:n {#1} }
+ {#2}
+ }
+\cs_new_protected:Npn \@@_path_rectangle:nnnn #1#2#3#4
+ {
+ \@@_path_update_limits:nn {#1} {#2}
+ \@@_path_update_limits:nn { #1 + #3 } { #2 + #4 }
+ \@@_softpath_rectangle:nnnn {#1} {#2} {#3} {#4}
+ \@@_path_update_last:nn {#1} {#2}
+ }
+\cs_new_protected:Npn \@@_path_rectangle_rounded:nnnn #1#2#3#4
+ {
+ \draw_path_moveto:n { #1 + #3 , #2 + #4 }
+ \draw_path_lineto:n { #1 , #2 + #4 }
+ \draw_path_lineto:n { #1 , #2 }
+ \draw_path_lineto:n { #1 + #3 , #2 }
+ \draw_path_close:
+ \draw_path_moveto:n { #1 , #2 }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\draw_path_rectangle_corners:nn}
+% \begin{macro}{\@@_path_rectangle_corners:nnnn}
+% Another shortcut wrapper.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_rectangle_corners:nn #1#2
+ {
+ \@@_point_process:nnn
+ { \@@_path_rectangle_corners:nnnnn {#1} }
+ {#1} {#2}
+ }
+\cs_new_protected:Npn \@@_path_rectangle_corners:nnnnn #1#2#3#4#5
+ { \draw_path_rectangle:nn {#1} { #4 - #2 , #5 - #3 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Grids}
+%
+% \begin{macro}{\draw_path_grid:nnnn}
+% \begin{macro}{\@@_path_grid:nnnnnn}
+% A simple set of loops.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_grid:nnnn #1#2#3#4
+ {
+ \@@_point_process:nnn
+ { \@@_path_grid:nnnnnn {#1} {#2} }
+ {#3} {#4}
+ }
+\cs_new_protected:Npn \@@_path_grid:nnnnnn #1#2#3#4#5#6
+ {
+ \dim_step_inline:nnnn
+ {#3} { \dim_compare:nNnF {#3} < {#5} { - } \dim_abs:n {#1} } {#5}
+ {
+ \draw_path_moveto:n { ##1 , #4 }
+ \draw_path_lineto:n { ##1 , #6 }
+ }
+ \dim_step_inline:nnnn
+ {#4} { \dim_compare:nNnF {#4} < {#6} { - } \dim_abs:n {#2} } {#6}
+ {
+ \draw_path_moveto:n { #3 , ##1 }
+ \draw_path_lineto:n { #5 , ##1 }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Using paths}
+%
+% \begin{variable}
+% {
+% \l_@@_path_use_clip_bool ,
+% \l_@@_path_use_fill_bool ,
+% \l_@@_path_use_stroke_bool
+% }
+% Actions to pass to the driver.
+% \begin{macrocode}
+\bool_new:N \l_@@_path_use_clip_bool
+\bool_new:N \l_@@_path_use_fill_bool
+\bool_new:N \l_@@_path_use_stroke_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_path_use_bb_bool, \l_@@_path_use_clear_bool}
+% Actions handled at the macro layer.
+% \begin{macrocode}
+\bool_new:N \l_@@_path_use_bb_bool
+\bool_new:N \l_@@_path_use_clear_bool
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\draw_path_use:n, \draw_path_use_clear:n}
+% \begin{macro}{\@@_path_use:n}
+% \begin{macro}{\@@_path_use_action_draw:}
+% \begin{macro}{\@@_path_use_stroke_bb:}
+% \begin{macro}{\@@_path_use_stroke_bb_aux:NnN}
+% There are a range of actions which can apply to a path: they are handled
+% in a single function which can carry out several of them. The first step
+% is to deal with the special case of clearing the path.
+% \begin{macrocode}
+\cs_new_protected:Npn \draw_path_use:n #1
+ {
+ \tl_if_blank:nF {#1}
+ { \@@_path_use:n {#1} }
+ }
+\cs_new_protected:Npn \draw_path_use_clear:n #1
+ {
+ \bool_lazy_or:nnTF
+ { \tl_if_blank_p:n {#1} }
+ { \str_if_eq_p:nn {#1} { clear } }
+ {
+ \@@_softpath_clear:
+ \@@_path_reset_limits:
+ }
+ { \@@_path_use:n { #1 , clear } }
+ }
+% \end{macrocode}
+% Map over the actions and set up the data: mainly just booleans,
+% but with the possibility to cover more complex cases. The business end
+% of the function is a series of checks on the various flags, then
+% taking the appropriate action(s).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_path_use:n #1
+ {
+ \bool_set_false:N \l_@@_path_use_clip_bool
+ \bool_set_false:N \l_@@_path_use_fill_bool
+ \bool_set_false:N \l_@@_path_use_stroke_bool
+ \clist_map_inline:nn {#1}
+ {
+ \cs_if_exist:cTF { l_@@_path_use_ ##1 _ bool }
+ { \bool_set_true:c { l_@@_path_use_ ##1 _ bool } }
+ {
+ \cs_if_exist_use:cF { @@_path_use_action_ ##1 : }
+ { \ERROR }
+ }
+ }
+ \bool_lazy_and:nnT
+ { \l_@@_update_bb_bool }
+ { \l_@@_path_use_stroke_bool }
+ { \@@_path_use_stroke_bb: }
+ \bool_if:NTF \l_@@_path_use_clear_bool
+ { \@@_softpath_use_clear: }
+ { \@@_softpath_use: }
+ \bool_if:NT \l_@@_path_use_clip_bool
+ { \driver_draw_clip: }
+ \bool_lazy_or:nnT
+ { \l_@@_path_use_fill_bool }
+ { \l_@@_path_use_stroke_bool }
+ {
+ \use:c
+ {
+ driver_draw_
+ \bool_if:NT \l_@@_path_use_fill_bool { fill }
+ \bool_if:NT \l_@@_path_use_stroke_bool { stroke }
+ :
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_path_use_action_draw:
+ {
+ \bool_set_true:N \l_@@_path_use_stroke_bool
+ }
+% \end{macrocode}
+% Where the path is relevant to size and is stroked, we need to allow for
+% the part which overlaps the edge of the bounding box.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_path_use_stroke_bb:
+ {
+ \@@_path_use_stroke_bb_aux:NnN x { max } +
+ \@@_path_use_stroke_bb_aux:NnN y { max } +
+ \@@_path_use_stroke_bb_aux:NnN x { min } -
+ \@@_path_use_stroke_bb_aux:NnN y { min } -
+ }
+\cs_new_protected:Npn \@@_path_use_stroke_bb_aux:NnN #1#2#3
+ {
+ \dim_compare:nNnF { \dim_use:c { g_@@_ #1#2 _dim } } = { #3 -\c_max_dim }
+ {
+ \dim_gset:cn { g_@@_ #1#2 _dim }
+ {
+ \use:c { dim_ #2 :nn }
+ { \dim_use:c { g_@@_ #1#2 _dim } }
+ {
+ \dim_use:c { g_@@_path_ #1#2 _dim }
+ #3 0.5 \g_@@_linewidth_dim
+ }
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex