Can I calculate the difference between two relatively positioned nodes with TikZ using calc?

I have a chart with relatively positioned nodes. How do I calculate the horizontal distance x, between node "sub3" and node "key" and multiply that value by 2 and use it for the positioning of node "bang2" such that it is aligned with node bang1.

I tried using some of the code from page 54 of the TikZ Manual v2.10 to draw a circle at node "sub3" with a radius reaching to node "bang2" as a test to see if I can calculate this, but it failed. I know that the TikZ example uses absolute positioning rather than relative positioning. I assume this is why my test failed.

\documentclass[12pt]{standalone}
\usepackage{fontspec}
\usepackage{tikz}
\usetikzlibrary{arrows,positioning,shapes,backgrounds,fit,calc}

\begin{document}

\begin{tikzpicture}[node distance=3cm, auto]
\tikzset{
mynode/.style={rectangle,rounded corners,draw=black, top color=white, bottom color=red!50,very thick, inner sep=1em, minimum size=3em, text centered},
keynode/.style={rectangle,rounded corners,draw=black, top color=white, bottom color=orange!50,very thick, inner sep=1em, minimum size=3em, text centered},
bangnode/.style={shape=star, star points=20, star point ratio=1.65,draw=black, top color=white, bottom color=yellow!50,very thick, inner sep=2em, minimum size=3em, text centered,font=\bfseries},
myarrow/.style={->, >=latex', shorten >=1pt, thick},
mylabel/.style={text width=7em, text centered}
}

\node[bangnode](bang1){Bang};
\node[keynode,below left=2cm and 2cm of bang1](key){Key Message};
\node[mynode,below right of=key](sub1){Sub Topic 1};
\node[mynode,below of=sub1](sub2){Sub Topic 2};
\node[mynode,below of=sub2](sub3){Sub Topic 3};
\node[bangnode,below right=2cm and 2cm of sub3](bang2){Bang};
%ARROWS
\draw[<->,bend left=45] (sub1)[]to(sub2);
\draw[<->,bend left=45] (sub2)to(sub3);
\draw[<->,bend left=45] (sub1)to (key);
\draw[<->,bend left=45] (sub2)to (key);
\draw[<->,bend left=45] (sub3)to (key);
%\draw (key) let
%   \p1 = ($(sub3) - (key)$);
% in
%   circle ({veclen(\x1,\y1)});

\begin{pgfonlayer}{background}
\node [fill=blue!10,fit=(bang1) (bang2) (sub1)(sub2)(sub3)(key)] {};
\end{pgfonlayer}
\end{tikzpicture}

\end{document}


-
Remove the semicolon in \p1 = ($(sub3) - (key)$); and your code would work. –  percusse Nov 18 '12 at 23:19

If all you want to do is to horizontally align the (bang1) and (bang2) nodes, you don't need such complicated calculations. You can use the let syntax to get the x-coordinate of (bang1) and the y-coordinate of (sub3), using something like

\path let \p1=(bang1), \p2=(sub3) in
node[bangnode,yshift=-2cm,anchor=north] (bang2) at (\x1,\y2) {Bang};


A complete example:

\documentclass[12pt]{standalone}
\usepackage{fontspec}
\usepackage{tikz}
\usetikzlibrary{arrows,positioning,shapes,backgrounds,fit,calc}

\begin{document}

\begin{tikzpicture}[node distance=3cm, auto]
\tikzset{
mynode/.style={rectangle,rounded corners,draw=black, top color=white, bottom color=red!50,very thick, inner sep=1em, minimum size=3em, text centered},
keynode/.style={rectangle,rounded corners,draw=black, top color=white, bottom color=orange!50,very thick, inner sep=1em, minimum size=3em, text centered},
bangnode/.style={shape=star, star points=20, star point ratio=1.65,draw=black, top color=white, bottom color=yellow!50,very thick, inner sep=2em, minimum size=3em, text centered,font=\bfseries},
myarrow/.style={->, >=latex', shorten >=1pt, thick},
mylabel/.style={text width=7em, text centered}
}

\node[bangnode](bang1){Bang};
\node[keynode,below left=2cm and 2cm of bang1](key){Key Message};
\node[mynode,below right of=key](sub1){Sub Topic 1};
\node[mynode,below of=sub1](sub2){Sub Topic 2};
\node[mynode,below of=sub2](sub3){Sub Topic 3};
\path let \p1=(bang1), \p2=(sub3) in
node[bangnode,yshift=-2cm,anchor=north] (bang2) at (\x1,\y2) {Bang};
%ARROWS
\draw[<->,bend left=45] (sub1)[]to(sub2);
\draw[<->,bend left=45] (sub2)to(sub3);
\draw[<->,bend left=45] (sub1)to (key);
\draw[<->,bend left=45] (sub2)to (key);
\draw[<->,bend left=45] (sub3)to (key);

\begin{pgfonlayer}{background}
\node [fill=blue!10,fit=(bang1) (bang2) (sub1)(sub2)(sub3)(key)] {};
\end{pgfonlayer}
\end{tikzpicture}

\end{document}


Another simpler option would be to use the perpendicular coordinate system, and use something like

\node[bangnode,yshift=-2cm,anchor=north] (bang2) at (bang1|-sub3) {Bang};


to place the node using the x-coordinate of (bang1) and the y-coordinate of (sub3). A complete example:

\documentclass[12pt]{standalone}
\usepackage{fontspec}
\usepackage{tikz}
\usetikzlibrary{arrows,positioning,shapes,backgrounds,fit,calc}

\begin{document}

\begin{tikzpicture}[node distance=3cm, auto]
\tikzset{
mynode/.style={rectangle,rounded corners,draw=black, top color=white, bottom color=red!50,very thick, inner sep=1em, minimum size=3em, text centered},
keynode/.style={rectangle,rounded corners,draw=black, top color=white, bottom color=orange!50,very thick, inner sep=1em, minimum size=3em, text centered},
bangnode/.style={shape=star, star points=20, star point ratio=1.65,draw=black, top color=white, bottom color=yellow!50,very thick, inner sep=2em, minimum size=3em, text centered,font=\bfseries},
myarrow/.style={->, >=latex', shorten >=1pt, thick},
mylabel/.style={text width=7em, text centered}
}

\node[bangnode](bang1){Bang};
\node[keynode,below left=2cm and 2cm of bang1](key){Key Message};
\node[mynode,below right of=key](sub1){Sub Topic 1};
\node[mynode,below of=sub1](sub2){Sub Topic 2};
\node[mynode,below of=sub2](sub3){Sub Topic 3};
\node[bangnode,yshift=-2cm,anchor=north] (bang2) at (bang1|-sub3) {Bang};
%ARROWS
\draw[<->,bend left=45] (sub1)[]to(sub2);
\draw[<->,bend left=45] (sub2)to(sub3);
\draw[<->,bend left=45] (sub1)to (key);
\draw[<->,bend left=45] (sub2)to (key);
\draw[<->,bend left=45] (sub3)to (key);

\begin{pgfonlayer}{background}
\node [fill=blue!10,fit=(bang1) (bang2) (sub1)(sub2)(sub3)(key)] {};
\end{pgfonlayer}
\end{tikzpicture}

\end{document}


And, to answer the specific question, of course you can calculate distances between nodes, using veclen and the let syntax; a simple example:

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{positioning,calc}

\begin{document}

\begin{tikzpicture}
\node (A) {A};
\node[below left=of A ] (B) {B};
\node[below =of B ] (C) {C};
\node[below right =of C ] (D) {D};
\draw (B) -- (D);
\draw (B) let \p1 = ($(D) - (B)$)
in circle ({veclen(\x1,\y1)});
\end{tikzpicture}

\end{document}


In the original code:

\draw (key) let
\p1 = ($(sub3) - (key)$);
in
circle ({veclen(\x1,\y1)});


there's a superfluous semicolon at the end of the second line; once this is removed:

\draw (key) let
\p1 = ($(sub3) - (key)$)
in
circle ({veclen(\x1,\y1)});


the code should work.

-
Cool! Great answer...almost there... my question was, could you calculate the difference between two relatively positioned nodes. Your examples do not do any calculations, they just assign x,y coordinates. Nonetheless, thanks for putting in an alternative too. Keep in mind, the point is not to align bang1 and bang2 (although it is nice to do so), the point is to learn how to make calculations between relative points. –  macmadness86 Nov 18 '12 at 23:07
@macmadness86 please see my updated answer for an example of calculation of distance between nodes. –  Gonzalo Medina Nov 18 '12 at 23:18
I hate to do this, because you made a nice-looking answer... but my question is: how do I calculate the distance between RELATIVE points. Point A in your example is fixed at (0,0). Maybe I should rephrase my question. Could I use a relatively positioned node as a reference point for my calculations? –  macmadness86 Nov 18 '12 at 23:27
@macmadness86 the idea is the same independently of whether the nodes are placed absolutely or relatively; I updated my last example. –  Gonzalo Medina Nov 18 '12 at 23:34
That is what I wanted to know! Thanks! Could you also tell me why may commented out calculation in gray is not working? It looks the same as your example to me... What am I missing? –  macmadness86 Nov 18 '12 at 23:44