绘制 3D 体积 - 立方体

绘制 3D 体积 - 立方体

我想知道如何绘制类似下图的东西:

在此处输入图片描述

我一直在研究该代码:

\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}  

但目前结果并不好:

在此处输入图片描述

我必须绘制一个 5 x 5 x 5 的立方体,并且我想使用不同的颜色突出显示中心体素 (3, 3, 3) 及其尺寸邻域 (3 x 3 x 3)。

我正在努力适应代码来生成“好看”的图像并绘制连接。但是,我不太理解。

先感谢您。

答案1

这里有两个\tikzset版本。本质区别在于的内容 \pgfmathparse{},也就是公式距离

我喜欢 L-1 规范

\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}

我喜欢 L-∞ 范数

\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}

我想要插入文字

\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];
            }
        }
    }

答案2

一个tikz-3dplot办法。

在此处输入图片描述

代码

\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}

答案3

如果简单的等距投影足够好,那么你可以得到相当不错的结果元帖子

等距线框立方体

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.

答案4

首先,我尝试了欧几里得范数,并将计算出的距离四舍五入为一系列 TikZ 样式,后来我尝试了符号 1 推荐的绝对值范数,以使用 Lua 中的递归算法验证我的尝试。我将其作为所给答案的有效替代方案附上。

-- 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)

我们运行texlua mal-recursive.lua。我们收到有关进度的消息,并mal-result.tikz生成一个 TikZ 文件,该文件随后由 LaTeX 加载。

% 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}

我们运行任何主流的 LaTeX 引擎,例如lualatex mal-volume.tex。我们得到两页相同的图片。第一张图片由 Lua 代码片段(递归算法)生成,第二张图片使用符号 1 的思想,并考虑到 L-1 范数。

我们可以重新定义级别的样式,因为它在 TikZ 中很常见。图片如下所示。

Lua生成的一张图片(递归算法)

相关内容