# Automating tikzpicture to reproduce a matrix-like image

There's this image I want to reproduce using tikz

My main issue is the right one since I want to take advantage tikz's automatic stuff for drawing. As of now, this MWE is my best attempt:

\documentclass[]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows,positioning}
\usetikzlibrary{calc}
\tikzset{%
controlcpu/.pic={
\node[fill=yellow,
label={[anchor=north,
name=cu]90:Control Unit},
minimum width=3cm,
minimum height=2.5cm,
draw] (CU) {};
},
controlgpu/.pic={
\node[fill=yellow,
minimum width=3mm,
minimum height=1mm,
draw] (CU) {};
},
cachecpu/.pic={
\node[fill=red,
label={[anchor=north,
name=cache]90:Cache},
minimum width=3cm,
minimum height=2.5cm,
draw] (CU) {};
},
cachegpu/.pic={
\node[fill=red,
minimum width=3mm,
minimum height=1mm,
draw] (CUGPU) {};
},
alucpu/.pic={
\node[fill=green,
label={[anchor=north,
name=alu]90:ALU},
minimum width=3cm,
minimum height=2.5cm,
draw] (ALUCPU) {};
},
alugpu/.pic={
\node[fill=green,
minimum width=0.3cm,
minimum height=0.2cm,
draw] (ALUGPU) {};
},
dram/.pic={
\node[fill=orange,
label={[anchor=north,
name=dr]90:DRAM},
minimum width=3cm,
minimum height=2.5cm,
draw] (DRAM) {};
}
}

\begin{document}
\begin{tikzpicture}

\matrix[] (GRID) [column sep=1mm, row sep=1mm]
{\pic{controlgpu}; & \pic{cachegpu}; & \pic{alugpu}; & \pic{alugpu}; & \pic{alugpu}; & \pic{alugpu};  \pic{alugpu}; & \pic{alugpu};  \pic{alugpu}; & \pic{alugpu};  \pic{alugpu}; & \pic{alugpu};  \pic{alugpu}; & \pic{alugpu};  \pic{alugpu}; & \pic{alugpu}; & \pic{alugpu}; & \pic{alugpu}; \\
\pic{controlgpu}; & \pic{cachegpu}; & \pic{alugpu}; & \pic{alugpu}; & \pic{alugpu}; & \pic{alugpu};  \pic{alugpu}; & \pic{alugpu};  \pic{alugpu}; & \pic{alugpu};  \pic{alugpu}; & \pic{alugpu};  \pic{alugpu}; & \pic{alugpu};  \pic{alugpu}; & \pic{alugpu}; & \pic{alugpu}; & \pic{alugpu}; \\};

\end{tikzpicture}

\end{document}


This is quite ugly and the output is horrible, since I don't know how to put the controlgpu and cachegpu one on top of the other

Furthermore, I couldn't manage to take advantage of foreach: maybe because it instead requires a "positional" (nodes) approach?

My questions are two

• What's the most efficient way to reproduce it using tikz's matrix?
• Can it be done using foreach? Is foreach recommended for drawings like those?

For instance, here at the official guide is what I mean with the use of foreach to generate a matrix-like shaped tikzfigure.

• – Ignasi Feb 13 '17 at 8:26

For right image (GPU):

cells are driwen by double loop:

\documentclass[tikz,
margin=3mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows,
calc,
positioning,
}

\begin{document}
\begin{tikzpicture}[
node distance = 0.5mm and 0.5mm,
start chain = going right,
every node/.style = {on chain, inner sep=0pt, outer sep=0pt},
YO/.style = {% Yellow Orange boxes
rectangle split, rectangle split parts=2, draw,
rectangle split part fill = {yellow,red},
rectangle split empty part height=2mm-\pgflinewidth,
minimum width=4mm,
node contents={\nodepart{one}\nodepart{two}}},
G/.style = {% Green boxes
draw, fill=green!80, minimum size=4mm,
node contents={}},
O/.style = {% Orange boxes
draw, fill=orange,
inner ysep=3mm, inner xsep=4mm, align=left,
node contents={DRAM}},
]
\node (n11) [YO];
\foreach \i in {2,3,...,16}
\node (n1\i) [G];
% other rows
\foreach \j [count=\jx from 1] in {2,3,...,8}
{
\node (n\j1) [YO,below=of n\jx1];
\foreach \i in {2,3,...,16}
\node (n2\i) [G];
}

% dram
\path   let \p1=($(n11.west)-(n116.east)$),
\n1={veclen(\y1,\x1)} in
node[below right=0.5 mm and 0 mm of n81.south west,
text width=\n1-8mm,
O, label={[inner sep=1mm]below:GPU}];
\end{tikzpicture}
\end{document}


Similarly can be drawn also left image for CPU.

• Wow, that's... Difficult. I guess one learns only through experience, right? – Eugenio Feb 12 '17 at 22:42
• All right, just a question: if I want to reproduce the CPU image to the left of it, is it enough to put two tikzpicture environments one after the other in the same figure environment? Or should I draw them together? – Eugenio Feb 12 '17 at 22:48
• @Eugenio, see edit of my answer. Now (in second iteration) is even shorter. Well, some experiences help :) but during collecting it, I read TikZ & PGF manual many, many time and look for similar examples on texample.net and here, on TeX.SE site ... I assume, that code is self explanatory. If it is not to you, I can add some comments into code. – Zarko Feb 12 '17 at 22:48
• @Eugenio, depends if you like to have separate (sub) caption for them. if not, than you can draw both images as one figure (not recommend). – Zarko Feb 12 '17 at 22:50
• Nope, the code is clear don't worry; your edit made it even clearer... Every time I look at tikz code is always very intuitive, although then difficult to program with. Thanks btw! For the latter, I will insert it in a beamer, so that I will put just a sentence on top of them. I'll work on it. Thx – Eugenio Feb 12 '17 at 22:50

\documentclass[border=2mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcommand\unit[4]   {\draw[fill=#2] (#4) rectangle +(#1);
\node[font=\sffamily\bfseries] at ($(#4)+0.5*(#1)$) {#3};
}
\newcommand\gpualu    {\unit{ 0.9,0.9 }{green!30}{}}
\newcommand\gpucontrol{\unit{ 0.9,0.4 }{yellow!30}{}}
\newcommand\gpucache  {\unit{ 0.9,0.4 }{red!30}{}}
\newcommand\dram      {\unit{16.9,1.9 }{red!30}{DRAM}}
\newcommand\cpualu    {\unit{ 4.1,2.05}{green!30}{ALU}}
\newcommand\cpucontrol{\unit{ 8.4,4.2 }{yellow!30}{Control}}
\newcommand\cpucache  {\unit{16.9,4.2 }{red!30}{Cache}}
\begin{document}
\begin{tabular}[b]{c}
\begin{tikzpicture}[scale=0.4]
\dram      { 0  ,-2.5}
\cpucache  { 0  , 0}
\cpucontrol{ 0  ,4.4}
\cpualu    { 8.6,4.4}
\cpualu    {12.8,4.4}
\cpualu    { 8.6,6.55}
\cpualu    {12.8,6.55}
\node[font=\sffamily\bfseries] at (8.45,-3.5) {CPU};
\end{tikzpicture}\\
Central Processing Unit (CPU)
\end{tabular}
\begin{tabular}[b]{c}
\begin{tikzpicture}[scale=0.4]
\dram{0,-2.5}
\foreach \i in {0,...,7}
\gpucontrol{0,1.1*\i+0.5}
\gpucache{0,1.1*\i}
\foreach \j in {1,...,16}
\gpualu{\j,1.1*\i};
\node[font=\sffamily\bfseries] at (8.45,-3.5) {GPU};
\end{tikzpicture}\\
Graphical Processing Unit (GPU)
\end{tabular}
\end{document}


Edit: Some explanations how I proceeded.

1. Relative or absolute positioning: The flexible solution is to position the elements relative to each other, never specifying any coordinates. The drawback is that you have to specify parameters for distances, group items, etc, which may require many lookups in the docs and may become nontransparent. For this one-of-a-kind drawing I chose the approach of absolute positioning since it promised to be faster and more compact.

2. The main structure is a filled rectangle, occasionally with a label. This is what \node[size,color] at (position) {label} does. For tikz reasons size and position are not treated the same: position can be dimensionless and can be scaled, size requires units and does not scale. Since such drawings are first drafted in some convenient grid and afterwards scaled to the required size, I have to \draw the rectangle:

\draw[fill=#2] (#4) rectangle +(#1); % #2 fill color, #4 coordinates left bottom corner, #1 size = width,height


and use \node only for positioning the label in the center.

\node at ($(#4)+0.5*(#1)$) {#3}; % #4 coordinates of left bottom corner, #1 vector to the top right corner, #3 label


The expression $(#4)+0.5*(#1)$ computes the coordinates of the center of the rectangle as "left bottom corner plus half of the distance to the top right corner". This coordinate calculation requires \usetikzlibrary{calc} in the preamble.

3. I chose the left bottom corner of the grid as the origin (0,0) and from there worked my way up and down. I chose a 1-by-1-square as the basic unit. To separate the GPU-ALUs I chose to give them a size of 0.9,0.9. Later I wanted to have even more space between the rows, so I introduced the factor 1.1 for the y-coordinate in the \foreach loop.

• Why do you declare \unit with a \draw plus \node commands instead of a simple \node with defined size? – Ignasi Feb 13 '17 at 7:58
• What's the easiest way to add the "CPU" and "GPU" labels? Because it's clear for the first answer, while here I don't know where to start with. – Eugenio Feb 13 '17 at 12:57
• @gernot hello gernot, your answer is very good example of learning syntax of foreach... but I can not understand all the codes, could you add any comments, for example \newcommand\unit[5] {\draw[fill=#3] (#5) rectangle +(#1,#2); \node[font=\sffamily\bfseries] at ($(#5)+({0.5*#1},{0.5*#2})$) {#4}; \foreach \i in {0,...,7} \gpucontrol{0,1.1*\i+0.5} How do you know the number, for example \unit{ 0.9}{0.9}... – dozer Feb 14 '17 at 5:34
• @gernot I appreciate your detailed explanations. – dozer Feb 14 '17 at 10:43