1

I need to draw a finite number of cubes below a 3D surface. Thus I need nested loops. The following code is close to what I want, except that I want to modify the z limit for the \k parameter to have all the cubes under the surface plot. Basically, I would like to use \fun{\i}{\j}-\dl instead of \fun{\i}{\j}:

\documentclass[tikz]{standalone}
\usepackage{pgfplots}
 \usetikzlibrary{calc}
\pgfplotsset{compat=1.16}


\begin{document}


 
\newcommand{\fun}[2]{{4*((#1/(#1+1))*(#2/(#2+1))+0.5)}}


\tikzset{
    mycube/.pic={
        \pgfmathsetmacro\size{{#1}}
        \draw[fill=gray!20] (0,0,\size) -- ++(0,\size,0) -- ++(\size,0,0) -- ++(0,-\size,0) -- cycle; % top
        \draw[fill=gray!10] (0,0,0) --++(\size,0,0)-- ++ (0,0,\size)--++(-\size,0,0)  -- cycle; % front
        \draw[fill=gray!40] (\size,0,0) --++(0,\size,0)-- ++ (0,0,\size)--++(0,-\size,0)  -- cycle; %side
}
}

\begin{tikzpicture}
\def\dl{1}
\begin{axis}[xlabel = $x$, ylabel = $y$, zlabel = {$z$},
    xmin=0, xmax=5,
    ymin=0, ymax = 5,
    zmin=0, 
    clip=false]

% WORKING but not satisfactory
\pgfplotsforeachungrouped \i in {0,0+\dl,...,5-\dl}{
    \pgfplotsforeachungrouped \j in {5-\dl,5-(2*\dl),...,0}{
        \pgfplotsforeachungrouped \k in {0,0+\dl,...,int(\fun{\i}{\j})}{
            \edef\temp{\noexpand \draw (\i,\j,\k) pic{mycube={\dl}};}\temp
        }
    }
}

% NOT working
% \pgfplotsforeachungrouped \i in {0,0+\dl,...,5-\dl}{
%     \pgfplotsforeachungrouped \j in {5-\dl,5-(2*\dl),...,0}{
%         \pgfplotsforeachungrouped \k in {0,0+\dl,...,int(\fun{\i}{\j}-\dl)}{
%             \edef\temp{\noexpand \draw (\i,\j,\k) pic{mycube={\dl}};}\temp
%         }
%     }
% }

% NOT working
% \pgfplotsforeachungrouped \i in {0,0+\dl,...,5-\dl}{
%     \pgfplotsforeachungrouped \j [evaluate=\j as \klim using {int(\fun{\i}{\j})}] in {5-\dl,5-(2*\dl),...,0}{
%         \pgfplotsforeachungrouped \k in {0,0+\dl,...,\klim}{
%             \edef\temp{\noexpand \draw (\i,\j,\k) pic{mycube={\dl}};}\temp
%         }
%     }
% }

    \addplot3[surf,domain=0:5,y domain=0:5,fill opacity = 0.3] {\fun{x}{y}};
\end{axis}
\end{tikzpicture}
 
\end{document}

cubes under surface

I tried several trick to lower this z-limit but nothing works here. Any help would be appreciated!


Bonus questions:

  • I'm not sure that defining my surface function as a separate macro is the best way to do. How to do it in a cleaner way?
  • Why is it necessary to use int( ) in the \k loop parameters?

Thanks a lot.

2
  • As to one of your questions, why needing int in {0,0+\dl,...,int(\fun{\i}{\j})}, it is because the first two items are integers 0,1. Thus, to make sense, the last item must be an integer. int makes sure it is. However, I still do not really understand the question, what it is that needs fixing. What does "lower the z limit" mean? May 9, 2021 at 1:17
  • I've tried to make the text clearer (but you've interpreted my confusing sentence right!). I want to be able to draw this plot for various values of \dl, including non integers. So I wonder if an alternative to {0,0+\dl,...,int(\fun{\i}{\j})} could be to use point values such as `{0.0,0.0+\dl,...,\fun{\i}{\j}}. Do I understand it right?
    – Tobard
    May 10, 2021 at 6:58

1 Answer 1

2

Trying to understand what "lower the z limit" is to mean, I eventually guessed that the OP would like the boxes shifted downward in the z direction, while leaving the function intact, in a fashion so that the boxes do not exceed the function.

I adjusted the \k loop index to

\pgfplotsforeachungrouped \k in {0,0+\dl,...,(int(\fun{\i}{\j}) - 1.5)}

Why -1.5? Well, since \k starts drawing at 0, and the zero draw extends up to z=1, that accounts for subtracting 1. Then, I discovered that if I set the top limit on \z manually to, for example, 2.5, it rounds it up, as if it were a 3 and actually plots boxes up to z=4. Thus, there can be as large a difference between the actual function (z=2.5) and the stacked z-boxes (to z=4) of 4 - 2.5 = 1.5. That is why 1.5 must be subtracted off the total, in order to ensure that the box height never exceeds the function height.

\documentclass[tikz]{standalone}
\usepackage{pgfplots}
 \usetikzlibrary{calc}
\pgfplotsset{compat=1.16}

\begin{document}

\newcommand{\fun}[2]{{4*((#1/(#1+1))*(#2/(#2+1))+0.5)}}

\tikzset{
    mycube/.pic={
        \pgfmathsetmacro\size{{#1}}
        \draw[fill=gray!20] (0,0,\size) -- ++(0,\size,0) -- ++(\size,0,0) -- ++(0,-\size,0) -- cycle; % top
        \draw[fill=gray!10] (0,0,0) --++(\size,0,0)-- ++ (0,0,\size)--++(-\size,0,0)  -- cycle; % front
        \draw[fill=gray!40] (\size,0,0) --++(0,\size,0)-- ++ (0,0,\size)--++(0,-\size,0)  -- cycle; %side
}
}

\begin{tikzpicture}
\def\dl{1}
\begin{axis}[xlabel = $x$, ylabel = $y$, zlabel = {$z$},
    xmin=0, xmax=5,
    ymin=0, ymax = 5,
    zmin=0, 
    clip=false]

% WORKING but not satisfactory
\pgfplotsforeachungrouped \i in {0,0+\dl,...,5-\dl}{
    \pgfplotsforeachungrouped \j in {5-\dl,5-(2*\dl),...,0}{
    \pgfplotsforeachungrouped \k in {0,0+\dl,...,(int(\fun{\i}{\j}) - 1.5)}{
            \edef\temp{\noexpand \draw (\i,\j,\k) pic{mycube={\dl}};}\temp
        }
    }
}

    \addplot3[surf,domain=0:5,y domain=0:5,fill opacity = 0.3] {\fun{x}{y}};
\end{axis}
\end{tikzpicture}
 
\end{document}

enter image description here

8
  • Thanks for your help. There still is a problem with the last \k value: the z loop goes one step too far and adds unexpected cubes (as the one at the very top). I think it may be caused by the int( ) but don't know how to fix it.
    – Tobard
    May 10, 2021 at 16:00
  • @Tobard For that, using my mentioned alternate approach...instead of what is stated there, \pgfplotsforeachungrouped \k in {0,0+\dl,...,(int(\fun{\i}{\j}) - 1)}, you might need the last term to be - 1.5. I am trying to rationally justify why, but having difficulty. May 12, 2021 at 1:11
  • @Tobard I think I figured out why it must be - 1.5. If you place in a fixed z stack, for example, \pgfplotsforeachungrouped \k in {0,0+\dl,...,2.5}, it rounds up, and plots up to z=4 (z=0 cube, z=1 cube, z=2 cube, and z=2.5 is considered as z=3 cube. Thus, difference in function and cube height can be as large as 4 - 2.5 = 1.5. That is why 1.5 must be subtracted off. May 12, 2021 at 1:32
  • 1
    @Tobard I have incorporated these comments into a revised answer. May 12, 2021 at 1:44
  • 1
    Great! That rounding thing is weird! Thanks for your code. I changed - 1.5 to - 1.5*\dl to keep it generic. Seems to work for \dl = 0.5, but the behavior is erratic for \dl = 0.7! Anyway, that's much better! Thanks!
    – Tobard
    May 14, 2021 at 11:57

You must log in to answer this question.

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