% \iffalse meta-comment % %% File: l3draw-transforms.dtx % % Copyright (C) 2018-2024 The LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % http://www.latex-project.org/lppl.txt % % This file is part of the "l3experimental bundle" (The Work in LPPL) % and all files in that bundle must be distributed together. % % ----------------------------------------------------------------------- % % The development version of the bundle can be found at % % https://github.com/latex3/latex3 % % for those people who are interested. % %<*driver> \RequirePackage{expl3} \documentclass[full]{l3doc} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{l3draw-transforms} package\\ Transformations^^A % } % % \author{^^A % The \LaTeX{} Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2024-03-14} % % \maketitle % % \begin{implementation} % % \section{\pkg{l3draw-transforms} implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=draw> % \end{macrocode} % % This sub-module covers more-or-less the same ideas as % \texttt{pgfcoretransformations.code.tex}. At present, equivalents of the % following are currently absent: % \begin{itemize} % \item \cs{pgfgettransform}, \cs{pgfgettransformentries}: Awaiting use cases. % \item \cs{pgftransformlineattime}, \cs{pgftransformarcaxesattime}, % \cs{pgftransformcurveattime}: Need to look at the use cases for % these to fully understand them. % \item \cs{pgftransformarrow}: Likely to be done when other arrow functions % are added. % \item \cs{pgftransformationadjustments}: Used mainly by CircuiTi\textit{k}Z % although also for shapes, likely needs more use cases before addressing. % \item \cs{pgflowlevelsynccm}, \cs{pgflowlevel}: Likely to be added when % use cases are encountered in other parts of the code. % \item \cs{pgfviewboxscope}: Seems very speicalied, need to understand the % requirements here. % \end{itemize} % % \begin{variable}{\l_@@_matrix_active_bool} % An internal flag to avoid redundant calculations. % \begin{macrocode} \bool_new:N \l_@@_matrix_active_bool % \end{macrocode} % \end{variable} % % \begin{variable} % { % \l_@@_matrix_a_fp, % \l_@@_matrix_b_fp, % \l_@@_matrix_c_fp, % \l_@@_matrix_a_fp, % \l_@@_xshift_dim, % \l_@@_yshift_dim % } % The active matrix and shifts. % \begin{macrocode} \fp_new:N \l_@@_matrix_a_fp \fp_new:N \l_@@_matrix_b_fp \fp_new:N \l_@@_matrix_c_fp \fp_new:N \l_@@_matrix_d_fp \dim_new:N \l_@@_xshift_dim \dim_new:N \l_@@_yshift_dim % \end{macrocode} % \end{variable} % % \begin{macro}{\draw_transform_matrix_reset:, \draw_transform_shift_reset:} % Fast resetting. % \begin{macrocode} \cs_new_protected:Npn \draw_transform_matrix_reset: { \fp_set:Nn \l_@@_matrix_a_fp { 1 } \fp_zero:N \l_@@_matrix_b_fp \fp_zero:N \l_@@_matrix_c_fp \fp_set:Nn \l_@@_matrix_d_fp { 1 } \bool_set_false:N \l_@@_matrix_active_bool } \cs_new_protected:Npn \draw_transform_shift_reset: { \dim_zero:N \l_@@_xshift_dim \dim_zero:N \l_@@_yshift_dim } \draw_transform_matrix_reset: \draw_transform_shift_reset: % \end{macrocode} % \end{macro} % % \begin{macro}{\draw_transform_matrix_absolute:nnnn} % \begin{macro}{\draw_transform_shift_absolute:n} % \begin{macro}{\@@_transform_shift_absolute:nn} % Setting the transform matrix is straight-forward, with just a bit % of expansion to sort out. With the mechanism active, the identity % matrix is set. % \begin{macrocode} \cs_new_protected:Npn \draw_transform_matrix_absolute:nnnn #1#2#3#4 { \fp_set:Nn \l_@@_matrix_a_fp {#1} \fp_set:Nn \l_@@_matrix_b_fp {#2} \fp_set:Nn \l_@@_matrix_c_fp {#3} \fp_set:Nn \l_@@_matrix_d_fp {#4} \bool_lazy_all:nTF { { \fp_compare_p:nNn \l_@@_matrix_a_fp = \c_one_fp } { \fp_compare_p:nNn \l_@@_matrix_b_fp = \c_zero_fp } { \fp_compare_p:nNn \l_@@_matrix_c_fp = \c_zero_fp } { \fp_compare_p:nNn \l_@@_matrix_d_fp = \c_one_fp } } { \bool_set_false:N \l_@@_matrix_active_bool } { \bool_set_true:N \l_@@_matrix_active_bool } } \cs_new_protected:Npn \draw_transform_shift_absolute:n #1 { \@@_point_process:nn { \@@_transform_shift_absolute:nn } {#1} } \cs_new_protected:Npn \@@_transform_shift_absolute:nn #1#2 { \@@_transform_shift:nnnn { 0pt } { 0pt } {#1} {#2} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\draw_transform_matrix:nnnn} % \begin{macro}{\@@_transform:nnnn} % \begin{macro}{\draw_transform_shift:n} % \begin{macro}{\@@_transform_shift:nn} % Much the same story for adding to an existing matrix, with a bit of % pre-expansion so that the calculation uses \enquote{frozen} values. % \begin{macrocode} \cs_new_protected:Npn \draw_transform_matrix:nnnn #1#2#3#4 { \use:e { \@@_transform:nnnn { \fp_eval:n {#1} } { \fp_eval:n {#2} } { \fp_eval:n {#3} } { \fp_eval:n {#4} } } } \cs_new_protected:Npn \@@_transform:nnnn #1#2#3#4 { \use:e { \draw_transform_matrix_absolute:nnnn { #1 * \l_@@_matrix_a_fp + #2 * \l_@@_matrix_c_fp } { #1 * \l_@@_matrix_b_fp + #2 * \l_@@_matrix_d_fp } { #3 * \l_@@_matrix_a_fp + #4 * \l_@@_matrix_c_fp } { #3 * \l_@@_matrix_b_fp + #4 * \l_@@_matrix_d_fp } } } \cs_new_protected:Npn \draw_transform_shift:n #1 { \@@_point_process:nn { \@@_transform_shift:nn } {#1} } \cs_new_protected:Npn \@@_transform_shift:nn #1#2 { \@@_transform_shift:nnnn \l_@@_xshift_dim \l_@@_yshift_dim {#1} {#2} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\@@_transform_shift:nnnn} % Apply the current transformation matrix to the shift, then store % the resulting values: we may or may not have a none-zero starting % point here. % \begin{macrocode} \cs_new_protected:Npn \@@_transform_shift:nnnn #1#2#3#4 { \dim_set:Nn \l_@@_xshift_dim { \fp_to_dim:n { #1 + ( #3 * \l_@@_matrix_a_fp + #4 * \l_@@_matrix_c_fp ) } } \dim_set:Nn \l_@@_yshift_dim { \fp_to_dim:n { #2 + ( #3 * \l_@@_matrix_b_fp + #4 * \l_@@_matrix_d_fp ) } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\draw_transform_matrix_invert:} % \begin{macro}{\@@_transform_invert:n, \@@_transform_invert:e} % \begin{macro}{\draw_transform_shift_invert:} % Standard mathematics: calculate the inverse matrix and use that, then % undo the shifts. % \begin{macrocode} \cs_new_protected:Npn \draw_transform_matrix_invert: { \bool_if:NT \l_@@_matrix_active_bool { \@@_transform_invert:e { \fp_eval:n { 1 / ( \l_@@_matrix_a_fp * \l_@@_matrix_d_fp - \l_@@_matrix_b_fp * \l_@@_matrix_c_fp ) } } } } \cs_new_protected:Npn \@@_transform_invert:n #1 { \fp_set:Nn \l_@@_matrix_a_fp { \l_@@_matrix_d_fp * #1 } \fp_set:Nn \l_@@_matrix_b_fp { -\l_@@_matrix_b_fp * #1 } \fp_set:Nn \l_@@_matrix_c_fp { -\l_@@_matrix_c_fp * #1 } \fp_set:Nn \l_@@_matrix_d_fp { \l_@@_matrix_a_fp * #1 } } \cs_generate_variant:Nn \@@_transform_invert:n { e } \cs_new_protected:Npn \draw_transform_shift_invert: { \dim_set:Nn \l_@@_xshift_dim { -\l_@@_xshift_dim } \dim_set:Nn \l_@@_yshift_dim { -\l_@@_yshift_dim } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\draw_transform_triangle:nnn} % Simple maths to move the canvas origin to |#1| and the two axes to % |#2| and |#3|. % \begin{macrocode} \cs_new_protected:Npn \draw_transform_triangle:nnn #1#2#3 { \@@_point_process:nnn { \@@_point_process:nn { \@@_transform_triangle:nnnnnn } {#1} } {#2} {#3} } \cs_new_protected:Npn \@@_transform_triangle:nnnnnn #1#2#3#4#5#6 { \use:e { \draw_transform_matrix_absolute:nnnn { #3 - #1 } { #4 - #2 } { #5 - #1 } { #6 - #2 } \draw_transform_shift_absolute:n { #1 , #2 } } } % \end{macrocode} % \end{macro} % % \begin{macro} % {\draw_transform_scale:n, \draw_transform_xscale:n, \draw_transform_yscale:n} % \begin{macro} % {\draw_transform_xshift:n, \draw_transform_yshift:n} % \begin{macro} % {\draw_transform_xslant:n, \draw_transform_yslant:n} % Lots of shortcuts. % \begin{macrocode} \cs_new_protected:Npn \draw_transform_scale:n #1 { \draw_transform_matrix:nnnn { #1 } { 0 } { 0 } { #1 } } \cs_new_protected:Npn \draw_transform_xscale:n #1 { \draw_transform_matrix:nnnn { #1 } { 0 } { 0 } { 1 } } \cs_new_protected:Npn \draw_transform_yscale:n #1 { \draw_transform_matrix:nnnn { 1 } { 0 } { 0 } { #1 } } \cs_new_protected:Npn \draw_transform_xshift:n #1 { \draw_transform_shift:n { #1 , 0pt } } \cs_new_protected:Npn \draw_transform_yshift:n #1 { \draw_transform_shift:n { 0pt , #1 } } \cs_new_protected:Npn \draw_transform_xslant:n #1 { \draw_transform_matrix:nnnn { 1 } { 0 } { #1 } { 1 } } \cs_new_protected:Npn \draw_transform_yslant:n #1 { \draw_transform_matrix:nnnn { 1 } { #1 } { 0 } { 1 } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\draw_transform_rotate:n} % \begin{macro} % { % \@@_transform_rotate:n, \@@_transform_rotate:e, % \@@_transform_rotate:nn, \@@_transform_rotate:ee % } % Slightly more involved: evaluate the angle only once, and the sine and % cosine only once. % \begin{macrocode} \cs_new_protected:Npn \draw_transform_rotate:n #1 { \@@_transform_rotate:e { \fp_eval:n {#1} } } \cs_new_protected:Npn \@@_transform_rotate:n #1 { \@@_transform_rotate:ee { \fp_eval:n { cosd(#1) } } { \fp_eval:n { sind(#1) } } } \cs_generate_variant:Nn \@@_transform_rotate:n { e } \cs_new_protected:Npn \@@_transform_rotate:nn #1#2 { \draw_transform_matrix:nnnn {#1} {#2} { -#2 } { #1 } } \cs_generate_variant:Nn \@@_transform_rotate:nn { ee } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex