17

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

enter image description here

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:

enter image description here

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.

Thank you in advance.

  • 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
  • 1
    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
20

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))}
        \shade[nodemeta=\pgfmathresult](\i,\j,\k)circle(3pt);
    }
}
\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)))}
        \shade[nodemeta=\pgfmathresult](\i,\j,\k)circle(3pt);
    }
}
\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
  • 1
    @pceccon Try textnode/\i/\j/\k/.try~~ – Symbol 1 Mar 15 '15 at 1:36
16

A tikz-3dplot solution.

enter image description here

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
10

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

An isometric wire frame cube

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
9

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 
                -- print("Not found: ("..test..")") 
                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.

A picture generated by Lua (recursive algorithm)

  • 1
    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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.