1

I would like to modify a command so that it doesn't need the ifthen package not necessarily because of this, Why is the ifthen package obsolete?, but just to used the funcationality in Tikz/PGF. The problem is nothing I've tried works. I thought \pgfmathifthenelse{}{}{} and \pgfmathequal{}{} would work but they doesn't. I get unknown control sequence errors. Am I missing something simple?

% !TEX TS-program = lualatexmk
% !TEX encoding = UTF-8 Unicode
\documentclass{article}
\RequirePackage{unicode-math}
\unimathsetup{math-style=ISO}
\setmathfont[Scale=MatchLowercase]{TeX Gyre DejaVu Math} % Good g everywhere. Based on 
\setmathfont[Scale=MatchLowercase,range={\mathscr,\mathbfscr}]{XITS Math}
\setmathfont[Scale=MatchLowercase,range={\mathcal,\mathbfcal},StylisticSet=1]{XITS Math}
\setmathfont[Scale=MatchLowercase,range=    it/{greek,Greek}]{Latin Modern Math}
\setmathfont[Scale=MatchLowercase,range=  bfit/{greek,Greek}]{Latin Modern Math}
\setmathfont[Scale=MatchLowercase,range=    up/{greek,Greek}]{Latin Modern Math}
\setmathfont[Scale=MatchLowercase,range=  bfup/{greek,Greek}]{Latin Modern Math}
\setmathfont[Scale=MatchLowercase,range=bfsfup/{greek,Greek}]{Latin Modern Math}

\usepackage{tikz} \usepackage{xstring} % needed for \StrCount \usepackage{ifthen}

\ExplSyntaxOn \NewDocumentCommand{\reverselist}{ m m }{% \clist_clear_new:N #1 \clist_set:Nx #1 { \clist_reverse:n { #2 } } }% \ExplSyntaxOff

\NewDocumentCommand{\TensorMachine}{ m O{} O{} }{% % For aesthetic reasons, we need to reverse the order of #1. \reverselist{\revslots}{#1} \begin{tikzpicture}[baseline] \pgfmathsetmacro{\cubex}{4} \pgfmathsetmacro{\cubey}{1} \pgfmathsetmacro{\cubez}{3} \pgfmathsetmacro{\slotwidth}{1}

% front
\draw[fill=white,thick,line join=round] (0.5*\cubex,0.5*\cubey,0.5*\cubez) -- 
  ++(-\cubex,0,0) -- ++(0,-\cubey,0) -- ++(\cubex,0,0) -- cycle
  % label the machine
  node at (-0.30*\cubex,0,0.5*\cubez) {\(\symbf{#2}\)}
  node at (0.30*\cubex,0,0.5*\cubez) {\(\symbf{#3}\)};
% side
\draw[fill=white,thick,line join=round] (0.5*\cubex,0.5*\cubey,-0.5*\cubez) --    
  ++(0,0,\cubez) -- ++(0,-\cubey,0) -- ++(0,0,-\cubez) -- cycle;
% top
\draw[fill=white,thick,line join=round] (0.5*\cubex,0.5*\cubey,-0.5*\cubez) --
  ++(-\cubex,0,0) -- ++(0,0,\cubez) -- ++(\cubex,0,0) -- cycle;
% output slot
\draw[fill=black,ultra thick] (-0.5*\slotwidth,0,0.5*\cubez) -- 
  ++(0,0.03,0) -- ++(\slotwidth,0,0) -- ++(0,-0.03,0) -- cycle;
%\fill (0,0,0) circle (2pt); % origin

\ifthenelse{\equal{#1}{}}%
  {% We have a scalar. Fill the output slot and we're done.
    \draw[fill=white,thin,-] (-0.5*\slotwidth+0.1,0,0.5*\cubez) 
      -- ++(-0.25,-\slotwidth,0) -- ++(\slotwidth-0.2,0,0) 
      -- ++(0.25,\slotwidth,0) 
      node at (-0.125*\slotwidth,-0.5*\cubey,0.5*\cubez) {\(\symbb{R}\)};
  }%
  {% We have slots, which may be filled or unfilled.
    % Set some values.
    \StrCount{#1,}{,}[\numslots]
    \StrCount{#1}{+}[\numfilledslots]
    \pgfmathsetmacro{\startvslotx}{-0.25*\cubex-0.5*\slotwidth}
    \pgfmathsetmacro{\startoslotx}{+0.25*\cubex-0.5*\slotwidth}
    \pgfmathsetmacro{\sloty}{0.5*\cubey}
    \pgfmathsetmacro{\totalnumslots}{\numslots}
    \pgfmathsetmacro{\slotspace}{\cubez / (\totalnumslots + 1)}
    \pgfmathsetmacro{\islotspaceindex}{1}
    % Loop through the reversed list of slots.
    \foreach \currentslot in \revslots {%
      %\node at (4,0,0) {\currentslot}; % debug
      \IfBeginWith{\currentslot}{v}%
        {% Draw a vector slot.
          \draw[fill=black,ultra thick]
            (\startvslotx,\sloty,-0.5*\cubez+\islotspaceindex*\slotspace) 
            -- ++(0,0,0.04) -- ++(\slotwidth,0,0) -- ++(0,0,-0.04) -- cycle;
          % Test to see if we need to fill the slot.
          \IfEndWith{\currentslot}{+}%
            {% Fill the slot.
              \draw[fill=white,thin]
               (\startvslotx+0.1,\sloty,-0.5*\cubez+\islotspaceindex*\slotspace) 
               -- ++(0,\slotwidth,0) -- ++(\slotwidth-0.2,0,0) -- ++(0,-\slotwidth,0);
            }%
            {% Leave it empty.
            }%
        }%
        {% Draw a 1-form slot.
          \draw[fill=black,ultra thick]
            (\startoslotx,\sloty,-0.5*\cubez+\islotspaceindex*\slotspace) 
            -- ++(0,0,0.04) -- ++(\slotwidth,0,0)-- ++(0,0,-0.04) -- cycle; 
          % Test to see if we need to fill the slot.
          \IfEndWith{\currentslot}{+}%
            {% Fill the slot.
              \draw[fill=lightgray,thin]
                (\startoslotx+0.1,\sloty,-0.5*\cubez+\islotspaceindex*\slotspace) 
                -- ++(0,\slotwidth,0) -- ++(\slotwidth-0.2,0,0) -- ++(0,-\slotwidth,0);
            }%
            {% Leave it empty.
            }%
        }%
      \pgfmathparse{\islotspaceindex+1}
      \xdef\islotspaceindex{\pgfmathresult}
    }%
    % Test to see if we need to fill the output slot.
    \ifthenelse{\equal{\numslots}{\numfilledslots}}%
    {% Fill the output slot.
      \draw[fill=white,thin,-] (-0.5*\slotwidth+0.1,0,0.5*\cubez) 
         -- ++(-0.25,-\slotwidth,0) -- ++(\slotwidth-0.2,0,0) 
         -- ++(0.25,\slotwidth,0) 
         node at (-0.125*\slotwidth,-0.5*\cubey,0.5*\cubez) {\(\symbb{R}\)};
    }%
    {% Leave it empty.
    }%
  }%

\end{tikzpicture} }%

\begin{document} [ \TensorMachine{v,o}[T][e] ] %[ %\TensorMachine{o+,v+}[T][e'] %] %[ %\TensorMachine{o+,o+,v+}[T] %] %[ %\TensorMachine{}[\phi] %] \end{document}

The output:

MWE Output

  • A couple of points: your code doesn't work, but probably that's part of why you're asking the question. However I'm not sure what your code is supposed to achieve. Can you explain briefly what is your desired result? – Alenanno Nov 07 '20 at 23:09
  • Strange. It worked for me. The picture shows the intended result. Let me see if I can explain why it's not working for you. – LaTeXereXeTaL Nov 07 '20 at 23:17
  • Interestingly enough if I only change the command to \TensorMachine{}[T][e] then it works and I get a different result. :D Like a piece of paper coming out of the box with the math R on it. – Alenanno Nov 07 '20 at 23:24
  • I just replaced the code and I recompiled the example and it assuredly worked for me. Yes, if you don't provide anything in braces the box represents a real number. – LaTeXereXeTaL Nov 07 '20 at 23:28
  • Change it to TensorMachine{v+,o+}[T][e] and see what happens. – LaTeXereXeTaL Nov 07 '20 at 23:31
  • the code works fine for me. But I wouldn't write a single definition going over 90 lines. I would try to write smaller building blocks. – Ulrike Fischer Nov 07 '20 at 23:31
  • The code works perfectly for me. The issue is I would like to eliminate use of the ifthen package. I don't know how to make the drawing code more compact. – LaTeXereXeTaL Nov 07 '20 at 23:33

1 Answers1

3

I rewrite your code in LaTeX3. Now it does not rely on ifthen anymore. I also improved the placement of slots.

enter image description here

\documentclass{article}
\RequirePackage{unicode-math}
\unimathsetup{math-style=ISO}
\setmathfont[Scale=MatchLowercase]{TeX Gyre DejaVu Math} % Good g everywhere. Based on 
\setmathfont[Scale=MatchLowercase,range={\mathscr,\mathbfscr}]{XITS Math}
\setmathfont[Scale=MatchLowercase,range={\mathcal,\mathbfcal},StylisticSet=1]{XITS Math}
\setmathfont[Scale=MatchLowercase,range=    it/{greek,Greek}]{Latin Modern Math}
\setmathfont[Scale=MatchLowercase,range=  bfit/{greek,Greek}]{Latin Modern Math}
\setmathfont[Scale=MatchLowercase,range=    up/{greek,Greek}]{Latin Modern Math}
\setmathfont[Scale=MatchLowercase,range=  bfup/{greek,Greek}]{Latin Modern Math}
\setmathfont[Scale=MatchLowercase,range=bfsfup/{greek,Greek}]{Latin Modern Math}

\usepackage{tikz} \usepackage{xstring} % needed for \StrCount \usepackage{ifthen}

\ExplSyntaxOn

% temp variables \clist_new:N \g_tm_slot_clist \clist_new:N \l_tm_tmpa_clist \seq_new:N \l_tm_tmpa_seq \seq_new:N \l_tm_tmpb_seq \tl_new:N \l_tm_tmpa_tl \str_new:N \l_tm_tmpa_str \str_new:N \l_tm_tmpb_str \str_new:N \l_tm_tmpc_str \bool_new:N \l_tm_tmpa_bool \dim_new:N \g_tm_tmpa_dim \dim_new:N \g_tm_tmpb_dim \int_new:N \l_tm_tmpa_int \int_new:N \l_tm_tmpb_int

% ------drawing parameters \dim_new:N \g_tm_cube_width_dim \dim_new:N \g_tm_cube_height_dim \dim_new:N \g_tm_cube_perspective_shift_x_dim \dim_new:N \g_tm_cube_perspective_shift_y_dim

\dim_gset:Nn \g_tm_cube_width_dim {3.5cm} \dim_gset:Nn \g_tm_cube_height_dim {1cm} \dim_gset:Nn \g_tm_cube_perspective_shift_x_dim {1.5cm} \dim_gset:Nn \g_tm_cube_perspective_shift_y_dim {1cm}

\fp_new:N \g_tm_slot_width_coef_fp \fp_new:N \g_tm_slot_height_coef_fp

% width and height of slots \fp_gset:Nn \g_tm_slot_width_coef_fp {0.2} \fp_gset:Nn \g_tm_slot_height_coef_fp {0.08}

% width and height of filled regions \fp_new:N \g_tm_fill_width_coef_fp \fp_new:N \g_tm_fill_height_coef_fp

\fp_gset:Nn \g_tm_fill_width_coef_fp {0.18} \fp_gset:Nn \g_tm_fill_height_coef_fp {0.9}

% slot fill parameter \fp_new:N \g_tm_slot_fill_start_coef_fp \fp_new:N \g_tm_slot_fill_end_coef_fp

\fp_gset:Nn \g_tm_slot_fill_start_coef_fp {0.15} \fp_gset:Nn \g_tm_slot_fill_end_coef_fp {0.85} % ------end drawing parameters

% compute important points/vectors and store them in the array % values are initialized with 0, so we dont need to change everything % each side takes 6 values % origin_x, origin_y; right_x, right_y; up_x, up_y \fparray_new:Nn \g_tm_point_fparr {18} % front side \fparray_gset:Nnn \g_tm_point_fparr {2} {\fp_eval:n {-\g_tm_cube_height_dim}} \fparray_gset:Nnn \g_tm_point_fparr {3} {\fp_eval:n {\g_tm_cube_width_dim}} \fparray_gset:Nnn \g_tm_point_fparr {6} {\fp_eval:n {\g_tm_cube_height_dim}} % top side \fparray_gset:Nnn \g_tm_point_fparr {9} {\fp_eval:n {\g_tm_cube_width_dim}} \fparray_gset:Nnn \g_tm_point_fparr {11} {\fp_eval:n {\g_tm_cube_perspective_shift_x_dim}} \fparray_gset:Nnn \g_tm_point_fparr {12} {\fp_eval:n {\g_tm_cube_perspective_shift_y_dim}} % right side \fparray_gset:Nnn \g_tm_point_fparr {13} {\fp_eval:n {\g_tm_cube_width_dim}} \fparray_gset:Nnn \g_tm_point_fparr {14} {\fp_eval:n {-\g_tm_cube_height_dim}} \fparray_gset:Nnn \g_tm_point_fparr {15} {\fp_eval:n {\g_tm_cube_perspective_shift_x_dim}} \fparray_gset:Nnn \g_tm_point_fparr {16} {\fp_eval:n {\g_tm_cube_perspective_shift_y_dim}} \fparray_gset:Nnn \g_tm_point_fparr {18} {\fp_eval:n {\g_tm_cube_height_dim}}

% o_x, o_y, a_x, a_y, b_x, b_y, c, d % output: o + ca + db \cs_set:Npn \tm_interp:nnnnnnnn #1#2#3#4#5#6#7#8 { \fp_eval:n {(#1) + (#7) * (#3) + (#8) * (#5)}pt , \fp_eval:n {(#2) + (#7) * (#4) + (#8) * (#6)}pt }

\cs_set:Npn \tm_indexed_interp:nnnnnnnn #1#2#3#4#5#6#7#8 { \tm_interp:nnnnnnnn {\fparray_item:Nn \g_tm_point_fparr {#1}} {\fparray_item:Nn \g_tm_point_fparr {#2}} {\fparray_item:Nn \g_tm_point_fparr {#3}} {\fparray_item:Nn \g_tm_point_fparr {#4}} {\fparray_item:Nn \g_tm_point_fparr {#5}} {\fparray_item:Nn \g_tm_point_fparr {#6}} {#7} {#8} }

\cs_set:Npn \tm_front_interp:nn #1#2 { \tm_indexed_interp:nnnnnnnn {1}{2}{3}{4}{5}{6}{#1}{#2} } \cs_set:Npn \tm_top_interp:nn #1#2 { \tm_indexed_interp:nnnnnnnn {7}{8}{9}{10}{11}{12}{#1}{#2} } \cs_set:Npn \tm_right_interp:nn #1#2 { \tm_indexed_interp:nnnnnnnn {13}{14}{15}{16}{17}{18}{#1}{#2} }

% center_x, center_y, c, d \cs_set:Npn \tm_top_fill_interp:nn #1#2 { \tm_interp:nnnnnnnn {\g_tm_tmpa_dim - 0.5 * \g_tm_fill_width_coef_fp * \g_tm_cube_width_dim} {\g_tm_tmpb_dim} {\g_tm_fill_width_coef_fp * \g_tm_cube_width_dim} {0} {0} {\g_tm_fill_height_coef_fp * \g_tm_cube_height_dim} {#1} {#2} }

\cs_set:Npn \tm_front_fill_interp:nn #1#2 { \tm_interp:nnnnnnnn {\g_tm_tmpa_dim + 0.5 * \g_tm_fill_width_coef_fp * \g_tm_cube_width_dim} {\g_tm_tmpb_dim} {-\g_tm_fill_width_coef_fp * \fparray_item:Nn \g_tm_point_fparr {9}} {-\g_tm_fill_height_coef_fp * \fparray_item:Nn \g_tm_point_fparr {10}} {-\g_tm_fill_width_coef_fp * \fparray_item:Nn \g_tm_point_fparr {11}} {-\g_tm_fill_height_coef_fp * \fparray_item:Nn \g_tm_point_fparr {12}} {#1} {#2} }

\cs_set:Npn \tm_interp_draw:nn #1#2 { \draw[#2] (\use:c {tm_#1_interp:nn} {0}{0}) --(\use:c {tm_#1_interp:nn} {1}{0}) --(\use:c {tm_#1_interp:nn} {1}{1}) --(\use:c {tm_#1_interp:nn} {0}{1}) --(\use:c {tm_#1_interp:nn} {0}{0}); }

% index, total slots, x \cs_set:Npn \tm_top_slot_y:nn #1#2 { \int_case:nnF {#2} { {1} {0.5} {2} {\fp_eval:n {0.3 + (#1) * 0.4}} } { \fp_eval:n { \g_tm_slot_fill_start_coef_fp + (#1) / (#2 - 1) * (\g_tm_slot_fill_end_coef_fp - \g_tm_slot_fill_start_coef_fp) } } }

% sets offset variable and draw the filled region % the fill interp command will be valid until this command is called the next time \cs_set:Npn \tm_fill_interp_draw:nnnn #1#2#3#4 { \clist_set:Nx \l_tm_tmpa_clist {\use:c {tm_#1_interp:nn} {#2} {#3}} \dim_gset:Nn \g_tm_tmpa_dim {\clist_item:Nn \l_tm_tmpa_clist {1}} \dim_gset:Nn \g_tm_tmpb_dim {\clist_item:Nn \l_tm_tmpa_clist {2}} \tm_interp_draw:nn {#1_fill} {#4} }

% side, center_x, center_y, style \cs_set:Npn \tm_draw_slot:n #1#2#3#4 { \draw[#4] (\use:c {tm_#1_interp:nn} {#2-0.5\g_tm_slot_width_coef_fp}{#3-0.5\g_tm_slot_height_coef_fp}) --(\use:c {tm_#1_interp:nn} {#2+0.5\g_tm_slot_width_coef_fp}{#3-0.5\g_tm_slot_height_coef_fp}) --(\use:c {tm_#1_interp:nn} {#2+0.5\g_tm_slot_width_coef_fp}{#3+0.5\g_tm_slot_height_coef_fp}) --(\use:c {tm_#1_interp:nn} {#2-0.5\g_tm_slot_width_coef_fp}{#3+0.5\g_tm_slot_height_coef_fp}) --(\use:c {tm_#1_interp:nn} {#2-0.5\g_tm_slot_width_coef_fp}{#3-0.5\g_tm_slot_height_coef_fp}); }

\NewDocumentCommand{\TensorMachine}{ m O{} O{} }{ \clist_gset:Nn \g_tm_slot_clist {#1} \clist_greverse:N \g_tm_slot_clist

\begin{tikzpicture}[baseline] % draw the cube \tm_interp_draw:nn {front} {} \tm_interp_draw:nn {top} {} \tm_interp_draw:nn {right} {} % draw slot in front side \tm_draw_slot:n {front}{0.5}{0.5}{fill=black} % put text \node at (\tm_front_interp:nn {0.2}{0.5}) {$\symbf{#2}$}; \node at (\tm_front_interp:nn {0.8}{0.5}) {$\symbf{#3}$};

\int_set:Nn \l_tm_tmpa_int {0} % number of slots
\int_set:Nn \l_tm_tmpb_int {0} % number of filled slots

% group v's and o's in different sequences
\seq_clear:N \l_tm_tmpa_seq % store v's
\seq_clear:N \l_tm_tmpb_seq % store n's
\clist_map_variable:NNn \g_tm_slot_clist \l_tm_tmpa_tl {
  \tl_set:Nx \l_tm_tmpa_tl {\exp_args:NV \tl_trim_spaces:n \l_tm_tmpa_tl}
  \str_set:Nx \l_tm_tmpa_str {\tl_head:N \l_tm_tmpa_tl}
  \str_case:Vn \l_tm_tmpa_str {
    {v} {\seq_put_right:NV \l_tm_tmpa_seq \l_tm_tmpa_tl}
    {o} {\seq_put_right:NV \l_tm_tmpb_seq \l_tm_tmpa_tl}
  }
}

% draw v slots
\int_step_inline:nn {\seq_count:N \l_tm_tmpa_seq} {
  \int_incr:N \l_tm_tmpa_int % increment #slots
  \str_set:Nx \l_tm_tmpa_str {\seq_item:Nn \l_tm_tmpa_seq {##1}}
  \str_set:Nx \l_tm_tmpb_str {\str_item:Nn \l_tm_tmpa_str {2}}
  \str_if_eq:VnTF \l_tm_tmpb_str {+} {\bool_set_true:N \l_tm_tmpa_bool} {\bool_set_false:N \l_tm_tmpa_bool}
  % get y location
  \tl_set:Nx \l_tm_tmpa_tl {\tm_top_slot_y:nn {\seq_count:N \l_tm_tmpa_seq - ##1}{\seq_count:N \l_tm_tmpa_seq}}
  \tm_draw_slot:n {top}{0.25}{\l_tm_tmpa_tl}{fill=black}
  \bool_if:NT \l_tm_tmpa_bool {
    \int_incr:N \l_tm_tmpb_int % increment #filled slots
    \tm_fill_interp_draw:nnnn {top} {0.25} {\l_tm_tmpa_tl} {fill=gray}
  }
}

% draw o slots
\int_step_inline:nn {\seq_count:N \l_tm_tmpb_seq} {
  \int_incr:N \l_tm_tmpa_int % increment #slots
  \str_set:Nx \l_tm_tmpa_str {\seq_item:Nn \l_tm_tmpb_seq {##1}}
  \str_set:Nx \l_tm_tmpb_str {\str_item:Nn \l_tm_tmpa_str {2}}
  \str_if_eq:VnTF \l_tm_tmpb_str {+} {\bool_set_true:N \l_tm_tmpa_bool} {\bool_set_false:N \l_tm_tmpa_bool}
  % get y location
  \tl_set:Nx \l_tm_tmpa_tl {\tm_top_slot_y:nn {\seq_count:N \l_tm_tmpb_seq - ##1}{\seq_count:N \l_tm_tmpb_seq}}
  \tm_draw_slot:n {top}{0.75}{\l_tm_tmpa_tl}{fill=black}
  \bool_if:NT \l_tm_tmpa_bool {
    \int_incr:N \l_tm_tmpb_int % increment #filled slots
    \tm_fill_interp_draw:nnnn {top} {0.75} {\l_tm_tmpa_tl} {fill=white}
  }
}

\int_compare:nNnT {\l_tm_tmpa_int} = {\l_tm_tmpb_int} {
  \tm_fill_interp_draw:nnnn {front}{0.5}{0.5}{fill=white}
  \node at (\tm_front_fill_interp:nn {0.5}{0.5}) {$\mathbb{R}$};
}

\end{tikzpicture} }

\ExplSyntaxOff

\begin{document}

[ \TensorMachine{v,o}[T][e] ] [ \TensorMachine{o+,v+}[T][e'] ] [ \TensorMachine{o+,o+,v+}[T] ] [ \TensorMachine{o+,o+,o,v+}[T][e] ] [ \TensorMachine{o+,o+,o,v+,v+,v,v+,v+}[T][e] ] [ \TensorMachine{}[\phi] ] \end{document}

Alan Xiang
  • 5,227
  • 1
    This is AMAZING. I was under the impression that LaTeX3 code couldn't be used inside tikzpicture due to catcode issues but obviously that is incorrect, at least here. it will take a while to understand all of this. – LaTeXereXeTaL Nov 08 '20 at 17:35
  • 2
    @LaTeXereXeTaL Basically, I create several interp functions that compute the coordinates on each side given parameterized coordinates in range [0,1]. The draw functions are used to render rectangles; the \tm_top_slot_y:nn functions is used to determine the placement of (multiple) slots. – Alan Xiang Nov 08 '20 at 17:46