13

My problem is to define blocks (mainly rectangles) that has labels in them in terms of matrices (mostly block diagonal) such that the n-many arrows that goes in this rectangle visually is at the same height as the corresponding element on the diagonal of the label. Consider the following example

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[>=stealth]
\node[draw,rectangle,inner sep=0.5cm] (y) at (0,0) {$A$};
\node[draw,rectangle,inner sep=0.3cm] (d) at (0,2) {$\begin{array}{cc}B&\\&C \end{array}$};
\draw[->] (y.west) -| ++(-1,1) |- (d.west);
\draw[->] (d.east) -| ++(1,-1) |- (y.east);
\end{tikzpicture}
\end{document}

Now, let me start by creating two subproblems:

  1. When I create this label, the inner sep option of node shape (the visible rectangle around B and C) is kind of annoying to adjust. So I don't think it would survive in a better solution. So this question would be obsolete if the following problem is solved.
  2. I would like to get a handle of the node height resulting from the matrix label that I have put in and then somehow obtain a subdivision of n+1 of this height creating n nodes to connect to and from on the west and east sides. such that in the minimal example above I would connect to two arrows going out from A (not from the same location but separate and equally dividing the height of A) and connecting to B and C respectively (and vice versa).
4
  • Just to be clear: you want the arrow from A->B to line up with B and the arrow from C->A to line up with C, hopefully all automatically, right?
    – Alan Munn
    May 26, 2011 at 20:22
  • Actually I have to further clarify my problem. What has been requested is quite nice but missing the detail of having also the A block different arrow starting and ending positions. So the arrows do not originate from the same root but rather from equally seperated positions. But I will try to employ the same solution done for the upper block, to the bottom block.
    – percusse
    May 26, 2011 at 21:41
  • I just read tex.meta.stackexchange.com/questions/7695 and decided to have a look at your first post :). +1 for sympathy and the @egreg injustice :). May 29, 2018 at 9:20
  • 1
    @Dr.ManuelKuehner Hahah, I'm ashamed :)
    – percusse
    May 29, 2018 at 9:21

2 Answers 2

10

You could use the .<angle> syntax to manually control the position of the arrows; another option would be to use a matrix of (math) nodes instead of the array environment, to get access to the nodes in the matrix. The following example illustrates both these ideas:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,matrix}

\begin{document}

\begin{tikzpicture}[>=stealth]
  \node[draw,rectangle,inner sep=0.5cm] (y) at (0,0) {$A$};
  \node[draw,rectangle,inner sep=0.3cm] (d) at (0,2) {$\begin{array}{cc}B&\\&C \end{array}$};
  \draw[->] (y.west) -| ++(-1,1) |- (d.160);
  \draw[->] (y.west) -| ++(-0.8,1) |- (d.190);
  \draw[->] (d.-10) -| ++(0.8,-1) |- (y.east);
  \draw[->] (d.20) -| ++(1,-1) |- (y.east);
\end{tikzpicture}

\vspace*{10pt}

\begin{tikzpicture}[>=stealth,remember picture]
\node[draw,rectangle,inner sep=0.5cm] (y) at (0,0) {$A$};
\node[draw] (d) at (0,2) {%
  \begin{tikzpicture}[remember picture]
    \matrix [matrix of math nodes] (mat)
  {
    B & \phantom{C}   \\
    \phantom{B} & C \\
  };
  \end{tikzpicture}
  };
\draw[->,shorten >= 6pt] (y.west) -| ++(-1,1) |- (mat-1-1);
\draw[->,shorten >= 6pt] (y.west) -| ++(-0.8,1) |- (mat-2-1);
\draw[->] ($(mat-2-2)+(14pt,0)$) -| ++(0.8,-1) |- (y.east);
\draw[->] ($(mat-1-2)+(14pt,0)$) -| ++(1,-1) |- (y.east);
\end{tikzpicture}

\end{document}

Image 1

EDIT: a variant on kgr's solution, but using two matrices of nodes:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,matrix,positioning}

\begin{document}

\begin{tikzpicture}[>=stealth]
\matrix[matrix of math nodes,nodes in empty cells,draw] (d) at (0,2)
{
  A & & & & \\
  & B & & & \\
  & & C  & & \\
  & & & D & \\
  & & & & E \\
};
\matrix[matrix of math nodes,nodes in empty cells,draw,minimum width=30pt,below=of d] (y) 
{
   \\ \\ \\ \\ \\
};
\node at (y) {$D$};

\foreach \i in {1,2,3,4,5}
  {
  \draw[->] let \p1 = (d.west), \p2 = (d-\i-\i.west), \p3 = (y.west), \p4 = (y-\i-1.west) in (\x3,\y4) -| 
    ++(-2.4+0.2*\i,1) |- (\x1,\y2);
  \draw[->] let \p1 = (d.east), \p2 = (d-\i-\i.east), \p3 = (y.east), \p4 = (y-\i-1.east) in (\x1,\y2) -|
    ++(1.6-0.2*\i,-1) |- (\x3,\y4);
  }
\end{tikzpicture}

\end{document}

Image 2

3
  • Thanks for the solution. I have tried to make the same to the A block. But then I couldn't make the two arrows start and end in the block seperately because then there is no matrix to be referenced in A block. Tried to make the A by multicol multirow trick but no avail.
    – percusse
    May 26, 2011 at 21:45
  • @percusse: I've updated my answer showing one possible way to consider two matrices of nodes. My example builds on kgr's solution; all the credit goes to him, of course. May 27, 2011 at 2:35
  • which is perfect. Thank you for the update and the example.
    – percusse
    May 27, 2011 at 2:42
3

I'm not perfectly clear on what you want, but here is a first pass, using tikz matrices:

\documentclass[11pt]{memoir}

\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{matrix}

\begin{document}

\begin{tikzpicture}[>=stealth]
\node[draw,rectangle,inner sep=0.5cm] (y) at (0,0) {$A$};
\matrix[matrix of nodes,nodes in empty cells,rectangle,draw] (d) at (0,2)
{
  $B$ & \\
  & $C$ \\
};
\draw[->] let \p1 = (d.west), \p2=(d-1-1.west) in (y.west) -| ++(-1,1) |- (\x1,\y2);
\draw[->] let \p1 = (d.west), \p2=(d-2-2.west) in (y.west) -| ++(-0.8,1) |- (\x1,\y2);
\draw[->] let \p1 = (d.east), \p2=(d-2-2.east) in (\x1,\y2) -| ++(0.8,-1) |- (y.east);
\draw[->] let \p1 = (d.east), \p2=(d-1-1.east) in (\x1,\y2) -| ++(1,-1) |- (y.east);
\end{tikzpicture}

\end{document}

The line drawing could probably be simplified quite a bit by writing some macros.

demonstration

For getting the lines to start/end in the right place, there is also an even simpler syntax for the line drawing you could use, that I didn't do above because it is slightly less transparent. Instead of e.g. the first draw line above, you could do:

\draw[->] (y.west) -| ++(-1,1) |- (d.west |- d-1-1.west);

and so on.

(Edited this a bit to match Gonzalo Medina's interpretation of the request, which seems more correct than mine initially was.)

Ok, and one more edit:

Just for fun I made something a little more interesting.

\documentclass[11pt]{memoir}

\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{matrix}

\begin{document}


\begin{tikzpicture}[>=stealth]
\node[draw,rectangle,inner sep=0.5cm] (y) at (0,0) {$A$};
\matrix[matrix of nodes,nodes in empty cells,rectangle,draw] (d) at (0,3)
{
  $B$ & & & &\\
 & $C$ & & & \\
 & & $D$ & & \\
 & & & $E$ & \\
 & & & & $F$ \\
};

\foreach \z in {1,...,5}
{
  \draw[->] let \n1={\z * -0.2cm - 0.5cm} in
      (y.west) -| ([xshift=\n1] d.west |- y.west) |- (d.west |- d-\z-\z.west);
  \draw[->] let \n1={\z * 0.2cm + 0.5cm} in
      (d.east |- d-\z-\z.east) -| ([xshift=\n1] d.east |- y.east) |- (y.east);
}

\end{tikzpicture}
\end{document}

enter image description here

5
  • Also, by the way, that syntax for referring to parts of a matrix, which I think is exactly what you want, is documented in section 38 (the libraries section), not in the actual matrix documentation; I guess this is because it is really only a feature of the "matrix of nodes" option. But it took me a while to find it, originally, so I thought I'd mention it.
    – kgr
    May 26, 2011 at 20:34
  • Wow, that would come in handy. Thanks a lot. As I commented below, I couldn't find a way to apply this trick to the A block also. So I will keep trying.
    – percusse
    May 26, 2011 at 21:46
  • @percusse can you clarify what you are trying to do with the A block that isn't working? For this particular diagram it should be easy to put a 1x1 matrix containing $A$ as the only node. (And btw a matrix is a node itself, so in practice there isn't really a difference between the node y as-is and a node containing a 1x1 matrix.)
    – kgr
    May 26, 2011 at 22:00
  • @percusse, maybe I see: even though the A block has just one letter in it, do you want the height of the arrows to match where they hit the larger block? If so, I can think of a brute-force way of doing this with empty matrices...I don't have time to work up a detailed solution, but just put a blank matrix using phantom (as in G.M.'s answer) instead of actual letters to match the node sizes, and draw the A over that. Then you can refer to the nodes in the matrix.
    – kgr
    May 26, 2011 at 22:24
  • Yep, that is exactly what I did actually. Thanks though, the fun part gave me a fresh look. I was expecting to find an iterative solution as in your final for loop but couldn't get it. It was really helpful to automate the process.
    – percusse
    May 27, 2011 at 0:15

You must log in to answer this question.

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