diff options
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.dtx | 901 |
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 |