5

My question today is about a suitable way to store data when I want to create a fixed design. So imagine I have a given layout in a TikZ drawing, where I want to have dynamic text. However the user shall not be confronted with the TikZ code, he shall only define the content and I am filling it into TikZ nodes. So here is my example:

\documentclass[tikz]{standalone}

\begin{document}
    \begin{tikzpicture}
    \node(center)[fill=cyan,circle,minimum size=3cm] at (135:10cm) {Topic};
    \foreach \i in {0,...,7}
    {
        \node(bubble)[fill=green,text width=2cm,circle,align=center] at (\i*20:\i*3cm) {\textbf{Heading \i}\\Content};
        \draw (center) -- (bubble);
    }
    \end{tikzpicture}
\end{document}

So my nodes are set, but what is a suitable way of providing data beforehand? Of course I could just define a macro for each node, as in:

\newcommand{\headingone}{Heading 1}
\newcommand{\contentone}{Content 1}

And then use these macros to read the data. But isn't there a simpler way which is more data driven. As the diagram should not dynamically grow, I don't want to create an environment with e.g. items, even though this looks like a possible approach to me. But isn't there a smarter way of doing it?

3
  • 1
    Maybe create two clists and push the data in there, then retrieve it inside the \foreach loop? Something like this? Commented Jul 27, 2022 at 22:05
  • If you are trying to create a truly interactive document, you might use textfields from eforms. Commented Jul 28, 2022 at 20:45
  • @JohnKormylo if you mean ctan.mirror.norbert-ruehl.de/macros/latex/contrib/acrotex/doc/… then this is for interactive PDFs, but this is not, what I am looking for or I misunderstand your comment.
    – TobiBS
    Commented Jul 29, 2022 at 20:56

2 Answers 2

7

I don't exactly know what you are after, but you could create two clists and store the data there. In your tikzpicture, you can then retrieve the data inside the \foreach loop.

The exact implementation depends on the kind of data that you want to store, but for simple strings it could work like this:

\documentclass[tikz]{standalone}

\ExplSyntaxOn
\clist_new:N \l_mypic_headers_clist 
\clist_new:N \l_mypic_contents_clist 

\NewDocumentCommand { \getHeadersAtIndex } { m } {
    \clist_item:Nn \l_mypic_headers_clist { #1 }
}

\NewDocumentCommand { \getContentsAtIndex } { m } {
    \clist_item:Nn \l_mypic_contents_clist { #1 }
}

\NewDocumentCommand { \getMaxIndex } { } {
    \clist_count:N \l_mypic_headers_clist
}

\NewDocumentCommand { \addToLists } { m m } { 
    \clist_put_right:Nn \l_mypic_headers_clist { { #1 } }
    \clist_put_right:Nn \l_mypic_contents_clist { { #2 } }
}
\ExplSyntaxOff

\addToLists{Header 1}{Contents 1}
\addToLists{Header 2}{Contents 2}
\addToLists{Header 3}{Contents 3}
\addToLists{Header 4}{Contents 4}
\addToLists{Header 5}{Contents 5}
\addToLists{Header 6}{Contents 6}
\addToLists{Header 7}{Contents 7}

\begin{document}
    \begin{tikzpicture}
    \node(center)[fill=cyan,circle,minimum size=3cm] at (135:10cm) {Topic};
    \foreach \i in {1,...,\getMaxIndex} {
        \node(bubble)[fill=green,text width=2cm,circle,align=center] at (\i*20:\i*3cm) 
        {\textbf{\getHeadersAtIndex{\i}} \\ \getContentsAtIndex{\i}};
        \draw (center) -- (bubble);
    }
    \end{tikzpicture}
\end{document}

If you need a more robust set-up, you can use squences instead of comma-lists for storing. Just replace every clist by seq in the code above.


Edit

You can add the following function inside the ExplSyntax block:

\NewDocumentCommand { \addMultipleToLists } { m } { 
    \clist_map_inline:nn { #1 } {
        \clist_put_right:Nn \l_mypic_headers_clist { 
            \clist_item:nn { ##1 } { 1 }
        }
        \clist_put_right:Nn \l_mypic_contents_clist { 
            \clist_item:nn { ##1 } { 2 }
        }
    }
}

You can then use a maybe more convenient way to input the data (note that in this case you need to wrap text in additional braces if it contains a comma, and you cannot simply switch to sequences here obviously, since you feed the macro a comma-list in the first place):

\addMultipleToLists{{Header 1,Contents 1},
                    {Header 2,Contents 2},
                    {Header 3,Contents 3},
                    {Header 4,Contents 4},
                    {Header 5,Contents 5},
                    {Header 6,Contents 6},
                    {Header 7,Contents 7}}

enter image description here

4
  • 2
    Thanks Jasper, this looks close to what I was looking for, need to check if it works in my specific setup before accepting the answer however. One thing that I would like to ask, ist it possible to adjust \addToLists{Header 1}{Contents 1} so it accepts a format like \addToLists{{Header 1,Contents 1},{{Header 2,Contents 2},{{Header 3,Contents 3}}} or maybe simpler, but harder to grasp \addToLists{Header 1,Contents 1,Header 2,Contents 2}?
    – TobiBS
    Commented Jul 28, 2022 at 7:53
  • @TobiBS Sure, see my edit. Commented Jul 28, 2022 at 8:19
  • 1
    The names of the variables should have a prefix. I'd not use \clist_push:Nn, but \clist_put_right:Nn. The former is for using a clist as a stack, but it's not how you're employing them.
    – egreg
    Commented Jul 28, 2022 at 9:05
  • Yes, I had \clist_put_right:Nn originally, but wasn't sure what is better. Thanks for pointing to the prefix rule. I changed my answer accordingly. Commented Jul 28, 2022 at 9:13
4

This PGFkeys supported solution supports both lists in the form of

{{Header 1, Content 1}, {Header 2, Content 2}, {…}, …}

as well as

{Header 1, Content 1, Header 2, Content 2, …

The latter is the default list use = all comma, the former can be used with list use = tuples.

For placing the green nodes, I'm using the chains library's

on chain = <chain name> placed {at = (<calculation>)}

where (<calculation>) simply is

(\tikzchaincount*20: \tikzchaincount*3cm)

i.e. the same as yours but \i replaced by \tikzchaincount.

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{chains}
\makeatletter
\tikzset{
  /utils/if not empty/.code 2 args={%
    \if\relax\detokenize{#1}\relax\expandafter\pgfutil@gobble
                             \else\expandafter\pgfutil@firstofone\fi
                             {\pgfkeysalso{#2}}}}
\makeatother
\tikzset{
  % defaults
  topic style/.style={shape=circle, minimum size=3cm, fill=cyan},
  list style/.style={fill=green, text width=2cm, circle, align=center},
  topic/.initial=Topic,
  list/.initial={Header 1, Contents 1,
                 Header 2, Contents 2,
                 Header 3, Contents 3,
                 Header 4, Contents 4,
                 Header 5, Contents 5,
                 Header 6, Contents 6},
  % mantaining the lists/settings
  reset list/.style={list=},
  add to list/.style={list/.append={,#1}},
  list use/.is choice,
  list use/all comma/.style={use list/.style={% H1, C1, H2, C2, …
    list parser/.expand twice/.expand once={\pgfkeysvalueof{/tikz/list},}}},
  list use/tuples/.style={use list/.style={% {H1, C1}, {H2, C2}, …
    placer/.list/.expand twice/.expand once=\pgfkeysvalueof{/tikz/list}}},
  list use=all comma,% default: all comma
  % actual placing
  list parser/.style args={#1,#2,#3}{                   % parsing H1, C1, H2, C2, …
    placer={{#1},{#2}},
    /utils/if not empty={#3}{list parser={#3}}},
  tuples parser/.style args={#1,#2}{placer={{#1},{#2}}},% parsing {H1, C1}, {H2, C2}, …
  placer/.code args={#1,#2}{%
    \node [list style, on chain] {\textbf{#1}\\#2} edge (center);}}

\newcommand*\myTikz[1][]{%
\begin{tikzpicture}[#1,
   start chain=headers placed {at=(\tikzchaincount*20:\tikzchaincount*3cm)}]
   \node (center) [topic style] at (135:10cm) {\pgfkeysvalueof{/tikz/topic}};
   \tikzset{use list}
\end{tikzpicture}}

\begin{document}
\myTikz
\myTikz[add to list={Header X, Content X, Header Y, Content Y}]
\myTikz[topic=\TeX, list={TikZ, extends PGF, \LaTeX, extends \TeX}]

\tikzset{
  list use=tuples,
  list={{Header 1, Contents 1},
        {Header 2, Contents 2},
        {Header 3, Contents 3},
        {Header 4, Contents 4},
        {Header 5, Contents 5},
        {Header 6, Contents 6}}}
\myTikz
\myTikz[add to list={{Header X, Content X}, {Header Y, Content Y}}]
\myTikz[topic=\TeX, list={{TikZ, extends PGF}, {\LaTeX, extends \TeX}}]
\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .