5

The (tikz-based) pictures I create are often modular. Therefore it would be ideal to use the same source for different variants of of picture.

Ideally this would be based on standalone, since this is what I am already using, and provide a new environment which defines conditional blocks (I've named the environment conditionally below). Those conditional code blocks could be enabled via a includestandalone option:

main.tex:

\documentclass{article}

\usepackage{standalone} \usepackage{tikz}

\begin{document} \includestandalone[enableall]{figure}

\includestandalone[enable=detour]{figure}

\includestandalone[enable={detour,longer-detour}]{figure}

\end{document}

figure.tex:

\documentclass{standalone}

\usepackage{tikz}

\begin{document} \begin{tikzpicture}

\pgfmathsetmacro{\myRadius}{2}

\coordinate (loop-start) at (-170:\myRadius);

\draw[ ->, line width=7pt, ] (loop-start) arc (-170:170:\myRadius) coordinate[pos=0.75] (detour-start) coordinate[pos=0.25] (detour-end) coordinate[pos=0.27] (detour-end-2) ;

\begin{conditionally}{detour} \draw[ ->, line width=3pt, red, ] (detour-start) to[out=160, in=220, distance=6cm] (detour-end); \end{conditionally}

\begin{conditionally}{longer-detour} \draw[ ->, line width=3pt, red, ] (detour-start) to[out=45, in=-30, distance=10cm] (detour-end-2); \end{conditionally}

\end{tikzpicture} \end{document}

Is something like this possible?

Flow
  • 1,013
  • I think something like https://tex.stackexchange.com/questions/468850/how-to-pass-boolean-value-to-a-tikz-option-key could get you started. – Marijn Dec 21 '23 at 11:11
  • Maybe use a \pic ... – Jasper Habicht Dec 21 '23 at 11:23
  • Thanks, but this is based on tikz's pic mechanism, which, I think, isn't suitable for larger figures. – Flow Dec 21 '23 at 11:23
  • 1
    Another option is to have subtikz --- I have an implementation for subcircuits here: https://texdoc.org/serve/circuitikzmanual.pdf/0#subsection.3.4 , but that is not specific to circuitikz at all. Not the most elegant implementation, I suppose, but could give some hint. It was added here – Rmano Dec 21 '23 at 11:25

2 Answers2

7

This solution enables the target syntax for the standalone file containing the tikzpicture, but uses a custom command in the main file.

More specifically, conditionally is an environment which takes a single, mandatory argument.

  • The argument should consist of a simple tag/label used to identify the contents.
  • In the example, this is used as part of a tikzpicture, but the solution doesn't depend on this. The same mechanism could equally be used to include a selection of quotations, say, or arbitrary code snippets.

\flowconditional[<key-value list>]{<filename>} is used in the main document to select snippets by tag/label from <filename>.

  • Only one key is currently defined, enable.
  • enable takes a comma-separated list of values.
  • Each value should be either a tag/label in <filename> or *, where * indicates that all available code should be included.
  • However, this requirement is not enforced: if a value passed to enable is not defined in filename, the value will be silently ignored.

Given the OP's figure file is named \jobname-fig.tex, we can then write

\flowconditionalpic[enable=*]{\jobname-fig}

\flowconditionalpic[enable=detour]{\jobname-fig}

\flowconditionalpic[enable={detour,longer-detour}]{\jobname-fig}

to produce the target behaviour.

target output

The same framework can be used to include or not include tabular environments (though including sets of rows would require a bit more work). For example,

\begin{table}
  \caption{Tables rather than pictures}
  \flowconditional[enable=tab1]{\jobname-tab}
  \flowconditional[enable=tab2]{\jobname-tab}
\end{table}

\begin{table} \caption{Order is irrelevant} \flowconditional[enable=*]{\jobname-tab} \flowconditional[enable={tab2,tab1}]{\jobname-tab} \end{table}

where \jobname-tab.tex contains

\documentclass{standalone}
\usepackage{array}
\usepackage{booktabs}
\begin{document}
\sffamily
\begin{conditionally}{tab1}
  \begin{tabular} {ll}
    \toprule
    \bfseries Column 1 & \bfseries Column 2 \\
    \midrule
    Row 1 & Cell A \\
    \bottomrule
  \end{tabular}
\end{conditionally}
\begin{conditionally}{tab2}
  \begin{tabular} {ll}
    \toprule
    \bfseries Column 1 & \bfseries Column 2 \\
    \midrule
    Row 1 & Cell A \\
    Row 2 & Cell B \\
    \bottomrule
  \end{tabular}
\end{conditionally}
\end{document}

same idea with tabulars rather than parts of a tikzpicture

Complete code:

\begin{filecontents}[overwrite]{\jobname-fig.tex}
\documentclass{standalone}

\usepackage{tikz}

\begin{document} \begin{tikzpicture}
\pgfmathsetmacro{\myRadius}{2} \coordinate (loop-start) at (-170:\myRadius);
\draw[->,line width=7pt,] (loop-start) arc (-170:170:\myRadius) coordinate[pos=0.75] (detour-start) coordinate[pos=0.25] (detour-end) coordinate[pos=0.27] (detour-end-2) ;
\begin{conditionally}{detour} \draw[ ->, line width=3pt, red, ] (detour-start) to[out=160, in=220, distance=6cm] (detour-end); \end{conditionally}
\begin{conditionally}{longer-detour} \draw[ ->, line width=3pt, red, ] (detour-start) to[out=45, in=-30, distance=10cm] (detour-end-2); \end{conditionally} \end{tikzpicture} \end{document} \end{filecontents} \begin{filecontents}[overwrite]{\jobname-tab.tex} \documentclass{standalone} \usepackage{array} \usepackage{booktabs} \begin{document} \sffamily \begin{conditionally}{tab1} \begin{tabular} {ll} \toprule \bfseries Column 1 & \bfseries Column 2 \ \midrule Row 1 & Cell A \ \bottomrule \end{tabular} \end{conditionally} \begin{conditionally}{tab2} \begin{tabular} {ll} \toprule \bfseries Column 1 & \bfseries Column 2 \ \midrule Row 1 & Cell A \ Row 2 & Cell B \ \bottomrule \end{tabular} \end{conditionally} \end{document} \end{filecontents}

\documentclass{article} % ateb: https://tex.stackexchange.com/a/705487/ addaswyd o gwestiwn Flow: https://tex.stackexchange.com/q/705458/ \usepackage[subpreambles]{standalone} \usepackage{tikz} \ExplSyntaxOn \bool_new:N \l_flow_condition_bool \bool_set_false:N \l_flow_condition_bool \keys_define:nn { flow } { enable .clist_set:N = \l_flow_conditions_clist, enable .initial:n = , enable .default:n = , } \NewDocumentEnvironment { conditionally } { m +b } { \clist_if_in:NnT \l_flow_conditions_clist { #1 } { \bool_set_true:N \l_flow_condition_bool } \clist_if_in:NnT \l_flow_conditions_clist { * } { \bool_set_true:N \l_flow_condition_bool } \bool_if:NT \l_flow_condition_bool { #2 } }{ } \NewDocumentCommand \flowconditional { O {} m } { \group_begin: \keys_set:nn { flow } { #1 } \includestandalone { #2 } \group_end: } \ExplSyntaxOff

\begin{document} \flowconditional[enable=*]{\jobname-fig}

\flowconditional[enable=detour]{\jobname-fig}

\flowconditional[enable={detour,longer-detour}]{\jobname-fig}

\begin{table} \caption{Tables rather than pictures} \flowconditional[enable=tab1]{\jobname-tab} \flowconditional[enable=tab2]{\jobname-tab} \end{table}

\begin{table} \caption{Order is irrelevant} \flowconditional[enable=*]{\jobname-tab} \flowconditional[enable={tab2,tab1}]{\jobname-tab} \end{table}

\end{document}

Edit

The following adds an additional key standalone options. Initially, this is set to mode = tex. Using the key without a value will empty the options. Using the key with a value will set the options to that value. In the example below, the initial value is applied to most cases, with angle=90 being used to demonstrate the effect of changing the option.

\documentclass{article}
% ateb: https://tex.stackexchange.com/a/705487/ addaswyd o gwestiwn Flow: https://tex.stackexchange.com/q/705458/
\usepackage[subpreambles]{standalone}
\usepackage{tikz}
\ExplSyntaxOn
\bool_new:N \l_flow_condition_bool
\bool_set_false:N \l_flow_condition_bool
\keys_define:nn { flow }
{
  enable .clist_set:N = \l_flow_conditions_clist,
  enable .initial:n = ,
  enable .default:n = ,
  standalone ~ options .tl_set:N = \l_flow_standalone_tl,
  standalone ~ options .default:n = ,
  standalone ~ options .initial:n = {mode = tex},
}
\NewDocumentEnvironment { conditionally } { m +b }
{
  \clist_if_in:NnT \l_flow_conditions_clist { #1 }
  { \bool_set_true:N \l_flow_condition_bool } 
  \clist_if_in:NnT \l_flow_conditions_clist { * }
  { \bool_set_true:N \l_flow_condition_bool } 
  \bool_if:NT \l_flow_condition_bool { #2 }
}{  }
\NewDocumentCommand \flowconditional { O {} m }
{
  \group_begin:
    \keys_set:nn { flow } { #1 }
    \flow_includestandalone:Vn \l_flow_standalone_tl { #2 }
  \group_end:
}
\cs_new_protected:Nn \flow_includestandalone:nn
{
  \includestandalone [ #1 ] { #2 }
}
\cs_generate_variant:Nn \flow_includestandalone:nn { Vn }
\ExplSyntaxOff

\begin{document} \flowconditional[enable=*]{\jobname-fig}

\flowconditional[enable=detour,standalone options={angle=90}]{\jobname-fig}

\flowconditional[enable={detour,longer-detour}]{\jobname-fig}

\begin{table} \caption{Tables rather than pictures} \flowconditional[enable=tab1]{\jobname-tab} \flowconditional[enable=tab2]{\jobname-tab} \end{table}

\begin{table} \caption{Order is irrelevant} \flowconditional[enable=*]{\jobname-tab} \flowconditional[enable={tab2,tab1}]{\jobname-tab} \end{table}

\end{document}

effect of setting `standalone options={angle=90}

cfr
  • 198,882
  • Thanks for your answer. It looks good, only two minor remarks. It should be possible to pass optional arguments to \includestandalone{}. Furthermore, either \includestandalone{} should always be invoked with mode=tex, or it should be modified so that it knows that result is a variant of the picture, and hence use a different name for the cached pictured if mode is e.g., mode=buildnew. Otherwise, standalone's result caching may causes the usage of the wrong picture to be included. – Flow Dec 22 '23 at 07:56
  • @Flow See edit. If you want mode=tex always, that's probably best set in the preamble. Instead, I've set it as the initial value of the key standalone options now accepted by \flowconditional. It can be overridden by using the key without a value or with any different value. (Options are not cumulative.) – cfr Dec 22 '23 at 16:22
  • You do not want mode=tex always, however, with the current design of the standalone package, you need it for figures that make use of conditionally if you use the caching feature of standalone (e.g., mode=buildnew). Otherwise, standalone may select the wrong cached picture. I have created https://github.com/MartinScharrer/standalone/issues/11 where I ask Martin to add support for conditionally to standalone, which would avoid this issue. We only need a place where the conditionally environment and it's auxiliary code is defined. @cfr do you want to publish your code on ctan? – Flow Dec 30 '23 at 13:28
  • one more feature request: if the standalone document is build, it should be possible, probably by default, that all conditionally blocks are enabled. You already did an amazing job with the resulting code, I hope you could do this also. Thanks! – Flow Dec 30 '23 at 13:38
  • @Flow You can adjust the initial and/or default values for the keys as you wish. * enables all blocks, so if you want that to be the initial value, just set the initial value to * rather than empty. If you want it to be default, set it as the default value. if you want it for both the default and the initial value, set both to *. Likewise for mode=tex and/or any other standalone options: you can set them as the initial and/or default values according to what makes most sense for your workflow. – cfr Dec 31 '23 at 20:14
  • @Flow Let's see what response you get on GitHub. I think this would be best handled in standalone if you want that kind of integration. – cfr Dec 31 '23 at 20:21
  • Right, standalone should the heavy lifting. However, one could argue that the conditionally block could also exist, and be useful, outside of standalone. Therefore, I wondered if factoring the conditionally block into an extra package would be sensible. In any case, happy new year! :) – Flow Jan 01 '24 at 16:10
  • 1
    @Flow Possibly, but the only known use case right now is with standalone, so let's see what happens there. To generalise it, it would be good to know what cases it should cover since clearly the current mechanism of inclusion depends on standalone. Possibly some kind of generic \file_input:n might be useful? (Personally, I've never used \includestandalone even though I use standalone.) But if standalone gets a bespoke mechanism of its own, for example, it would be good to know that first. And happy New Year! – cfr Jan 03 '24 at 19:13
3

You could use a \pic:

\documentclass{standalone}
\usepackage{tikz}
\begin{document}

\tikzset{ pics/conditional loop/.style={ code={ \tikzset{conditional loop/.cd, #1} \coordinate (-loop-start) at (-170:{\pgfkeysvalueof{/tikz/conditional loop/radius}}) ; \draw[ ->, line width=7pt, ] (-loop-start) arc[ start angle=-170, end angle=170, radius={\pgfkeysvalueof{/tikz/conditional loop/radius}} ] coordinate[pos=0.75] (-detour-start) coordinate[pos=0.25] (-detour-end-short) coordinate[pos=0.27] (-detour-end-long) ; \foreach \o/\i/\d/\e in \conditionallooploopsettings { \draw[ ->, line width=3pt, red, ] (-detour-start) to[out=\o, in=\i, distance=\d] (-\e) ; } } }, conditional loop/radius/.initial={2}, conditional loop/loop settings/.store in=\conditionallooploopsettings, conditional loop/loop settings={}, conditional loop/all/.style={ loop settings={160/220/6cm/detour-end-short,45/-30/10cm/detour-end-long} }, conditional loop/detour/.style={ loop settings={160/220/6cm/detour-end-short} }, conditional loop/longer detour/.style={ loop settings={45/-30/10cm/detour-end-long} } }

\begin{tikzpicture}

\pic at (0,0) {conditional loop};

\pic at (0,-10) {conditional loop={all}};

\pic at (0,-20) {conditional loop={detour}};

\pic at (0,-30) {conditional loop={longer detour}};

\pic at (0,-40) {conditional loop={radius=1cm, all}};

\end{tikzpicture} \end{document}

enter image description here

A variation that also allows for conditional loop={detour, longer detour}:

\documentclass{standalone}
\usepackage{tikz}
\begin{document}

\tikzset{ pics/conditional loop/.style={ code={ \tikzset{conditional loop/.cd, #1} \coordinate (-loop-start) at (-170:{\pgfkeysvalueof{/tikz/conditional loop/radius}}) ; \draw[ ->, line width=7pt, ] (-loop-start) arc[ start angle=-170, end angle=170, radius={\pgfkeysvalueof{/tikz/conditional loop/radius}} ] coordinate[pos=0.75] (-detour-start) coordinate[pos=0.25] (-detour-end-short) coordinate[pos=0.27] (-detour-end-long) ; \pgfkeys{/tikz/conditional loop/draw loops} } }, conditional loop/detour arrow/.style={ ->, line width=3pt, red, }, conditional loop/radius/.initial={2}, conditional loop/loop settings/.store in=\conditionallooploopsettings, conditional loop/draw loops/.initial={}, conditional loop/detour/.style={ draw loops/.append code={ \draw[conditional loop/detour arrow] (-detour-start) to[out=160, in=220, distance=6cm] (-detour-end-short); } }, conditional loop/longer detour/.style={ draw loops/.append code={ \draw[conditional loop/detour arrow] (-detour-start) to[out=34, in=-30, distance=10cm] (-detour-end-long); } }, conditional loop/all/.style={ detour, longer detour } }

\begin{tikzpicture}

\pic at (0,0) {conditional loop};

\pic at (0,-10) {conditional loop={all}};

\pic at (0,-20) {conditional loop={detour}};

\pic at (0,-30) {conditional loop={longer detour}};

\pic at (0,-40) {conditional loop={detour, longer detour}};

\end{tikzpicture} \end{document}

enter image description here