Graphics, Figures & TablesHelp With Chains

Information and discussion about graphics, figures & tables in LaTeX documents.
Post Reply
jorsborn
Posts: 12
Joined: Sat Feb 26, 2011 4:17 am

Help With Chains

Post by jorsborn »

I am trying to construct a diagram which represents a virtual menu. The menu can be several levels deep. I've got the main menu (chain trunk) laid out just fine but I want to illustrate a sub menu now (branch testbranch). The PGF/TikZ document seems pretty straightforward in regards to this but I don't get the desired result. Instead of branching off to the right and then downwards (parallel to the main menu) my child entries show up in line with the main menu. If it helps, a look at the log file shows the following errors:
Missing character: There is no [ in font nullfont!
Missing character: There is no s in font nullfont!
Missing character: There is no t in font nullfont!
Missing character: There is no a in font nullfont!
Missing character: There is no r in font nullfont!
Missing character: There is no t in font nullfont!
Missing character: There is no b in font nullfont!
Missing character: There is no r in font nullfont!
Missing character: There is no a in font nullfont!
Missing character: There is no n in font nullfont!
Missing character: There is no c in font nullfont!
Missing character: There is no h in font nullfont!
Missing character: There is no = in font nullfont!
Missing character: There is no t in font nullfont!
Missing character: There is no e in font nullfont!
Missing character: There is no s in font nullfont!
Missing character: There is no t in font nullfont!
Missing character: There is no b in font nullfont!
Missing character: There is no r in font nullfont!
Missing character: There is no a in font nullfont!
Missing character: There is no n in font nullfont!
Missing character: There is no c in font nullfont!
Missing character: There is no h in font nullfont!
Missing character: There is no g in font nullfont!
Missing character: There is no o in font nullfont!
Missing character: There is no i in font nullfont!
Missing character: There is no n in font nullfont!
Missing character: There is no g in font nullfont!
Missing character: There is no r in font nullfont!
Missing character: There is no i in font nullfont!
Missing character: There is no g in font nullfont!
Missing character: There is no h in font nullfont!
Missing character: There is no t in font nullfont!
Missing character: There is no ] in font nullfont!

Code: Select all

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{calc,trees,positioning,arrows,chains,shapes.geometric,%
    decorations.pathreplacing,decorations.pathmorphing,shapes,%
    matrix,shapes.symbols,shadows}
\usepackage{comment}

\tikzset{
>=stealth',
  every join/.style={<->, thick,shorten >=1pt},
  decoration={brace},
  tuborg/.style={decorate},
  tubnode/.style={midway, right=2pt},
}

\tikzstyle{shadedbox} =  [
    rectangle, 
    rounded corners, 
    top color=white,bottom color=black!30,
    draw=black, very thick,
    text width=10em, 
    minimum height=3em, 
    text centered, 
    on chain,
]

\tikzstyle{line} = [
    draw, -latex'
]

\begin{document}

\begin{tikzpicture}
  [node distance=.6cm and 1cm,
  start chain=trunk going below,]
    \node[shadedbox,join,on chain=trunk] (deviceserver) {Device Server};
    \node[shadedbox, join,on chain=trunk] (tests) {System Tests};

    { [start branch=testbranch going right]
      \node[shadedbox,join,on chain=going below](c1){Test Child};
      \node[shadedbox,join,on chain=going below](c2){Test Child};
    }  

    \node[shadedbox, join, on chain=trunk] (status) {System Status};
    \node[shadedbox, join, on chain=trunk] (settings) {System Settings};
    \node[shadedbox, join, on chain=trunk] (lock) {Lock};
    \node[shadedbox, join, on chain=trunk] (exit) {Exit};


  \path [line,dotted] (exit.east) -- ($(exit.east)+(.5cm,0)$) -- node[midway]{return} ($(trunk-begin.east)+(.5cm,0)$) -- (trunk-begin.east) ;

\end{tikzpicture}

\end{document}

My second issue is that I want the main menu to continue in line but spaced down from the last child node by node distance. So, the main menu would traverse down, the child would go right (then downward),the main menu would then continue but there would be a large space in between the next main menu node and the last (which diverts to the child node) I would imagine that I could override the default node distance specified when I started the chain by simply adding node distance=$\tikzchainprevious+.6cm$. I can't really test this as the above doesn't render the child nodes correctly. However, hardcoding a large node distance in on a particular node doesn't seem to have any effect.

Thoughts?
Last edited by jorsborn on Mon Mar 14, 2011 5:12 am, edited 2 times in total.

Recommended reading 2024:

LaTeXguide.org • LaTeX-Cookbook.net • TikZ.org

NEW: TikZ book now 40% off at Amazon.com for a short time.

And: Currently, Packt sells ebooks for $4.99 each if you buy 5 of their over 1000 ebooks. If you choose only a single one, $9.99. How about combining 3 LaTeX books with Python, gnuplot, mathplotlib, Matlab, ChatGPT or other AI books? Epub and PDF. Bundle (3 books, add more for higher discount): https://packt.link/MDH5p

gmedina
Posts: 2313
Joined: Wed Jul 11, 2007 11:45 pm

Help With Chains

Post by gmedina »

Hi,

the code you posted compiles with no errors on my system. If I understood your question correctly, then all you have to do is to use a scope environment for branching the main chain; something like this:

Code: Select all

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,trees,positioning,arrows,chains,shapes.geometric,%
    decorations.pathreplacing,decorations.pathmorphing,shapes,%
    matrix,shapes.symbols,shadows}
\usepackage{comment}

\tikzset{
>=stealth',
  every join/.style={<->, thick,shorten >=1pt},
  decoration={brace},
  tuborg/.style={decorate},
  tubnode/.style={midway, right=2pt},
}

\tikzstyle{shadedbox} =  [
    rectangle, 
    rounded corners, 
    top color=white,bottom color=black!30,
    draw=black, very thick,
    text width=10em, 
    minimum height=3em, 
    text centered, 
    on chain,
]

\tikzstyle{line} = [
    draw, -latex'
]

\begin{document}

\begin{tikzpicture}
  [node distance=.6cm and 1cm,
  start chain=trunk going below,]
    \node[shadedbox,join,on chain=trunk] (deviceserver) {Device Server};
    \node[shadedbox, join,on chain=trunk] (tests) {System Tests};

    \begin{scope} [start branch=testbranch going right]
      \node[shadedbox,join,on chain=going right](c1){Test Child};
      \node[shadedbox,join,on chain=going below](c2){Test Child};
    \end{scope}  

    \node[shadedbox, join, on chain=trunk] (status) {System Status};
    \node[shadedbox, join, on chain=trunk] (settings) {System Settings};
    \node[shadedbox, join, on chain=trunk] (lock) {Lock};
    \node[shadedbox, join, on chain=trunk] (exit) {Exit};


  \path [line,dotted] (exit.east) -- ($(exit.east)+(.5cm,0)$) -- node[midway]{return} ($(trunk-begin.east)+(.5cm,0)$) -- (trunk-begin.east) ;

\end{tikzpicture}

\end{document}
Here's the resulting chain:
chain.png
chain.png (18.06 KiB) Viewed 7733 times
1,1,2,3,5,8,13,21,34,55,89,144,233,...
jorsborn
Posts: 12
Joined: Sat Feb 26, 2011 4:17 am

Help With Chains

Post by jorsborn »

Perfect, that worked and the errors in my log file have disappeared. Any idea why the manual doesn't state this? I was pretty much following the branch example on page 289 of the manual http://www.ctan.org/tex-archive/graphic ... manual.pdf. They make no mention of the scope environment in the example.

I still have the second issue. How do I override the node distance specified in start chain. Take the following example:

Code: Select all

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,trees,positioning,arrows,chains,shapes.geometric,%
    decorations.pathreplacing,decorations.pathmorphing,shapes,%
    matrix,shapes.symbols,shadows}
\usepackage{comment}

\tikzset{
>=stealth',
  every join/.style={<->, thick,shorten >=1pt},
  decoration={brace},
  tuborg/.style={decorate},
  tubnode/.style={midway, right=2pt},
}

\tikzstyle{shadedbox} =  [
    rectangle, 
    rounded corners, 
    top color=white,bottom color=black!30,
    draw=black, very thick,
    text width=10em, 
    minimum height=3em, 
    text centered, 
    on chain,
]

\tikzstyle{line} = [
    draw, -latex'
]

\begin{document}

\begin{tikzpicture}
  [node distance=.6cm and 1cm,
  start chain=trunk going below,]
    \node[shadedbox,join,on chain=trunk] (deviceserver) {Device Server};
    \node[shadedbox, join,on chain=trunk] (tests) {System Tests};

    \begin{scope} [start branch=testbranch going right]
      \node[shadedbox,join,on chain](c1){Test Child};
      \node[shadedbox,join,on chain=going below](c2){Test Child};
        \begin{scope} [start branch=testbranchtwo going right]
          \node[shadedbox,join,on chain](ca){Test Child};
          \node[shadedbox,join,on chain=going below](cb){Final Test Child};
        \end{scope}  
    \end{scope}  

    \node[shadedbox, join, on chain=trunk] (status) {System Status};
    \node[shadedbox, join, on chain=trunk] (settings) {System Settings};
    \node[shadedbox, join, on chain=trunk] (lock) {Lock};
    \node[shadedbox, join, on chain=trunk] (exit) {Exit};

  \path [line,dotted] (exit.east) -- ($(exit.east)+(.5cm,0)$) -- node[midway]{return} ($(trunk-begin.east)+(.5cm,0)$) -- (trunk-begin.east) ;
  \path [line,dotted] (c2.east) -- ($(c2.east)+(.5cm,0)$) -- node[midway]{return} ($(c1.east)+(.5cm,0)$) -- (c1.east) ;
  \path [line,dotted] (cb.east) -- ($(cb.east)+(.5cm,0)$) -- node[midway]{return} ($(ca.east)+(.5cm,0)$) -- (ca.east) ;

\end{tikzpicture}

\end{document}
In the output I want the 'System Status' menu to start .6cm below the 'Final Test Child' element. The result would be a large gap between 'System Status' and 'System Tests'. It seems like I could simply change to...

Code: Select all

\node[shadedbox, join, on chain=trunk] (status) at ($(cb.south)+(0,.6cm)$) {System Status};
... but that places it below 'Final Test Child'. Is there a way to use just the y coordinate of cb.south?
Attachments
Capture.PNG
Capture.PNG (25.27 KiB) Viewed 7728 times
User avatar
gmedina
Posts: 2313
Joined: Wed Jul 11, 2007 11:45 pm

Help With Chains

Post by gmedina »

jorsborn wrote:Perfect, that worked and the errors in my log file have disappeared. Any idea why the manual doesn't state this? I was pretty much following the branch example on page 289 of the manual http://www.ctan.org/tex-archive/graphic ... manual.pdf. They make no mention of the scope environment in the example.
The first example on branching chains (see Section 5.4.2 Branching and Joining a Chain) mentions the use of the scope environment. I will take a look at the example on page 289 (in which scope is not used anymore and it's not even mentioned) and let you know what I found. Note: (I,ve found the answer; see Edit below).
jorsborn wrote:I still have the second issue. How do I override the node distance specified in start chain...but that yields coordinate errors. Any thoughts on how I could achieve this?
You can use the yshift option (with a suitable value) for the "System Status" node:

Code: Select all

    \node[shadedbox, join, on chain=trunk,yshift=-3.2cm] (status) {System Status};
Edit: in the example on page 289 the scope shorthand is used and that requires the scopes library which you didn't load. Your original code would be something like tis (using the scope shorthand instead of the scope environment):

Code: Select all

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{calc,trees,positioning,arrows,chains,shapes.geometric,%
    decorations.pathreplacing,decorations.pathmorphing,shapes,%
    matrix,shapes.symbols,shadows,scopes}
\usepackage{comment}

\tikzset{
>=stealth',
  every join/.style={<->, thick,shorten >=1pt},
  decoration={brace},
  tuborg/.style={decorate},
  tubnode/.style={midway, right=2pt},
}

\tikzstyle{shadedbox} =  [
    rectangle, 
    rounded corners, 
    top color=white,bottom color=black!30,
    draw=black, very thick,
    text width=10em, 
    minimum height=3em, 
    text centered, 
    on chain,
]

\tikzstyle{line} = [
    draw, -latex'
]

\begin{document}

\begin{tikzpicture}
  [node distance=.6cm and 1cm,
  start chain=trunk going below,]
    \node[shadedbox,join,on chain=trunk] (deviceserver) {Device Server};
    \node[shadedbox, join,on chain=trunk] (tests) {System Tests};

    { [start branch=testbranch going right]
      \node[shadedbox,join,on chain=going right](c1){Test Child};
      \node[shadedbox,join,on chain=going below](c2){Test Child};
    }  

    \node[shadedbox, join, on chain=trunk] (status) {System Status};
    \node[shadedbox, join, on chain=trunk] (settings) {System Settings};
    \node[shadedbox, join, on chain=trunk] (lock) {Lock};
    \node[shadedbox, join, on chain=trunk] (exit) {Exit};


  \path [line,dotted] (exit.east) -- ($(exit.east)+(.5cm,0)$) -- node[midway]{return} ($(trunk-begin.east)+(.5cm,0)$) -- (trunk-begin.east) ;

\end{tikzpicture}

\end{document}
1,1,2,3,5,8,13,21,34,55,89,144,233,...
jorsborn
Posts: 12
Joined: Sat Feb 26, 2011 4:17 am

Re: Help With Chains

Post by jorsborn »

Thank you!

The yshift transformation works but it has one drawback. I eventually want to script this so that this 'menu map' is created automatically. Given the child depth will vary for each menu item the y-shift value would be different for all of them. Is there anyway to calculate the y-shift value relative to the the cb node?
User avatar
gmedina
Posts: 2313
Joined: Wed Jul 11, 2007 11:45 pm

Help With Chains

Post by gmedina »

jorsborn wrote:Thank you!
Is there anyway to calculate the y-shift value relative to the the cb node?
Of course, you can extract the Y coordinates of the involved nodes and then subtract them to calculate the value for yshift; here's an example using your code:

Code: Select all

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,trees,positioning,arrows,chains,shapes.geometric,%
    decorations.pathreplacing,decorations.pathmorphing,shapes,%
    matrix,shapes.symbols,shadows}
\usepackage{comment}

\tikzset{
>=stealth',
  every join/.style={<->, thick,shorten >=1pt},
  decoration={brace},
  tuborg/.style={decorate},
  tubnode/.style={midway, right=2pt},
}

\tikzstyle{shadedbox} =  [
    rectangle, 
    rounded corners, 
    top color=white,bottom color=black!30,
    draw=black, very thick,
    text width=10em, 
    minimum height=3em, 
    text centered, 
    on chain,
]

\tikzstyle{line} = [
    draw, -latex'
]

% length used to calculate the Y shift
\newlength\YCoordOne
\newlength\YCoordTwo
\newlength\YShift

\begin{document}

\begin{tikzpicture}
  [node distance=.6cm and 1cm,
  start chain=trunk going below,]
    \node[shadedbox,join,on chain=trunk] (deviceserver) {Device Server};
    \node[shadedbox, join,on chain=trunk] (tests) {System Tests};
    % extract Y coordinate of (tests.south)
    \pgfextracty{\YCoordOne}{+(tests.south)}

    \begin{scope} [start branch=testbranch going right,node distance=.6cm and 1cm]
      \node[shadedbox,join,on chain](c1){Test Child};
      \node[shadedbox,join,on chain=going below](c2){Test Child};
        \begin{scope} [start branch=testbranchtwo going right]
          \node[shadedbox,join,on chain](ca){Test Child};
          \node[shadedbox,join,on chain=going below](cb){Final Test Child};
        \end{scope}  
    \end{scope}  

    % extract Y coordinate of (cb.south)
    \pgfextracty{\YCoordTwo}{(cb.south)}

    % calculate the Yshift = subtract the two coordinates
    \setlength\YShift{\YCoordTwo}
    \addtolength\YShift{-\YCoordOne}

    \node[shadedbox, join, on chain=trunk,yshift=\YShift] (status) {System Status};
    \node[shadedbox, join, on chain=trunk] (settings) {System Settings};
    \node[shadedbox, join, on chain=trunk] (lock) {Lock};
    \node[shadedbox, join, on chain=trunk] (exit) {Exit};

  \path [line,dotted] (exit.east) -- ($(exit.east)+(.5cm,0)$) -- node[midway]{return} ($(trunk-begin.east)+(.5cm,0)$) -- (trunk-begin.east) ;
  \path [line,dotted] (c2.east) -- ($(c2.east)+(.5cm,0)$) -- node[midway]{return} ($(c1.east)+(.5cm,0)$) -- (c1.east) ;
  \path [line,dotted] (cb.east) -- ($(cb.east)+(.5cm,0)$) -- node[midway]{return} ($(ca.east)+(.5cm,0)$) -- (ca.east) ;

\end{tikzpicture}

\end{document}
1,1,2,3,5,8,13,21,34,55,89,144,233,...
jorsborn
Posts: 12
Joined: Sat Feb 26, 2011 4:17 am

Re: Help With Chains

Post by jorsborn »

Works like a champ! Thank you so much for helping me out!
jorsborn
Posts: 12
Joined: Sat Feb 26, 2011 4:17 am

Help With Chains

Post by jorsborn »

I'm back!

This problem is still somewhat related so I thought I'd continue it in this post. I am now trying to wrap this into environments and commands. I am almost there but the newlength calcs are giving me issues. I can't seem to get the correct math and it appears the problem might be related to the scope of newlength definitions.

Take a look below. I create a few newlength vars in the menu environment. In each mainmenuitem I store the southern coordinate of the node so I always have the last main menu coordinate available. In each submenuitem I store the southern coordinate of the node so I always have the last child coordinate available as well. In each mainmenuitem I have a test for the need to shift, and if I do need to shift I take the difference in my stored coordinates and calculate a shift value which I apply. Two things...

1) The math is not correct. I am suspecting there is a scope issue. I am setting the coordinate in one environment and using it in another. Does that matter?

2) For debug purposes I have printed out the y coordinate in each of the child nodes. For the two child nodes at the same level, why are the y coordinates different?

Code: Select all

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,trees,positioning,arrows,chains,shapes.geometric,%
    decorations.pathreplacing,decorations.pathmorphing,shapes,%
    matrix,shapes.symbols,shadows}
\usepackage{comment}
\usepackage{ifthen}

\tikzset{
>=stealth',
  every join/.style={<->, thick,shorten >=1pt},
  decoration={brace},
  tuborg/.style={decorate},
  tubnode/.style={midway, right=2pt},
}

\tikzstyle{genericbox} =  [
    rectangle, 
    rounded corners, 
    top color=white,bottom color=black!30,
    draw=black, very thick,
    text width=3cm, 
    minimum height=1cm, 
    text centered, 
]

\tikzstyle{targetbox} =  [
    rectangle, 
    rounded corners, 
    top color=white,bottom color=red!30!black!30,
    draw=black, very thick,
    text width=2cm, 
    minimum height=1cm, 
    text centered, 
]

\tikzstyle{continuationboxtop} =  [
    rectangle, 
    rounded corners, 
    top color=white,bottom color=black!30,
    draw=black, very thick,
    text width=2cm, 
    minimum height=1cm, 
    text centered, 
]

\tikzstyle{continuationboxbottom} =  [
    rectangle, 
    rounded corners, 
    top color=white,bottom color=black!30,
    draw=black, very thick,
    text width=2cm, 
    minimum height=1cm, 
    text centered, 
    yshift=.4cm
]

\tikzstyle{continuationdots} = [
  circle,
  fill=black,
  inner sep=.5mm,
  yshift=.5cm
]

\tikzstyle{line} = [
    draw, -latex'
]

%Menu environment sets up menu listing
\newenvironment{menu}
{
  \begin{tikzpicture}
    [node distance=.6cm and 1cm,start chain=trunk going below,]

    %Setup length variables for calculating main menu entry shift
    \newlength\YCoordMain
    \newlength\YCoordChild
    \newlength\YDelta

    %Setup boolean flags 
    \newboolean{firstchildtest}
    %%%%%%\newboolean{doshift}\setboolean{doshift}{false}

    %Setup shift counter (necessary because of scope issues with boolean)
    \newcounter{doshift}\setcounter{doshift}{0}

}
{
  \end{tikzpicture}
}


%Sub menu environment sets up a branch for a child element
\newenvironment{submenu}{

  %Boolean firstchildtest is set the first time through to indicate if node should be to right or below
  \setboolean{firstchildtest}{true}

  %Boolean doshift is set when a sub menu is created so that the main menu can shift the next entry down 
  %%%%%%\setboolean{doshift}{true}
  \addtocounter{doshift}{1}  

  \begin{scope} [start branch=abranch going right,node distance=.6cm and 1cm] 

}
{
  \end{scope}
}

%Main menu item command inserts a main menu item
\newcommand{\mainmenuitem}[3]{

  %If shift is required, clear the flag and add the shift; otherwise simply add the node
  \ifthenelse{\value{doshift}>0}
  {
    
    %Clear the flag
    %%%%%%\setboolean{doshift}{false}
    \setcounter{doshift}{0}

    %Calculate the required shift (LastChild-LastMain)
    \setlength\YDelta{\YCoordChild}
    \addtolength\YDelta{\YCoordMain}

    \node[genericbox,join,on chain=trunk,yshift=\YDelta,#3] (#1) {#2 SHIFT \the\YCoordMain};
  }
  {
    \node[genericbox,join,on chain=trunk,#3] (#1) {#2 NO SHIFT};
  }

  %Get the y coordinate of this main menu entry
  \pgfextracty{\YCoordMain}{+(\tikzchaincurrent.south)}

}

%Sub menu item command inserts a sub menu item
\newcommand{\submenuitem}[3]{

  %Get the y coordinate of this child
  \pgfextracty{\YCoordChild}{(\tikzchaincurrent.south)}

  %If this is NOT the first child in the sub menu then the chain direction is going below
  \ifthenelse{\boolean{firstchildtest}}
  {
    \setboolean{firstchildtest}{false} 
    \node[genericbox,join,on chain,#3] (#1) {#2 \the\YCoordChild};
  }
  {
    \node[genericbox,join,on chain=going below,#3] (#1) {#2 \the\YCoordChild};
  }

}

\begin{document}

\begin{menu}
\mainmenuitem{m1}{Menu Entry}{}
\begin{submenu}
\submenuitem{a}{Sub Menu Entry}{}{}
\submenuitem{b}{Sub Menu Entry}{=going below}{}
\begin{submenu}
\submenuitem{c}{Sub Menu Entry}{}{}
\submenuitem{d}{Sub Menu Entry}{=going below}{}
\end{submenu}
\end{submenu}
\mainmenuitem{m2}{Menu Entry}{}
\end{menu}

\end{document}
Attachments
Capture.PNG
Capture.PNG (21.31 KiB) Viewed 7695 times
Post Reply