4

I'd like to create an exact surface plot of a function defined on the unit simplex {(x, y, z): x + y + z = 1}. Here's a sketch of the figure I'd like to create.

enter image description here

I know about surface plots in PSTricks and TikZ, about barycentric coordinate systems in TikZ, and about ternaryaxis in pgfplots, but I don't see a way of combining them to produce what I want. (My difficulty is not with the specific form of the function; I'll be happy with an answer that plots the function x.)

I'd like to say something like

\documentclass{article}
\usepackage{pgfplots}

\begin{document}

\begin{tikzpicture}
  \begin{axis} % or some special type of axis
    \addplot3 {<f(x,y,z)>};
  \end{axis}
\end{tikzpicture}

\end{document}

where f(x,y,z) is the definition of my function. Is something like that possible?

6

PGFPLOTS has a patching type called triangle

\documentclass{article}
\usepackage{pgfplots}
\usepgfplotslibrary{patchplots}
\pgfplotsset{compat=1.14}

\begin{document}
    \begin{tikzpicture}
        \begin{axis}
            \addplot3
                [patch,patch type=triangle]
                coordinates { (0,0,0) (1,1,.1) (0,1,.2) };
        \end{axis}
    \end{tikzpicture}
\end{document}

Every three coordinates you give it, it will draw (fill) a triangle for you.

So the next step is to generate those coordinates:

\def\addtriangle#1{
    \xdef\trianglesbuffer{\trianglesbuffer #1}
}
\def\calculatecoordinate(#1,#2,#3)[#4]=\f(#5,#6){
        \pgfmathsetmacro#1{#5}
        \pgfmathsetmacro#2{#6}
        \pgfmathsetmacro#3{\f({(#5)},{(#6)})}
        \pgfmathsetmacro#4{\g({(#5)},{(#6)})}
}
\def\calculatetriangle#1{
    % #1 is +            #1 is -
    %         C                  B A  
    %          ◣                  ◥   
    %         A B                  C  
    \calculatecoordinate(\xa,\ya,\za)[\wa]=\f(\x,\y)
    \calculatecoordinate(\xb,\yb,\zb)[\wb]=\f(\x#11,\y)
    \calculatecoordinate(\xc,\yc,\zc)[\wc]=\f(\x,\y#11)
    \addtriangle{(\xa,\ya,\za)[\wa] (\xb,\yb,\zb)[\wb] (\xc,\yc,\zc)[\wc]}
}
\def\calculatetheplot{
    \foreach\x in{0,...,20}{
        \foreach\y in{0,...,20}{
            % check \x + \y + \z =1
            %       ◣
            %       ◣ ◣
            %       ◣ ◣ ◣
            \ifnum\numexpr\x+\y<20
                \calculatetriangle+
            \fi
            %       
            %        ◥
            %        ◥ ◥
            \ifnum\numexpr\x>0 \ifnum\numexpr\y>0 \ifnum\numexpr\x+\y<21
                \calculatetriangle-
            \fi\fi\fi
        }
    }
}

\begin{tikzpicture}
    \begin{axis}
        \def\trianglesbuffer{} % initialize the buffer
        \def\f(#1,#2){#2*#2/20+2*sin(40*#1)} % we want to plot this function
        \def\g(#1,#2){sqrt((#1-20/3)^2+(#2-20/3)^2)} % with this point meta
        \calculatetheplot
        \edef\pgfmarshal{
            \noexpand\addplot3
                [patch,patch type=triangle,point meta=explicit]
                coordinates{\trianglesbuffer};
        }
        \pgfmarshal
    \end{axis}
\end{tikzpicture}

Next we want to change the coordinate to the unit simplex

\def\calculatecoordinate(#1,#2,#3)[#4]=\f(#5,#6){
        \pgfmathsetmacro#1{#5+\f({(#5)},{(#6)})}
        \pgfmathsetmacro#2{#6+\f({(#5)},{(#6)})}
        \pgfmathsetmacro#3{20-(#5)-(#6)+\f({(#5)},{(#6)})}
        \pgfmathsetmacro#4{\g({(#5)},{(#6)})}
}

\begin{tikzpicture}
    \begin{axis}[axis lines=middle,axis equal,view={80}{15}]
        \def\trianglesbuffer{} % clear the buffer
        \def\f(#1,#2){0} % we want to plot this function
        \def\g(#1,#2){0} % with this point meta
        \calculatetheplot
        \edef\pgfmarshal{
            \noexpand\addplot3
                [patch,patch type=triangle,mesh,point meta=explicit]
                coordinates{\trianglesbuffer};
        }
        \pgfmarshal
        \def\trianglesbuffer{} % clear the buffer
        \def\f(#1,#2){1+sin(20*#1)*cos(30*#2)} % we want to plot this function
        \def\g(#1,#2){\f({(#1)},{(#2)})} % with this point meta
        \calculatetheplot
        \edef\pgfmarshal{
            \noexpand\addplot3
                [patch,patch type=triangle,point meta=explicit]
                coordinates{\trianglesbuffer};
        }
        \pgfmarshal
    \end{axis}
\end{tikzpicture}

  • Spectacular! I'd like the simplex to appear to be horizontal. That is, I'd like the line from each corner of the simplex to the value of the function at that point to be vertical. One way to do that is to put the tikzpicture in a \rotatebox. For view={60}{15}, \rotatebox{57.5}{\begin{tikzpicture}...\end{tikzpicture}} seems about right. (a) Is there a better way of doing that? (b) Why does 57.5 work (approximately) for view={60}{15}? (I suppose that is really a math question, not a TeX question.) I'd also like, if possible, to remove the existing axes and add a vertical axis. – Martin J. Osborne Mar 21 '17 at 1:49
  • @MartinJ.Osborne (a) Usually \begin{tikzpicture}[rotate=57.5]. (b) It is indeed math. A better way is view={135}{-35}. An even better way is to define \calculatecoordinate, \f, \g properly. (c) \begin{axis}[hide axis]. – Symbol 1 Mar 21 '17 at 1:53
  • Can any permissible pgf math expression be used in the definition of the function? The surface resulting from \def\f(#1,#2){#1 > 10 ? #1 : 10} doesn't seem to be correct. – Martin J. Osborne Mar 21 '17 at 2:55
  • Try (#1 > 10 ? #1 : 10). Notice that \f is more like an inline expansion rather than a real function. One must be careful of -(#1-#2) and -#1-#2. – Symbol 1 Mar 21 '17 at 3:49
  • I can't find any version of the definition of my function that does not generate a TeX error. Is there a lower-level language I can use to reliably define the function? I want \def\f(#1,#2){(#1 > 10 ? 20*(#1/(40-#1)) : (#2 > 10 ? 20*(20-#2)/(40-#2) : (#1 + #2 < 10 ? 20*(#1 + #2)/(20 + #1 + #2) : 20/3)))}. The definition \def\f(#1,#2){(#1 > 10 ? 20*(#1/(40-#1)) : (#2 > 10 ? (20*(20-#2)/40) : 20/3))} compiles without error, but changing 20*(20-#2)/40 to 20*(20-#2)/(40-#2), which is what I want, gives an error (even though \def\f(#1,#2){20*(20-#2)/(40-#2)} compiles without error). – Martin J. Osborne Mar 21 '17 at 23:50

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.