% \iffalse meta-comment % %% File: l3draw.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|package> \RequirePackage{expl3} % %<*driver> \documentclass[full]{l3doc} \usepackage{l3draw} % For creating code demonstrations % This needs access to some code-level interfaces in listings \usepackage{listings} \makeatletter \lst@RequireAspects{writefile} \newsavebox\demo@box \lstnewenvironment{demo}[1][code and example] {% \global\let\lst@intname\@empty \edef\demo@end{% \expandafter\noexpand\csname demo@@#1@end\endcsname }% \@nameuse{demo@@#1}% } {\demo@end} \newcommand\demo@new[3]{% \@namedef{demo@@#1}{#2}% \@namedef{demo@@#1@end}{#3}% } \newcommand*\demo@common{% \setkeys{lst} {% basicstyle = \small\ttfamily, basewidth = 0.51em, gobble = 5, language = [LaTeX]{TeX}, }% } \newcommand*\demo@input{% \ExplSyntaxOn \catcode`\^^M = 10\relax \catcode`\% = 14\relax \input{\jobname.tmp}% \ExplSyntaxOff } \demo@new{code and example}{% \setbox\demo@box=\hbox\bgroup \lst@BeginAlsoWriteFile{\jobname.tmp}% \demo@common }{% \lst@EndWriteFile \egroup \begin{center} \ifdim\wd\demo@box > 0.75\linewidth \begin{minipage}{\linewidth} \usebox\demo@box \end{minipage}% \par \begin{minipage}{\linewidth} \demo@input \end{minipage} \else \begin{minipage}{0.25\linewidth} \demo@input \end{minipage}% \hfil \begin{minipage}{0.75\linewidth} \usebox\demo@box \end{minipage}% \fi \end{center} } \makeatother \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3draw} package\\ Core drawing support^^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{documentation} % % \section{\pkg{l3draw} documentation} % % The \pkg{l3draw} package provides a set of tools for creating (vector) % drawings in \pkg{expl3}. It is heavily inspired by the \pkg{pgf} layer of % the Ti\textit{k}Z system, with many of the interfaces having the same form. % However, the code provided here is build entirely on core \pkg{expl3} ideas % and uses the \LaTeX3 FPU for numerical support. % % Numerical expressions in \pkg{l3draw} are handled as floating point % expressions, unless otherwise noted. This means that they may contain or % omit explicit units. Where units are omitted, they will automatically be % taken as given in (\TeX{}) points. % % The code here is \emph{highly} experimental. % % \subsection{Drawings} % % \begin{function}{\draw_begin:, \draw_end:} % \begin{syntax} % \cs{draw_begin:} % ... % \cs{draw_end:} % \end{syntax} % Each drawing should be created within a \cs{draw_begin:}/\cs{draw_end:} % function pair. The \texttt{begin} function sets up a number of key % data structures for the rest of the functions here: unless otherwise % specified, use of |\draw_...| functions outside of this % \enquote{environment} is \emph{not supported}. % % The drawing created within the environment will be inserted into % the typesetting stream by the \cs{draw_end:} function, which will % switch out of vertical mode if required. % \begin{demo} % \dim_new:N \l_mypos_dim % \draw_begin: % \draw_path_moveto:n { 0cm , \l_mypos_dim } % \draw_path_lineto:n { 1cm , \l_mypos_dim } % \dim_set:Nn \l_mypos_dim { 1cm } % \draw_path_lineto:n { 1cm , \l_mypos_dim } % \draw_path_close: % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % % Within a drawing, the only commands that should appear are those directly % aimed at drawing (from \pkg{l3draw}) and those which produce \emph{no} % typeset output. For example, it is possible to include loops inside a % drawing using |\int_step_function:nnnn| or similar. On the other hand, % text should not be included directly in drawings, but should rather be % inserted using the appropriate \pkg{l3draw} command. % % The drawing environment sets the following standard behaviors % \begin{itemize} % \item Non-zero rule for fill overlaps % \item Butt caps for lines % \item Mitering for line joins with a miter factor of $10$ % \item Solid line strokes % \end{itemize} % \end{function} % % Within a drawing, there are different ways of referring to a position. % The co-ordinates of a point are given relative to the current % \emph{drawing axes}. These can be manipulated and tracked at the code % level. Underlying this is the \meta{canvas}, which is at the \pkg{draw} % level essentially fixed and in line with the paper. Initially, the two % sets of axes are coincident. (It is possible to manipulate the canvas % axes at the driver level: this is then \enquote{transparent} to the % \pkg{draw} level, and so should be used only when strictly required.) % % The bounding box of the drawing is determined by tracking the size of the % \pkg{draw} commands between the start and end. The bounding box is (roughly) % the smallest box that contains all of the co-ordinates mentioned within the % drawing. This can include those automatically generated, for example the % supporting points needed to construct an arc. % % \begin{function}{\draw_baseline:n} % \begin{syntax} % \cs{draw_baseline:n} \Arg{length} % \end{syntax} % As standard, the baseline of the bounding box of a drawing is calculated % automatically at the bottom of the drawing. It is possible to adjust this % using the \cs{draw_baseline:n} function. If the drawing co-ordinates lead % to lower $y$-axis values than the \meta{length}, then the drawing will have % a depth as well as a height. % \begin{demo} % text % \draw_begin: % \draw_path_rectangle:nn { 0 , 0 } { 2ex , 1ex } % \draw_path_use:n { stroke } % \draw_end: % text % \draw_begin: % \draw_path_rectangle:nn { 0 , 1ex } { 2ex , 1ex } % \draw_baseline:n { 0pt } % \draw_path_use:n { stroke } % \draw_end: % text % \draw_begin: % \draw_path_rectangle:nn { 0 , -1ex } { 2ex , 1ex } % \draw_baseline:n { -0.5ex } % \draw_path_use:n { stroke } % \draw_end: % text % \end{demo} % \end{function} % % \begin{function}{\draw_suspend_begin:, \draw_suspend_end:} % \begin{syntax} % \cs{draw_suspend_begin:} % ... % \cs{draw_suspend_end:} % \end{syntax} % Suspends all of the drawing mechanisms to allow \enquote{normal} % material to be created. Typically, this environment will be applied % inside a box which may contain nested pictures. % \begin{demo} % \draw_begin: % \draw_path_moveto:n { 0cm , 0cm } % \hbox_set:Nn \l_tmpa_box % { % \draw_suspend_begin: % This~is~normal~text. % \draw_begin: % A subpicture % \draw_path_moveto:n { 1cm , 0cm } % \draw_path_lineto:n { 1cm , 1cm } % \draw_path_use_clear:n { stroke } % \draw_end: % More~text. % \draw_suspend_end: % } % \draw_box_use:N \l_tmpa_box % \draw_path_lineto:n { 0cm , 1cm } % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % \end{function} % % \subsection{Graphics state} % % Within the drawing environment, a number of functions control how drawings % will appear. Note that these all apply \emph{globally}, though some are % rest at the start of each drawing (\cs{draw_begin:}). % % \begin{variable}{\l_draw_default_linewidth_dim} % The default value of the linewidth for stokes, set at the start % of every drawing (\cs{draw_begin:}). % \end{variable} % % \begin{function}{\draw_linewidth:n} % \begin{syntax} % \cs{draw_linewidth:n} \Arg{width} % \end{syntax} % Sets the width to be used for stroking to the \meta{width} (an % \meta{fp expr}). % \end{function} % % \begin{function}{\draw_dash_pattern:nn} % \begin{syntax} % \cs{draw_dash_pattern:nn} \Arg{pattern} \Arg{phase} % \end{syntax} % Specifies a dash pattern. The \meta{pattern} itself is a comma-separated % list of entries which represent the \enquote{on} and \enquote{off} % parts of the line. These are all \meta{fp expr} and repeat as required. % Thus the \meta{pattern} may be of arbitrary length. The \meta{phase} % specifies where during the first \enquote{on} line the pattern should % start. % \begin{demo} % \draw_begin: % \draw_dash_pattern:nn % { 0.5cm , 0.5cm , 0.1cm , 0.2cm } % { 0cm } % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_lineto:n { 2cm , 0cm } % \draw_path_use_clear:n { stroke } % \draw_dash_pattern:nn % { 0.5cm , 0.5cm , 0.1cm , 0.2cm } % { 0.1cm } % \draw_path_moveto:n { 0cm , 1mm } % \draw_path_lineto:n { 2cm , 1mm } % \draw_path_use_clear:n { stroke } % \draw_dash_pattern:nn % { 0.5cm , 0.5cm , 0.1cm , 0.2cm } % { 0.2cm } % \draw_path_moveto:n { 0cm , 2mm } % \draw_path_lineto:n { 2cm , 2mm } % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % Setting an empty pattern will produce a solid line. % % Note the \meta{pattern} interface here is different from that in \pkg{pgf}: % the list is comma-separated not given in brace groups. % \end{function} % % \begin{function}{\draw_nonzero_rule:, \draw_evenodd_rule:} % \begin{syntax} % \cs{draw_nonzero_rule:} % \end{syntax} % Active either the non-zero winding number or the even-odd rule, % respectively, for determining what is inside a fill or clip area. % For technical reasons, these command are not influenced by scoping % and apply on an ongoing basis. % \end{function} % % \begin{function} % { % \draw_cap_butt: , % \draw_cap_rectangle: , % \draw_cap_round: % } % \begin{syntax} % \cs{draw_cap_butt:} % \end{syntax} % Sets the style of terminal stroke position to one of butt, rectangle or % round. % \end{function} % % \begin{function} % { % \draw_join_bevel: , % \draw_join_miter: , % \draw_join_round: % } % \begin{syntax} % \cs{draw_join_miter:} % \end{syntax} % Sets the style of stroke joins to one of bevel, miter or round. % \end{function} % % \begin{function}{\draw_miterlimit:n} % \begin{syntax} % \cs{draw_miterlimit:n} \Arg{factor} % \end{syntax} % Sets the miter \meta{factor} of lines joined as a miter, as described in the % PDF and PostScript manuals. The \meta{factor} is an \meta{fp expr}. % \end{function} % % \subsection{Scoping drawing elements} % % Scoping drawing elements is necessary to allowing nesting of subparts. % These features have specific use requirements: the preconditions must be % met. In particular, each type of drawing scope also constitutes a % group (\cs{group_begin:}/\cs{group_end:} pair): as such, they must be % nested correctly. % % \begin{function}{\draw_scope_begin:, \draw_scope_end:} % \begin{syntax} % \cs{draw_scope_begin:} % ... % \cs{draw_scope_end:} % \end{syntax} % Creates a scope for localisation of state settings within a drawing. % A scope forms a \TeX{} group but will also localise global state % variables (such as \cs{l_draw_default_linewidth_dim}), and driver-level % concepts such as the termination of lines. % \begin{demo} % \draw_begin: % \draw_scope_begin: % \group_begin: % \draw_linewidth:n { 2pt } % \draw_path_rectangle:nn { 0 , 0 } { 2ex , 2ex } % \draw_path_use:n { stroke } % \group_end: % \draw_path_rectangle:nn { 3ex , 0ex } { 2ex , 2ex } % \draw_path_use:n { stroke } % \draw_scope_end: % \draw_path_rectangle:nn { 6ex , 0ex } { 2ex , 2ex } % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % Global graphical concepts restricted by scope are % \begin{itemize} % \item Line width % \item Stroke and fill color % \item Dash pattern % \item Line joining and capping, including the miter limit % \item Clipping paths % \item Canvas (driver) transformations % \end{itemize} % \end{function} % % \subsection{Points} % % Functions supporting the calculation of points (co-ordinates) are expandable % and may be used outside of the drawing environment. The outputs of all of the % point functions are tuples. This output form is then suitable as \emph{input} % for subsequent point calculations, \emph{i.e.}~where a \meta{point} is required % it may be given as a tuple. This \emph{may} include units and surrounding % parentheses, for example % \begin{verbatim} % 1,2 % (1,2) % 1cm,3pt % (1pt,2cm) % 2 * sind(30), 2^4in % \end{verbatim} % are all valid input forms. Notice that each part of the tuple may itself % be a float point expression. % % Point co-ordinates are relative to the canvas axes, but can be transformed % by \cs{draw_point_transform:n}. These manipulation is applied by many % higher-level functions, for example path construction, and allows parts of % a drawing to be rotated, scaled or skewed. This occurs before writing any % data to the driver, and so such manipulations are tracked by the drawing % mechanisms. See \cs{@@_backend_cm:nnnn} for backend-level % manipulation of the canvas axes themselves. % % Notice that in contrast to \pkg{pgf} it is possible to give the positions % of points \emph{directly}. % % \subsubsection{Basic point functions} % % \begin{function}[EXP]{\draw_point_polar:nn, \draw_point_polar:nnn} % \begin{syntax} % \cs{draw_point_polar:nn} \Arg{radius} \Arg{angle} % \cs{draw_point_polar:nnn} \Arg{radius-a} \Arg{radius-b} \Arg{angle} % \end{syntax} % Gives the co-ordinates of the point at \meta{angle} (an \meta{fp expr} in % \emph{degrees}) and \meta{radius}. The three-argument version accepts % two radii of different lengths. % % Note the interface here is somewhat different from that in \pkg{pgf}: % the one- and two-radii versions in \pkg{l3draw} use separate functions, % whilst in \pkg{pgf} they use the same function and a keyword. % \end{function} % % \begin{function}[EXP]{\draw_point_unit_vector:n} % \begin{syntax} % \cs{draw_point_unit_vector:n} \Arg{point} % \end{syntax} % Expands to the co-ordinates of a unit vector in the direction of the % \meta{point} from the origin. If the \meta{point} is at the origin, % a vertical unit vector is returned % \end{function} % % \begin{function}[EXP]{\draw_point_transform:n} % \begin{syntax} % \cs{draw_point_transform:n} \Arg{point} % \end{syntax} % Evaluates the position of the \meta{point} subject to the current % transformation matrix. This operation is applied automatically by % most higher-level functions (\emph{e.g.}~path manipulations). % \end{function} % % \subsubsection{Points on a vector basis} % % As well as giving explicit values, it is possible to describe points % in terms of underlying direction vectors. The latter are initially % co-incident with the standard Cartesian axes, but may be altered by % the user. % % \begin{function}{\draw_xvec:n, \draw_yvec:n, \draw_zvec:n} % \begin{syntax} % \cs{draw_xvec:n} \Arg{point} % \end{syntax} % Defines the appropriate base vector to point toward the \meta{point} % on the canvas. The standard settings for the $x$- and $y$-vectors are % $1\,\mathrm{cm}$ along the relevant canvas axis, whilst for the % $z$-vector an appropriate direction is taken. % \end{function} % % \begin{function}[EXP]{\draw_point_vec:nn, \draw_point_vec:nnn} % \begin{syntax} % \cs{draw_point_vec:nn} \Arg{xscale} \Arg{yscale} % \cs{draw_point_vec:nnn} \Arg{xscale} \Arg{yscale} \Arg{zscale} % \end{syntax} % Expands to the co-ordinate of the point at \meta{xscale} times the % $x$-vector and \meta{yscale} times the $y$-vector. The three-argument % version extends this to include the $z$-vector. % \end{function} % % \begin{function}[EXP]{\draw_point_vec_polar:nn, \draw_point_vec_polar:nnn} % \begin{syntax} % \cs{draw_point_vec_polar:nn} \Arg{radius} \Arg{angle} % \cs{draw_point_vec_polar:nnn} \Arg{radius-a} \Arg{radius-b} \Arg{angle} % \end{syntax} % Gives the co-ordinates of the point at \meta{angle} (an \meta{fp expr} in % \emph{degrees}) and \meta{radius}, relative to the prevailing % $x$- and $y$-vectors. The three-argument version accepts two radii of % different lengths. % % Note the interface here is somewhat different from that in \pkg{pgf}: % the one- and two-radii versions in \pkg{l3draw} use separate functions, % whilst in \pkg{pgf} they use the same function and a keyword. % \end{function} % % \subsubsection{Intersections} % % \begin{function}[EXP]{\draw_point_intersect_lines:nnnn} % \begin{syntax} % \cs{draw_point_intersect_lines:nnnn} \Arg{point1} \Arg{point2} \Arg{point3} \Arg{point4} % \end{syntax} % Evaluates the point at the intersection of one line, joining % \meta{point1} and \meta{point2}, and a second line joining \meta{point3} % and \meta{point4}. If the lines do not intersect, or are coincident, and % error will occur. % \end{function} % % \begin{function}[EXP]{\draw_point_intersect_circles:nnnnn} % \begin{syntax} % \cs{draw_point_intersect_circles:nnnnn} % \Arg{center1} \Arg{radius1} \Arg{center2} \Arg{radius2} \Arg{root} % \end{syntax} % Evaluates the point at the intersection of one circle with % \meta{center1} and \meta{radius1}, and a second circle with \meta{center2} % and \meta{radius2}. If the circles do not intersect, or are coincident, and % error will occur. % % Note the interface here has a different argument ordering from that in % \pkg{pgf}, which has the two centers then the two radii. % \end{function} % % \begin{function}[EXP]{\draw_point_intersect_line_circle:nnnnn} % \begin{syntax} % \cs{draw_point_intersect_line_circle:nnnnn} % \Arg{point1} \Arg{point2} \Arg{center} \Arg{radius} \Arg{root} % \end{syntax} % Evaluates the point at the intersection of one line, joining % \meta{point1} and \meta{point2}, and a circle with \meta{center} % and \meta{radius}. If the lines and circle do not intersect and % error will occur. % \end{function} % % \subsubsection{Interpolations} % % \begin{function}[EXP]{\draw_point_interpolate_line:nnn} % \begin{syntax} % \cs{draw_point_interpolate_line:nnn} \Arg{part} \Arg{point1} \Arg{point2} % \end{syntax} % Expands to the point which is \meta{part} way along the line joining % \meta{point1} and \meta{point2}. The \meta{part} may be an interpolation or % an extrapolation, and is a floating point value expressing a percentage % along the line, \emph{e.g.}~a value of \texttt{0.5} would be half-way % between the two points. % \end{function} % % \begin{function}[EXP]{\draw_point_interpolate_distance:nnn} % \begin{syntax} % \cs{draw_point_interpolate_distance:nnn} \Arg{distance} \Arg{point expr1} \Arg{point expr2} % \end{syntax} % Expands to the point which is \meta{distance} way along the line joining % \meta{point1} and \meta{point2}. The \meta{distance} may be an interpolation % or an extrapolation. % \end{function} % % \begin{function}[EXP]{\draw_point_interpolate_curve:nnnnnn} % \begin{syntax} % \cs{draw_point_interpolate_curve:nnnnnn} \Arg{part} % \Arg{start} \Arg{control1} \Arg{control2} \Arg{end} % \end{syntax} % Expands to the point which is \meta{part} way along the curve between % \meta{start} and \meta{end} and defined by \meta{control1} and % \meta{control2}. The \meta{part} may be an interpolation or % an extrapolation, and is a floating point value expressing a percentage % along the curve, \emph{e.g.}~a value of \texttt{0.5} would be half-way % along the curve. % \end{function} % % \subsection{Paths} % % Paths are constructed by combining one or more operations before applying % one or more actions. Thus until a path is \enquote{used}, it may be % manipulated or indeed discarded entirely. Only one path is active at % any one time, and the path is \emph{not} affected by \TeX{} grouping. % % \begin{function}{\draw_path_corner_arc:nn} % \begin{syntax} % \cs{draw_path_corner_arc:nn} \Arg{length1} \Arg{length2} % \end{syntax} % Sets the degree of rounding applied to corners in a path: the two % \meta{length} values are the distances from the corner at which the curving % should start. The first \meta{length} applies to the part of the path % \enquote{leading in} to the corner (\emph{i.e.}~from the previous path % operation), and the second to that \enquote{leading out}. If both % values are \texttt{0pt} then corners will not be rounded. The values % apply within the scope of the current \TeX{} group. % \begin{demo} % \draw_begin: % \draw_path_corner_arc:nn { 5mm } { 5mm } % \draw_path_rectangle_corners:nn % { 0cm , 0cm } { 3cm , 2cm } % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % \begin{demo} % \draw_begin: % \draw_path_corner_arc:nn { 10mm } { 5mm } % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_lineto:n { 0cm , 2cm } % \draw_path_lineto:n { 3cm , 2cm } % \draw_path_curveto:nnn % { 3cm , 0cm } { 2cm , 0cm } { 1cm , 0cm } % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % The corners created are quarter-circles for exact right-angles and % close approximations otherwise. Closing a path will result in rounding % correctly. % \begin{demo} % \draw_begin: % \draw_path_corner_arc:nn { 4pt } { 4pt } % \draw_path_moveto:n % { \draw_point_polar:nn { 1cm } { 0 } } % \int_step_inline:nnnn { 72 } { 72 } { 359 } % { % \draw_path_lineto:n % { \draw_point_polar:nn { 1cm } { #1 } } % } % \draw_path_close: % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % \end{function} % % \begin{function}{\draw_path_close:} % \begin{syntax} % \cs{draw_path_close:} % \end{syntax} % Closes the current part of the path by appending a straight line from % the current point to the starting point of the path. In general, any % path to be \emph{filled} should include a close instructions. % \end{function} % % \begin{function}{\draw_path_use:n, \draw_path_use_clear:n} % \begin{syntax} % \cs{draw_path_use:n} \Arg{action(s)} % \end{syntax} % Inserts the current path, carrying out one ore more possible \meta{actions} % (a comma list): % \begin{itemize} % \item \texttt{stroke} Draws a line along the current path % \item \texttt{draw} A synonym for \texttt{stroke} % \item \texttt{fill} Fills the interior of the path with the current % file color % \item \texttt{clip} Clips any content outside of the path % \end{itemize} % Actions are applied in the order given irrespective of the input order. % Note that whilst it is possible to use a path without clearing it, the % normal use case would be to clear the path (this resets data structures % at the macro level). % \end{function} % % \begin{function}{\draw_path_replace_bb:} % \begin{syntax} % \cs{draw_path_replace_bb:} % \end{syntax} % Replaces the current bounding box of the drawing with one specified by the % current path: this will be applied even when \cs{l_draw_bb_update_bool} % is \texttt{false}. The current path is then cleared. Note that % \cs{l_draw_bb_update_bool} is \emph{not} changed by this function: % the user may wish to set it to \texttt{false} so that the bounding box % is then left unchanged by further operations. % \end{function} % % \subsubsection{Path operations on drawing axes} % % The standard path functions are all influenced by the active transformation % matrix, \emph{i.e.}~the work relative to the drawing axes rather than % the canvas. % % \begin{function}{\draw_path_moveto:n} % \begin{syntax} % \cs{draw_path_moveto:n} \Arg{point} % \end{syntax} % Moves the reference point of the path to the \meta{point}, but will % not join this to any previous point. % \end{function} % % \begin{function}{\draw_path_lineto:n} % \begin{syntax} % \cs{draw_path_lineto:n} \Arg{point} % \end{syntax} % Joins the current path to the \meta{point} with a straight line. % In general, for reliable treatment by viewers, a \cs{draw_path_moveto:n} % operation should precede the first use of a \cs{draw_path_lineto:n} % on a path. % \begin{demo} % \draw_begin: % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_lineto:n { 1cm , 1cm } % \draw_path_lineto:n { 2cm , 1cm } % \draw_path_lineto:n { 3cm , 0.5cm } % \draw_path_lineto:n { 3cm , 0cm } % \color_fill:n { yellow!80!black } % \draw_path_use_clear:n { fill , stroke } % \draw_end: % \end{demo} % \begin{demo} % \draw_begin: % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_lineto:n { 1cm , 1cm } % \draw_path_lineto:n { 2cm , 1cm } % \draw_path_moveto:n { 2cm , 1cm } % Begins a new part % \draw_path_lineto:n { 3cm , 0.5cm } % \draw_path_lineto:n { 3cm , 0cm } % \color_fill:n { yellow!80!black } % \draw_path_use_clear:n { fill , stroke } % \draw_end: % \end{demo} % \end{function} % % \begin{function}{\draw_path_curveto:nnn} % \begin{syntax} % \cs{draw_path_curveto:nnn} \Arg{control1} \Arg{control2} \Arg{end} % \end{syntax} % Joins the current path to the \meta{end} with a curved line defined by % cubic Bézier points \meta{control1} and \meta{control2}. The bounding box % of the path (and image) will fully-contain the curve and control points, % \emph{i.e.}~it may be bigger than strictly necessary to contain the curve % \emph{alone}. % \begin{demo} % \draw_begin: % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_curveto:nnn % { 1cm , 1cm } % First control % { 2cm , 1cm } % Second control % { 3cm , 0cm } % End % \color_fill:n { yellow!80!black } % \draw_path_use_clear:n { fill , stroke } % \draw_end: % \end{demo} % \end{function} % % \begin{function}{\draw_path_curveto:nn} % \begin{syntax} % \cs{draw_path_curveto:nn} \Arg{control} \Arg{end} % \end{syntax} % Joins the current path to the \meta{end} with a curved line defined by % quadratic Bézier point \meta{control}. The bounding box % of the path (and image) will fully-contain the curve and computed (cubic) % control points, \emph{i.e.}~it may be bigger than strictly necessary to % contain the curve \emph{alone}. % \begin{demo} % \draw_begin: % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_curveto:nn % { 1cm , 1cm } % { 2cm , 0cm } % \color_fill:n { yellow!80!black } % \draw_path_use_clear:n { fill , stroke } % \draw_end: % \end{demo} % \end{function} % % \begin{function}{\draw_path_arc:nnn, \draw_path_arc:nnnn} % \begin{syntax} % \cs{draw_path_arc:nnn} \Arg{angle1} \Arg{angle2} \Arg{radius} % \cs{draw_path_arc:nnnn} \Arg{angle1} \Arg{angle2} \Arg{radius-a} \Arg{radius-b} % \end{syntax} % Joins the current path with an arc between \meta{angle1} and \meta{angle2} % and of \meta{radius}. The four-argument version accepts two radii of % different lengths. % \begin{demo} % \draw_begin: % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_lineto:n { 0cm , 1cm } % \draw_path_arc:nnn { 180 } { 90 } { 0.5cm } % \draw_path_lineto:n { 3cm , 1.5cm } % \draw_path_arc:nnn { 90 } { -45 } { 0.5cm } % \draw_path_use_clear:n { fill } % \draw_end: % \end{demo} % % Note the interface here has a different argument ordering from that in % \pkg{pgf}, which has the two centers then the two radii. % \begin{demo} % \draw_begin: % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_arc:nnnn { 180 } { 45 } { 2cm } { 1cm } % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % \end{function} % % \begin{function}{\draw_path_arc_axes:nnnn} % \begin{syntax} % \cs{draw_path_arc_axes:nnn} \Arg{angle1} \Arg{angle2} \Arg{vector1} \Arg{vector2} % \end{syntax} % Appends the portion of an ellipse from \meta{angle1} to \meta{angle2} of an % ellipse with axes along \meta{vector1} and \meta{vector2} to the current % path. % \begin{demo} % \draw_begin: % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_lineto:n { 2cm , 5mm } % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_lineto:n { 0cm , 1cm } % \draw_path_moveto:n { 2cm , 5mm } % \draw_path_arc_axes:nnnn { 0 } { 90 } % { 2cm , 5mm } { 0cm , 1cm } % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % \end{function} % % \begin{function}{\draw_path_ellipse:nnn} % \begin{syntax} % \cs{draw_path_ellipse:nnn} \Arg{center} \Arg{vector1} \Arg{vector2} % \end{syntax} % Appends an ellipse at \meta{center} with axes along \meta{vector1} and % \meta{vector2} to the current path. A new part is started if the path % is already non-empty. Notice that the underlying drawing is constructed % from arcs with appropriate moves: the interfaces is a more efficient % convenience. % \begin{demo} % \draw_begin: % \draw_path_ellipse:nnn % { 1cm , 0cm } % { 1.5cm , 0cm } % { 0cm , 1cm } % \draw_path_use_clear:n { stroke } % \color_select:n { red } % \draw_path_ellipse:nnn % { 1cm , 0cm } % { 1cm , 1cm } % { -0.5cm , 0.5cm } % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % % Note that any transformation is applied to the completed ellipse rather than % to the axes. % \end{function} % % \begin{function}{\draw_path_circle:nn} % \begin{syntax} % \cs{draw_path_circle:nn} \Arg{center} \Arg{radius} % \end{syntax} % Appends a circle of \meta{radius} at \meta{center} to the current path. % This is a shortcut for \cs{draw_path_ellipse:nnn}. % \end{function} % % \begin{function}{\draw_path_rectangle:nn, \draw_path_rectangle_corners:nn} % \begin{syntax} % \cs{draw_path_rectangle:nn} \Arg{lower-left} \Arg{displacement} % \cs{draw_path_rectangle_corners:nn} \Arg{lower-left} \Arg{top-right} % \end{syntax} % Appends a rectangle starting at \meta{lower-left} to the current path, % with the size of the rectangle determined either by a \meta{displacement} % or the position of the \meta{top-right}. % \begin{demo} % \draw_begin: % \draw_path_rectangle:nn % { 1cm , 0cm } % { 1.5cm , 1cm } % \draw_path_rectangle:nn % { 1.5cm , 0.25cm } % { 1.5cm , 1cm } % \draw_path_rectangle:nn % { 2cm , 0.5cm } % { 1.5cm , 1cm } % \draw_path_use_clear:n { draw } % \draw_end: % \end{demo} % \begin{demo} % \draw_begin: % \draw_path_rectangle_corners:nn % { 1cm , 0cm } % { 1.5cm , 1cm } % \draw_path_use_clear:n { draw } % \draw_end: % \end{demo} % \end{function} % % \begin{function}{\draw_path_grid:nnnn} % \begin{syntax} % \cs{draw_path_grid:nnnn} \Arg{xstep} \Arg{ystep} \Arg{lower-left} \Arg{upper-right} % \end{syntax} % Constructs a grid of \meta{xstep} and \meta{ystep} inside the rectangle % defined by the \meta{lower-left} and the \meta{upper-right}, and appends % this to the current path. The grid will be aligned such that grid lines % pass through the origin, which may result in \enquote{protruding} ends % if the start/end positions do not fully align. % \begin{demo} % \draw_begin: % \draw_linewidth:n { 0.8pt } % \draw_path_grid:nnnn % { 1cm } { 1cm } % { -3mm , -3mm } % { 33mm , 23mm } % \draw_path_use_clear:n { stroke } % \draw_linewidth:n { 0.4pt } % \draw_path_grid:nnnn % { 1mm } { 1mm } % { -1.5mm , -1.5mm } % { 31.5mm , 21.5mm } % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % % Any transformation is applied to the finished grid. % \begin{demo} % \draw_begin: % \draw_transform_rotate:n { 10 } % \draw_path_grid:nnnn % { 1mm } { 2mm } % { 0mm , 0mm } % { 30mm , 30mm } % \draw_path_use_clear:n { stroke } % \draw_end: % \end{demo} % \end{function} % % \subsubsection{Path scope} % % \begin{function}{\draw_path_scope_begin:, \draw_path_scope_end:} % \begin{syntax} % \cs{draw_path_scope_begin:} % ... % \cs{draw_path_scope_end:} % \end{syntax} % Suspends (and saves) the current (partial) path, initialising a new % path within the scope. Path operations are written to output only when % used, so the scoped path is stored at the \pkg{expl3} level, not % in the output. % \end{function} % % \subsubsection{Path operations on canvas axes} % % For \emph{specialist} work, a small number of functions are provided % which work relative to the canvas axes, \emph{i.e.}~these functions % ignore the transformation matrix. % % \begin{function}{\draw_path_canvas_moveto:n} % \begin{syntax} % \cs{draw_path_canvas_moveto:n} \Arg{canvas point} % \end{syntax} % Moves the reference point of the path to the \meta{canvas point}, but will % not join this to any previous point. % \end{function} % % \begin{function}{\draw_path_canvas_lineto:n} % \begin{syntax} % \cs{draw_path_canvas_lineto:n} \Arg{canvas point} % \end{syntax} % Joins the current path to the \meta{canvas point} with a straight line. % \end{function} % % \begin{function}{\draw_path_canvas_curveto:nnn} % \begin{syntax} % \cs{draw_path_canvas_curveto:nnn} \Arg{control1} \Arg{control2} \Arg{end} % \end{syntax} % Joins the current path to the \meta{end} with a curved line defined by % cubic Bézier points \meta{control1} and \meta{control2}. These positions % are given as canvas points. % \end{function} % % \subsection{Bounding box} % % \begin{variable}{\l_draw_bb_update_bool} % All functions automatically update the bounding box of the image, unless % specified otherwise. This behavior is selectable using the % \cs{l_draw_bb_update_bool} boolean. % \end{variable} % % \subsection{Boxes and coffins} % % \begin{function}{\draw_box_use:N, \draw_box_use:Nn} % \begin{syntax} % \cs{draw_box_use:N} \meta{box} % \cs{draw_box_use:Nn} \meta{box} \Arg{point} % \end{syntax} % Inserts the \meta{box} into a drawing, taking account of the current % transformation matrix and shift, and adjusting the drawing bounding % box to contain the (apparent) size of the box if this is active % (see \cs{l_draw_bb_update_bool}). % \begin{demo} % \draw_begin: % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_lineto:n { 0cm , 1cm } % \draw_path_use_clear:n { stroke } % \hbox_set:Nn \l_tmpa_box % { This~is~text. } % \draw_box_use:N \l_tmpa_box % \draw_end: % \end{demo} % \begin{demo} % \draw_begin: % \draw_transform_matrix_absolute:nnnn { 2 } { 0 } { 1 } { 2 } % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_lineto:n { 0cm , 1cm } % \draw_path_use_clear:n { stroke } % \hbox_set:Nn \l_tmpa_box % { This~is~text. } % \draw_box_use:N \l_tmpa_box % \draw_end: % \end{demo} % If the \meta{point} is given, the reference point of the box is placed there: % otherwise it is positioned at the origin of the drawing. % \end{function} % % \begin{function}{\draw_coffin_use:Nnn, \draw_coffin_use:Nnnn} % \begin{syntax} % \cs{draw_coffin_use:Nnn} \meta{coffin} \Arg{hpole} \Arg{vpole} % \cs{draw_coffin_use:Nnnn} \meta{coffin} \Arg{hpole} \Arg{vpole} \Arg{point} % \end{syntax} % Inserts the \meta{coffin} into a drawing, taking account of the current % transformation matrix and shift, and adjusting the drawing bounding % box to contain the (apparent) size of the box if this is active % (see \cs{l_draw_bb_update_bool}). The alignment point of the coffin to % the origin is specified by the intersection of the \meta{hpole} and the % \meta{vpole}. % \begin{demo} % \draw_begin: % \draw_path_moveto:n { 0cm , 0cm } % \draw_path_lineto:n { 0cm , 1cm } % \draw_path_use_clear:n { stroke } % \hcoffin_set:Nn \l_tmpa_coffin % { This~is~text. } % \draw_coffin_use:Nnn \l_tmpa_coffin { hc } { vc } % \draw_end: % \end{demo} % If the \meta{point} is given, the pole intersection of the coffine is % placed there: otherwise it is positioned at the origin of the drawing. % \end{function} % % \subsection{Transformations} % % Points are normally used unchanged relative to the canvas axes. This can % be modified by applying a transformation matrix. The canvas axes themselves % may be adjusted using \cs{driver_draw_cm:nnnn}: note that this % is transparent to the drawing code so is not tracked. % % \begin{function} % {\draw_transform_matrix:nnnn, \draw_transform_matrix_absolute:nnnn} % \begin{syntax} % \cs{draw_transform_matrix:nnnn} % \Arg{a} \Arg{b} \Arg{c} \Arg{d} % \end{syntax} % Applies the transformation matrix $[ \meta{a} \meta{b} \meta{c} \meta{d}]$. % The basic applies the transformation in addition to those active; the % |absolute| version overwrites any active transformation. % This assignment is local. % \end{function} % % \begin{function}{\draw_transform_shift:n, \draw_transform_shift_absolute:n} % \begin{syntax} % \cs{draw_transform_shift:n} \Arg{vector} % \end{syntax} % Applies the transformation \meta{vector} to points. % The basic applies the vector in addition to those active; the % |absolute| version overwrites any active vector. Any active % transformation matrix is applied to the shifts each time they are adjusted. % This assignment is local. % \end{function} % % \begin{function}{\draw_transform_triangle:nnn} % \begin{syntax} % \cs{draw_transform_triangle:nnn} % \Arg{origin} \Arg{point1} \Arg{point2} % \end{syntax} % Applies a transformation such that the co-ordinates $(0, 0)$, $(1, 0)$ % and $(0, 1)$ are given by the \meta{origin}, \meta{point1} and % \meta{point2}, respectively. % This assignment is local. % \end{function} % % \begin{function}{\draw_transform_rotate:n} % \begin{syntax} % \cs{draw_transform_rotate:n} \Arg{angle} % \end{syntax} % Applies a rotation by the \meta{angle}, measured anti-clockwise in degrees. % This rotation is \emph{additional} to any prevailing transformation. % This assignment is local. % \end{function} % % \begin{function} % { % \draw_transform_scale:n, % \draw_transform_xscale:n, % \draw_transform_yscale:n % } % \begin{syntax} % \cs{draw_transform_scale:n} \Arg{scale} % \end{syntax} % Applies the \meta{scale} in either $x$ or $y$ (or both). % This scale is \emph{added} to any prevailing transformation. % This assignment is local. % \end{function} % % \begin{function} % { % \draw_transform_xshift:n, % \draw_transform_yshift:n % } % \begin{syntax} % \cs{draw_transform_xshift:n} \Arg{xshift} % \end{syntax} % Applies an \meta{xshift} or \meta{yshift}, as appropriate. This shift is % \emph{added} to any prevailing one. % This assignment is local. % \end{function} % % \begin{function} % { % \draw_transform_xslant:n, % \draw_transform_yslant:n % } % \begin{syntax} % \cs{draw_transform_xslant:n} \Arg{slant} % \end{syntax} % Applies the \meta{slant} (a factor) in either $x$ or $y$. % This slant is \emph{added} to any prevailing transformation. % This assignment is local. % \end{function} % % \begin{function} % {\draw_transform_matrix_invert:, \draw_transform_shift_invert:} % \begin{syntax} % \cs{draw_transform_matrix_invert:} % \end{syntax} % Inverts the current transformation matrix or shift vector, as % appropriate. % This assignment is local. % \end{function} % % \begin{function} % {\draw_transform_matrix_reset:, \draw_transform_shift_reset:} % \begin{syntax} % \cs{draw_transform_matrix_reset:} % \end{syntax} % Resets the current transformation matrix or shift vector, as % appropriate. % This assignment is local. % \end{function} % % \subsection{Layers} % % Drawing layers may be used to alter the way in which elements are stacked % on top of one another. In particular, they are useful when the nature of % an element depends on another, but where it needs to be behind it's % \enquote{parent}. A classic example is a filled background: this needs to % know the size of the material it is behind. % % All drawings feature a layer called |main|. This layer should always be % present, and is the one which \enquote{non-layered} content is added to. % % \begin{function}{\draw_layer_new:n} % \begin{syntax} % \cs{draw_layer_new:n} \Arg{layer} % \end{syntax} % Creates a new \meta{layer} which can then be used in drawing. The layer % |main| is pre-defined. % \end{function} % % \begin{function}{\draw_layer_begin:n, \draw_layer_end:} % \begin{syntax} % \cs{draw_layer_begin:n} \Arg{layer} % ... % \cs{draw_layer_end:} % \end{syntax} % Begin and end collection of material for the \meta{layer}, which should % previously have been created (and which cannot be |main|). Material % collected for the layer is available globally within the current drawing. % \end{function} % % \begin{variable}{\l_draw_layers_clist} % The list of active layers: may be given anywhere before \cs{draw_end:}. % The |main| layer should always be included. % \end{variable} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3draw} implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=draw> % \end{macrocode} % % \begin{macrocode} \ProvidesExplPackage{l3draw}{2024-03-14}{} {L3 Experimental core drawing support} % \end{macrocode} % % \subsection{Internal auxiliaries} % % \begin{variable}{\s_@@_mark,\s_@@_stop} % Internal scan marks. % \begin{macrocode} \scan_new:N \s_@@_mark \scan_new:N \s_@@_stop % \end{macrocode} % \end{variable} % % \begin{variable}{\q_@@_recursion_tail,\q_@@_recursion_stop} % Internal recursion quarks. % \begin{macrocode} \quark_new:N \q_@@_recursion_tail \quark_new:N \q_@@_recursion_stop % \end{macrocode} % \end{variable} % % \begin{macro}[EXP]{\@@_if_recursion_tail_stop_do:Nn} % Functions to query recursion quarks. % \begin{macrocode} \__kernel_quark_new_test:N \@@_if_recursion_tail_stop_do:Nn % \end{macrocode} % \end{macro} % % Everything else is in the sub-files! % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex