# Drawing a 3D volume - Cube

I'm wondering how I could draw something like the image below:

I've been working in that code:

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{matrix, calc, backgrounds, decorations.pathreplacing}

\begin{document}

\begin{tikzpicture}[thick]
\foreach[count=\i] \a in {0,2,4,6,8}
{
\foreach[count=\j] \a in {0,2,4,6,8}
{
\foreach[count=\k] \a in {0,2,4,6,8}
{
\node at (\i,\j,\k) [circle,fill=black] {};
}
}
}
\end{tikzpicture}

\end{document}


But so far the result isn't good:

I have to draw a cube of 5 x 5 x 5 and I would like to highlight the central voxel (3, 3, 3) and its neighborhood of dimension (3 x 3 x 3), using different colors.

I'm trying to adapt this code to produce a 'good to look' image and to draw the connections. However, I didn't understand it well.

• This is not really a latex or tikz related question but a question of finding a coloring-algorithm since the nodes are already on their correct positions. For instance, for coloring the center-node (i,j,k)=(3,3,3) check (with an \ifthenelse-test) whether ijk=27 and - if that's the case - color it with a different color. – jonathan.scholbach Mar 13 '15 at 22:59
• Sorry, to me it's not just this. For example, the nodes aren't well placed as in the first figure. I know how to paint them and how to do the lines with 'ifs', but not how to provide a good figure as in my example. – pceccon Mar 13 '15 at 23:16
• Can you tell how the positions of the nodes would differ in your desired result? Cause I don't see which positions are wrong. Maybe check your algo just with 3 nodes in each dimension and use a higher distance between the nodes. – jonathan.scholbach Mar 13 '15 at 23:19
• This 3d cuba and code should get you started texample.net/tikz/examples/gray-code-in-4-cube and this one for more of 3d projections tex.stackexchange.com/questions/37442/… – R. Schumacher Mar 13 '15 at 23:36
• Hi, @jonathan.scholbach. The example that I'm following (link in the question) for example, uses a positioning in which there aren't two vertices overlapping (as I'm obtaining with my code). However, I didn't understand how the code achieve this result (it only uses 2 loops and I suspect that is with the myGlobalTransformation function). But I didn't get this function, and I really would like to understand besides copying and pasting. – pceccon Mar 14 '15 at 1:36

Here are two \tikzset version. The essential difference is the content of \pgfmathparse{}, which is the formula of distance.

## I Like L-1 Norm

\tikzset{
blur/.style={preaction={draw,fill,white,opacity=.9,line width=2pt}},
showdepth/.style={color=#1,ball color=#1,opacity=1+(\k-2)/5},
edgemeta/.is choice,
edgemeta/1/.style={blur,showdepth=blue!66!green},
edgemeta/2/.style={blur,showdepth=green!66!blue},
edgemeta/3/.style={blur,showdepth=green},
edgemeta/4/.style={blur,showdepth=green!66!yellow},
edgemeta/5/.style={blur,showdepth=yellow!66!green},
edgemeta/6/.style={blur,showdepth=yellow},
nodemeta/.is choice,
nodemeta/0/.style={blur,showdepth=blue},
nodemeta/1/.style={blur,showdepth=blue!66!green},
nodemeta/2/.style={blur,showdepth=green!66!blue},
nodemeta/3/.style={blur,showdepth=green},
nodemeta/4/.style={blur,showdepth=green!66!yellow},
nodemeta/5/.style={blur,showdepth=yellow!66!green},
nodemeta/6/.style={blur,showdepth=yellow},
drawedge/.style={drawedgex,drawedgey,drawedgez},
drawedgex/.code={
\ifnum\i=2\else
\pgfmathparse{int(max(abs(\i),abs(\i+1))+abs(\j)+abs(\k))}
\draw[edgemeta=\pgfmathresult](\i,\j,\k)--+(1,0,0);
\fi
},
drawedgey/.code={
\ifnum\j=2\else
\pgfmathparse{int(abs(\i)+max(abs(\j),abs(\j+1))+abs(\k))}
\draw[edgemeta=\pgfmathresult](\i,\j,\k)--+(0,1,0);
\fi
},
drawedgez/.code={
\ifnum\k=2\else
\pgfmathparse{int(abs(\i)+abs(\j)+max(abs(\k),abs(\k+1)))}
\draw[edgemeta=\pgfmathresult](\i,\j,\k)--+(0,0,1);
\fi
},
drawnode/.code={
\pgfmathparse{int(abs(\i)+abs(\j)+abs(\k))}
}
}
\begin{tikzpicture}[z={(-.28,-.15)},draw=white]
\foreach\k in{-2,...,2}{
\foreach\j in{-2,...,2}{
\foreach\i in{-2,...,2}{
\path[drawedge,drawnode];
}
}
}
\end{tikzpicture}


## I Like L-∞ Norm

\documentclass[tikz,border=9]{standalone}
\begin{document}

\tikzset{
blur/.style={preaction={draw,fill,white,opacity=.9,line width=2pt}},
showdepth/.style={color=#1,ball color=#1,opacity=1+(\k-2)/5},
edgemeta/.is choice,
edgemeta/1/.style={blur,showdepth=green},
edgemeta/2/.style={blur,showdepth=yellow},
nodemeta/.is choice,
nodemeta/0/.style={blur,showdepth=blue},
nodemeta/1/.style={blur,showdepth=green},
nodemeta/2/.style={blur,showdepth=yellow},
drawedge/.style={drawedgex,drawedgey,drawedgez},
drawedgex/.code={
\ifnum\i=2\else
\pgfmathparse{int(max(abs(\i),abs(\i+1),abs(\j),abs(\k)))}
\draw[edgemeta=\pgfmathresult](\i,\j,\k)--+(1,0,0);
\fi
},
drawedgey/.code={
\ifnum\j=2\else
\pgfmathparse{int(max(abs(\i),abs(\j),abs(\j+1),abs(\k)))}
\draw[edgemeta=\pgfmathresult](\i,\j,\k)--+(0,1,0);
\fi
},
drawedgez/.code={
\ifnum\k=2\else
\pgfmathparse{int(max(abs(\i),abs(\j),abs(\k),abs(\k+1)))}
\draw[edgemeta=\pgfmathresult](\i,\j,\k)--+(0,0,1);
\fi
},
drawnode/.code={
\pgfmathparse{int(max(abs(\i),abs(\j),abs(\k)))}
}
}
\begin{tikzpicture}[z={(-.3,-.15)},draw=white]
\foreach\k in{-2,...,2}{
\foreach\j in{-2,...,2}{
\foreach\i in{-2,...,2}{
\path[drawedge,drawnode];
}
}
}
\end{tikzpicture}

\end{document}


## I Want to Insert Text

\tikzset{
drawnode/.code={
\pgfmathparse{int(max(abs(\i),abs(\j),abs(\k)))}
\node at(\i,\j,\k)[circle,nodemeta=\pgfmathresult]{\color{black}\tikzset{textnode/\i/\j/\k/.try}};
}
}
\begin{tikzpicture}[scale=2,z={(-.28,-.15)},draw=white,textnode/1/0/-1/.code={(-2,0,-1)}]
\foreach\k in{-2,...,2}{
\foreach\j in{-2,...,2}{
\foreach\i in{-2,...,2}{
\path[drawedge,drawnode];
}
}
}

• That's a nice example of .code use, but neighbour nodes are not drawn correctly. The first level of the (3,3,3) node are these 6 nodes: (2,3,3), (4,3,3), (3,2,3), (3,4,3), (3,3,2) and (3,3,4). Generally speaking, we can use recursive algorithm to find them. – Malipivo Mar 14 '15 at 8:03
• Thank you very much for you answer, @Symbol-1, your result is beautiful and also understandable. I was thinking about to draw the cube with circles, but now I wanted in full 3D. (: – pceccon Mar 14 '15 at 16:24
• Just one, question @Symbol-1: is it possible to put a label inside some of that circles? Thank you. – pceccon Mar 14 '15 at 16:40
• @pceccon Try textnode/\i/\j/\k/.try~~ – Symbol 1 Mar 15 '15 at 1:36

A tikz-3dplot solution.

Code

\documentclass{article}
\usepackage{tikz}
\usepackage{tikz-3dplot}
\usetikzlibrary{shapes,calc,positioning,intersections}
\tdplotsetmaincoords{80}{120}

\begin{document}
\begin{tikzpicture}[scale=3, tdplot_main_coords,axis/.style={->},thick]

\foreach \x in {0,1,2}
\foreach \y in {0,1,2}
\foreach \z in {0,1,2}
{
\draw[thick,opacity=1] (\x,0,\z) -- (\x,2,\z);
\draw[thick,opacity=1] (0,\y,\z) -- (2,\y,\z);
\draw[thick,opacity=1] (\x,\y,0) -- (\x,\y,2);
}
% --- labels for vertices
\foreach \x in {0,1,2}
\foreach \y in {0,1,2}
\foreach \z in {0,1,2}
{\draw[fill=gray!10] (\x,\y,\z) circle (0.3em);}

\foreach \x/\y in {1/0,2/1,1/2,0/1}
\foreach \z in {0,2}
{\draw[fill=gray!50] (\x,\y,\z) circle (0.3em);}

\foreach \x/\y in {2/0,2/2,0/2,0/0}
\foreach \z in {1}
{\draw[fill=gray!50] (\x,\y,\z) circle (0.3em);}

\foreach \x/\y in {1/0,2/1,1/2,0/1}
\foreach \z in {1}
{\draw[fill=gray!90] (\x,\y,\z) circle (0.3em);}

\foreach \x/\y in {1/1}
\foreach \z in {0,1,2}
{\draw[fill=gray] (\x,\y,\z) circle (0.3em);}

\end{tikzpicture}
\end{document}

• Thank you, @Jesse. Your answer showed me a simple approach to do it. I would like to be able to accept all your answers here now. :S – pceccon Mar 14 '15 at 16:18

If a simple isometric projection is good enough, then you can get quite a nice result with Metapost.

prologues := 3;
outputtemplate := "%j%c.eps";

vardef odraw expr p = undraw p withpen pencircle scaled 1.2; draw p enddef;

beginfig(1);

path p[];
transform t; t = identity shifted (27,12);
p0 = unitsquare scaled 90;
p1 = p0 transformed t;
p2 = p1 transformed t;

% draw the back first
odraw p2; odraw point 0.5 of p2 -- point 2.5 of p2; odraw point 1.5 of p2 -- point 3.5 of p2;
% draw LH sides
for t=0 step 1/2 until 7/2:
odraw point t of p0 -- point t of p2;
endfor
% now the rest
odraw p1; odraw point 0.5 of p1 -- point 2.5 of p1; odraw point 1.5 of p1 -- point 3.5 of p1;
odraw center p0 -- center p2;
odraw p0; odraw point 0.5 of p0 -- point 2.5 of p0; odraw point 1.5 of p0 -- point 3.5 of p0;

picture b[]; d=8;
b1 = image(fill fullcircle scaled d withcolor .8[red,white]; draw fullcircle scaled d;);
b2 = image(fill fullcircle scaled d withcolor .5[red,white]; draw fullcircle scaled d;);
b3 = image(fill fullcircle scaled d withcolor .1[red,blue];  draw fullcircle scaled d;);

forsuffixes $=0,2: for t=0 upto 3: draw b1 shifted point t of p$;
draw b2 shifted point t+1/2 of p$; endfor draw b3 shifted center p$;
endfor
for t=0 upto 3:
draw b2 shifted point t of p1;
draw b3 shifted point t+1/2 of p1;
endfor
draw b1 shifted center p1;

endfig;
end.

• Thank you, @Thruston. In this specific question I would like to be able to accept them all. Different approaches that helped me a lot! – pceccon Mar 14 '15 at 16:15

At first, I tried Euclidean norm and I rounded computed distance to a series of TikZ styles, later I tried absolute-value norm as recommended by Symbol 1 to verify my attempt with recursive algorithm in Lua. I enclose it as a working alternative to presented answers.

-- texlua mal-recursive.lua, 2015-03-14
-- A snippet to find a level of nodes from a specific node.
-- I am testing recursive approach, however, the L-1 norm is more effective.

function searchit(malx,maly,malz, mallevel)
-- an initial point
print("Finding neighbours in volume...")
maxlevel = mallevel -- from local to global
-- local testing=fromx -- from global to local
malp = malx.." "..maly.." "..malz -- a small data trick if point was tested earlier
data = {
[malp] = {
x = malx,
y = maly,
z = malz,
level = 0
}
} -- initial value
neighbours(data[malp])
print("Saving nodes to a TikZ file...")
occupied(data)
end -- searchit

-- The core of the program...
function neighbours(point)
if point.level<maxlevel then
for _, newpoint in pairs{ {-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1} } do
--print(dlevel, point.x+dx.." "..point.y+dy.." "..point.z+dz)
newx = point.x+newpoint[1]
newy = point.y+newpoint[2]
newz = point.z+newpoint[3]
testing = newx.." "..newy.." "..newz
if not data[testing] or point.level <= data[testing].level then
-- test an untested point or retest higher level point recursively
data[testing] = {
x = newx,
y = newy,
z = newz,
level = point.level + 1}
neighbours(data[testing])
end -- test a point
end -- for cycle for all 6 directions
end -- if, test a level
end -- function neighbours

function occupied(maldata)
-- Drawing lines...
saveme=io.open("mal-result.tikz","w")

-- This is not effective as it uses many lines, but it is a short TeX code.
saveme:write([[\foreach \z in {]]..fromz..[[,...,]]..uptoz..[[} { % from back to front
\foreach \y in {]]..fromy..[[,...,]]..uptoy..[[} { % from bottom to up
\foreach \x in {]]..fromx..[[,...,]]..uptox..[[} { % from left to right
\ifnum\x<]]..uptox..[[ \draw[line] (\x,\y,\z)--(\x+1,\y,\z); \fi
\ifnum\y<]]..uptoy..[[ \draw[line] (\x,\y,\z)--(\x,\y+1,\z); \fi
\ifnum\z<]]..uptoz..[[ \draw[line] (\x,\y,\z)--(\x,\y,\z+1); \fi
}}} % \z, \y, \x
]]
) -- print or :write

-- Drawing nodes in styles...
for z=fromz, uptoz do
for y=fromy, uptoy do
for x=fromx, uptox do
test=x.." "..y.." "..z
--[[if not data[test] then
else --
-- print("("..test.."), level "..data[test].level)
end -- if]]
if data[test] then mallevel=data[test].level else mallevel="nil" end
saveme:write("\\node["..mallevel.."] at ("..x..","..y..","..z..") {};\n")
end
end
end -- for x, y, z
saveme:close()
end -- function occupied

-- For user manipulation, an initial setting of the volume...

--fromx=0; fromy=0; fromz=0 -- lower corner
--uptox=6; uptoy=6; uptoz=6 -- upper corner
--searchit(2,2,2,  5) -- (x,y,z), maxlevel

-- OP's request
fromx=1; fromy=1; fromz=1 -- lower corner
uptox=5; uptoy=5; uptoz=5 -- upper corner
--fromx=2; fromy=2; fromz=2 -- lower corner
--uptox=4; uptoy=4; uptoz=4 -- upper corner
searchit(3,3,3, 3)

--[[ Malipivo's testing example...
fromx=1; fromy=1; fromz=1 -- lower corner
uptox=10; uptoy=10; uptoz=10 -- upper corner
searchit(5,5,7, 6)]]

-- A tip for possible animation of growing levels...
--searchit(3,3,3,  0)
--searchit(3,3,3,  1)
--searchit(3,3,3,  2)
--searchit(3,3,3,  3)


We run texlua mal-recursive.lua. We are getting a message about the progress and a TikZ file mal-result.tikz is generated which is loaded by LaTeX afterwards.

% texlua mal-recursive.lua
% *latex mal-volume.tex
\documentclass[a4paper]{article}
\pagestyle{empty}
\usepackage{tikz}
% In case I don't want to use z= directly, use:
%\usepackage{tikz-3dplot}
%\tdplotsetmaincoords{25}{25}

\begin{document}
\tikzset{inner sep=0pt, outer sep=0pt,
every node/.append style={minimum width=1.8mm, circle, draw=gray},
% nodes without assigned level, nil is a Lua term for an undefined variable
nil/.style={ball color=white},
0/.style={ball color=red},
1/.style={ball color=blue},
2/.style={ball color=green},
3/.style={ball color=orange},
4/.style={ball color=yellow},
5/.style={ball color=brown},
6/.style={ball color=black},
line/.style={gray, line width=.2pt},
}

% My second attempt...
\begin{tikzpicture}[z={(-.3,-.15)}]%[tdplot_main_coords]
% a skeleton of volume and
% nodes with levels
\input mal-result.tikz
\end{tikzpicture}

\newpage
% My first attempt...
\begin{tikzpicture}[z={(-.3,-.15)}] %[tdplot_main_coords]
% This doesn't work well for all points as OP's requested...
\foreach \z in {1,...,5} { % from back to front
\foreach \y in {1,...,5} { % from bottom to up
\foreach \x in {1,...,5} { % from left to right
% a skeleton of volume
\ifnum\x<5 \draw[line] (\x,\y,\z)--(\x+1,\y,\z); \fi
\ifnum\y<5 \draw[line] (\x,\y,\z)--(\x,\y+1,\z); \fi
\ifnum\z<5 \draw[line] (\x,\y,\z)--(\x,\y,\z+1); \fi
% nodes with levels
%\pgfmathparse{int(ceil(sqrt((\x-3)^2+(\y-3)^2+(\z-3)^2)))} % an Euclidean distance from the center, (3,3,3)
\pgfmathparse{int(abs(\x-3)+abs(\y-3)+abs(\z-3))} % L-1 norm for comparison
\def\malstyle{\pgfmathresult}
\ifnum\pgfmathresult>3 \def\malstyle{nil} \fi % 3+
\node[\malstyle] at (\x,\y,\z) {};
}}} % \z, \y, \x
\end{tikzpicture}

\end{document}


We run any major LaTeX engine, e.g. lualatex mal-volume.tex. We get two pages with the same picture. The first picture is generated by a Lua snippet (recursive algorithm), the second picture is using Symbol 1's idea with the L-1 norm in mind.

We can redefine styles of levels as it is common in TikZ. The picture looks like this.

• Thank you, @Malipivo. All answers that I'm receiving are helping a lot (to construct what I want) and to learn different things. – pceccon Mar 14 '15 at 16:16