12

Consider Gabriel's Horn:

enter image description here

which, mathematically speaking, is the curve y=1/x over the interval [1,∞] revolved about the x-axis. It is interesting because though it contains a finite volume, its surface area is infinite.

In an effort to draw it, I imported the image into Inkscape, traced a bitmap, and exported it to a tikzpath, which I incorporated into LaTeX as follows:

\documentclass[12pt]{book}
\usepackage{tikz}

\begin{document}
\thispagestyle{empty}
\begin{tikzpicture}
    \path[fill=black] (6.8, 14.1).. controls (6.7, 14.1) and (6.7, 14.3) .. (6.6, 
    14.4).. controls (6.5, 14.6) and (6.5, 15.2) .. (6.5, 15.1).. controls (6.5, 
    15.1) and (6.7, 15.1) .. (6.9, 15.1) -- (7.3, 15.1) -- (7.3, 14.9).. controls 
    (7.3, 14.7) and (7.2, 14.5) .. (7.0, 14.4).. controls (6.9, 14.4) and (6.8, 
    14.3) .. (6.8, 14.3).. controls (6.7, 14.2) and (6.7, 14.2) .. (6.8, 14.1).. 
    controls (6.9, 14.0) and (6.9, 14.0) .. (6.8, 14.1) -- cycle(7.2, 14.9).. 
    controls (7.3, 15.0) and (7.2, 15.1) .. (7.0, 15.0).. controls (6.9, 15.0) and
    (6.8, 14.9) .. (6.9, 14.8).. controls (6.9, 14.8) and (7.1, 14.8) .. (7.2, 
    14.9) -- cycle(7.0, 14.1).. controls (7.0, 14.1) and (7.1, 14.1) .. (7.2, 
    14.2).. controls (7.3, 14.2) and (7.3, 14.3) .. (7.2, 14.2).. controls (7.2, 
    14.2) and (7.1, 14.2) .. (7.1, 14.3).. controls (7.1, 14.3) and (7.2, 14.3) ..
    (7.3, 14.3).. controls (7.4, 14.3) and (7.4, 14.3) .. (7.4, 14.3).. controls 
    (7.4, 14.4) and (7.4, 14.4) .. (7.4, 14.4).. controls (7.5, 14.4) and (7.5, 
    14.4) .. (7.5, 14.4).. controls (7.5, 14.4) and (7.5, 14.4) .. (7.5, 14.4).. 
    controls (7.6, 14.4) and (7.6, 14.4) .. (7.5, 14.4).. controls (7.5, 14.5) and
    (7.5, 14.5) .. (7.5, 14.5).. controls (7.6, 14.4) and (7.6, 14.4) .. (7.3, 
    14.2).. controls (7.0, 14.0) and (7.0, 14.0) .. (7.0, 14.1) -- cycle(7.6, 
    14.5).. controls (7.6, 14.5) and (7.6, 14.5) .. (7.7, 14.5).. controls (7.7, 
    14.5) and (7.7, 14.5) .. (7.7, 14.5).. controls (7.7, 14.5) and (7.7, 14.5) ..
    (7.7, 14.5).. controls (7.8, 14.5) and (7.8, 14.5) .. (7.8, 14.5).. controls 
    (7.7, 14.6) and (7.8, 14.6) .. (7.8, 14.6).. controls (7.8, 14.5) and (7.8, 
    14.5) .. (7.8, 14.5).. controls (7.7, 14.4) and (7.6, 14.4) .. (7.6, 14.5) -- 
    cycle(7.8, 14.6).. controls (7.8, 14.6) and (7.8, 14.6) .. (7.9, 14.6).. 
    controls (7.9, 14.6) and (8.0, 14.6) .. (7.9, 14.6).. controls (7.9, 14.6) and
    (7.9, 14.6) .. (8.0, 14.6).. controls (8.0, 14.6) and (8.1, 14.6) .. (8.0, 
    14.6).. controls (8.0, 14.7) and (8.0, 14.7) .. (8.1, 14.7).. controls (8.2, 
    14.6) and (8.2, 14.6) .. (8.1, 14.7).. controls (8.1, 14.7) and (8.2, 14.7) ..
    (8.2, 14.7).. controls (8.3, 14.7) and (8.2, 14.6) .. (8.1, 14.6).. controls 
    (8.0, 14.6) and (8.0, 14.6) .. (7.9, 14.6).. controls (7.9, 14.5) and (7.9, 
    14.6) .. (7.8, 14.6) -- cycle(7.3, 14.7).. controls (7.3, 14.8) and (7.3, 
    14.8) .. (7.3, 14.8).. controls (7.3, 14.7) and (7.3, 14.7) .. (7.3, 14.7).. 
    controls (7.3, 14.7) and (7.3, 14.7) .. (7.3, 14.7) -- cycle(8.3, 14.7).. 
    controls (8.2, 14.8) and (8.3, 14.8) .. (8.4, 14.7).. controls (8.4, 14.7) and
    (8.4, 14.7) .. (8.4, 14.7).. controls (8.3, 14.7) and (8.3, 14.7) .. (8.3, 
    14.7) -- cycle(8.4, 14.8).. controls (8.4, 14.8) and (8.4, 14.8) .. (8.5, 
    14.8).. controls (8.6, 14.8) and (8.6, 14.8) .. (8.6, 14.8).. controls (8.5, 
    14.8) and (8.5, 14.8) .. (8.6, 14.8).. controls (8.6, 14.8) and (8.7, 14.8) ..
    (8.7, 14.8).. controls (8.7, 14.8) and (8.7, 14.8) .. (8.7, 14.8).. controls 
    (8.5, 14.7) and (8.5, 14.7) .. (8.4, 14.8) -- cycle(8.7, 14.8).. controls 
    (8.7, 14.8) and (8.7, 14.9) .. (8.8, 14.8).. controls (8.9, 14.8) and (8.9, 
    14.8) .. (8.9, 14.8).. controls (9.0, 14.8) and (8.8, 14.8) .. (8.7, 14.8) -- 
    cycle(9.0, 14.8).. controls (9.1, 14.9) and (9.1, 14.9) .. (9.1, 14.8).. 
    controls (9.1, 14.8) and (9.1, 14.8) .. (9.1, 14.8).. controls (9.0, 14.8) and
    (9.0, 14.8) .. (9.0, 14.8) -- cycle(7.3, 15.0).. controls (7.3, 15.4) and 
    (7.3, 15.4) .. (7.0, 15.5) -- (6.7, 15.7) -- (6.7, 15.9).. controls (6.7, 
    16.0) and (6.7, 16.1) .. (6.7, 16.1).. controls (6.7, 16.1) and (6.6, 16.0) ..
    (6.6, 15.9).. controls (6.6, 15.9) and (6.6, 15.9) .. (6.6, 15.9).. controls 
    (6.6, 15.9) and (6.6, 16.0) .. (6.6, 16.1).. controls (6.7, 16.2) and (6.7, 
    16.2) .. (6.7, 16.2).. controls (6.7, 16.1) and (6.7, 16.1) .. (6.8, 16.1).. 
    controls (6.8, 16.2) and (6.8, 16.2) .. (6.8, 16.1).. controls (6.7, 15.9) and
    (6.7, 15.9) .. (7.0, 15.7).. controls (7.2, 15.6) and (7.3, 15.5) .. (7.3, 
    15.5).. controls (7.3, 15.4) and (7.3, 15.3) .. (7.3, 15.1).. controls (7.3, 
    14.9) and (7.3, 14.9) .. (7.3, 15.0) -- cycle(8.9, 14.9).. controls (8.9, 
    14.9) and (8.9, 14.9) .. (8.9, 14.9).. controls (8.9, 14.9) and (8.9, 14.9) ..
    (8.9, 14.9).. controls (8.8, 14.9) and (8.8, 14.9) .. (8.9, 14.9) -- 
    cycle(9.2, 14.9).. controls (9.3, 14.9) and (9.3, 14.9) .. (9.4, 14.9).. 
    controls (9.4, 14.9) and (9.4, 14.9) .. (9.3, 14.9).. controls (9.2, 14.9) and
    (9.2, 14.9) .. (9.2, 14.9) -- cycle(9.1, 14.9).. controls (9.1, 14.9) and 
    (9.1, 14.9) .. (9.1, 14.9).. controls (9.2, 14.9) and (9.1, 14.9) .. (9.1, 
    14.9).. controls (9.1, 14.9) and (9.0, 14.9) .. (9.1, 14.9) -- cycle(9.5, 
    14.9).. controls (9.5, 14.9) and (9.6, 14.9) .. (9.6, 14.9).. controls (9.7, 
    14.9) and (9.6, 14.9) .. (9.6, 14.9).. controls (9.5, 14.9) and (9.5, 14.9) ..
    (9.5, 14.9) -- cycle(9.3, 15.0).. controls (9.3, 15.0) and (9.3, 15.0) .. 
    (9.4, 15.0).. controls (9.4, 14.9) and (9.4, 14.9) .. (9.3, 14.9).. controls 
    (9.3, 14.9) and (9.3, 14.9) .. (9.3, 15.0) -- cycle(9.8, 15.0).. controls 
    (9.8, 15.0) and (9.9, 15.0) .. (10.0, 15.0).. controls (10.0, 14.9) and (10.0,
    14.9) .. (9.9, 14.9).. controls (9.8, 14.9) and (9.7, 14.9) .. (9.8, 15.0) --
    cycle(7.9, 15.0).. controls (7.6, 15.1) and (7.4, 15.3) .. (7.4, 15.7).. 
    controls (7.3, 15.8) and (7.3, 15.9) .. (7.2, 16.1).. controls (7.1, 16.2) and
    (7.0, 16.3) .. (7.0, 16.3).. controls (7.0, 16.3) and (7.0, 16.3) .. (7.1, 
    16.2).. controls (7.1, 16.2) and (7.1, 16.2) .. (7.1, 16.2).. controls (7.1, 
    16.2) and (7.2, 16.1) .. (7.3, 16.1).. controls (7.9, 15.6) and (9.3, 15.3) ..
    (11.4, 15.2).. controls (11.8, 15.2) and (12.2, 15.2) .. (12.3, 15.2).. 
    controls (12.4, 15.2) and (12.6, 15.2) .. (12.8, 15.2).. controls (12.9, 15.2)
    and (13.0, 15.2) .. (13.0, 15.1).. controls (13.0, 15.1) and (12.8, 15.1) .. 
    (11.5, 15.0).. controls (11.1, 15.0) and (10.6, 15.0) .. (10.4, 15.0).. 
    controls (10.2, 15.0) and (10.1, 15.0) .. (10.1, 15.0).. controls (10.1, 15.0)
    and (10.4, 15.0) .. (10.7, 15.0).. controls (11.9, 15.1) and (12.0, 15.1) .. 
    (11.9, 15.1).. controls (11.9, 15.2) and (11.7, 15.2) .. (11.5, 15.2).. 
    controls (9.7, 15.2) and (8.0, 15.5) .. (7.4, 15.9).. controls (7.4, 16.0) and
    (7.3, 16.0) .. (7.3, 16.0).. controls (7.3, 16.0) and (7.4, 15.9) .. (7.5, 
    15.8).. controls (7.6, 15.7) and (7.6, 15.7) .. (7.6, 15.6).. controls (7.6, 
    15.4) and (7.9, 15.2) .. (8.1, 15.2).. controls (8.2, 15.2) and (8.3, 15.2) ..
    (8.3, 15.1).. controls (8.3, 15.1) and (8.7, 15.1) .. (9.4, 15.1).. controls 
    (10.1, 15.1) and (10.6, 15.1) .. (10.6, 15.1).. controls (10.6, 15.1) and 
    (10.3, 15.0) .. (9.9, 15.0).. controls (9.8, 15.0) and (9.7, 15.0) .. (9.8, 
    15.0) -- (9.9, 15.1) -- (9.8, 15.1).. controls (9.7, 15.1) and (9.5, 15.1) .. 
    (9.2, 15.0).. controls (8.5, 15.0) and (8.0, 15.0) .. (7.9, 15.0) -- 
    cycle(7.3, 16.0).. controls (7.3, 16.0) and (7.2, 16.0) .. (7.2, 16.1) -- 
    (7.1, 16.2) -- (7.2, 16.1).. controls (7.3, 16.0) and (7.3, 16.0) .. (7.3, 
    16.0) -- cycle(9.5, 15.0).. controls (9.5, 15.0) and (9.6, 15.0) .. (9.6, 
    15.0).. controls (9.7, 15.0) and (9.6, 15.0) .. (9.6, 15.0).. controls (9.5, 
    15.0) and (9.5, 15.0) .. (9.5, 15.0) -- cycle(6.5, 15.5).. controls (6.5, 
    15.5) and (6.5, 15.6) .. (6.5, 15.5).. controls (6.5, 15.5) and (6.5, 15.4) ..
    (6.5, 15.4).. controls (6.5, 15.4) and (6.5, 15.4) .. (6.5, 15.5) -- cycle;
    
\end{tikzpicture}
\end{document}

Compiling it, I get

enter image description here

Then, when I tried to add some color shading to get the horn to look more realistic, I got the following TikZ code which I cannot compile because the colorcodes (e.g., cb9b9b9) are not recognized by LaTeX. Moreover, I am not posting the code here because it is extremely long.

QUESTION: How may I produce a reasonable facsimile of the above Wiki image with LaTeX code? From the colorized bitmap trace from Inkscape, I thought I had it, but LaTeX does not recognize Inkscape's color codes; for example, \path[fill=c767676] (-11.7, 7.6).. controls (-11.7, 7.6) and (-11.3, 8.0) ..

4
  • Is TikZ required? Would you accept other solutions (PSTricks or asymptote for example?) Commented Jun 28 at 3:07
  • @WillieWong pstricks is fine; As for Asymptote, I have not been able to successfully run it in Latex---I tried to just a couple of days ago or so.
    – DDS
    Commented Jun 28 at 3:15
  • @WillieWong I just edited the title, removing the reference to TIKZ,
    – DDS
    Commented Jun 28 at 3:23
  • 3
    Hey uh... can I take this bad render? I'd love to use this for an album cover. Commented Jun 28 at 12:58

3 Answers 3

28

enter image description here

Almost same image but from a different point of view and with the sun in a different position.

enter image description here

Using only tikz with the library math and layers. It may take some time to execute; the time can be shorten by modifying the integers \Nx and \Nyz in the pic element horn.

The code is lengthy since it includes the definitions of the observer's position, the sun's position, and the construction of the colors of the small polygonal faces.

The code

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{math}
\usepackage{tikz-3dplot}
\begin{document}
\makeatletter
\tikzset{%
  view/.style 2 args={%  observer longitude and latitude (y upwards)
                      %  Remark. lomg=0 means x=0
    z={({-sin(#1)}, {-cos(#1)*sin(#2)})},
    x={({cos(#1)}, {-sin(#1)*sin(#2)})},
    y={(0, {cos(#2)})},
    evaluate={%
      \tox={sin(#1)*cos(#2)};
      \toy={sin(#2)};
      \toz={cos(#1)*cos(#2)};
    },
    longitude = #1,
    latitude = #2
  },
  sun/.style n args={3}{% longitude, latitude, light contrast in [0, 1]
    sun longitude = #1,
    sun latitude = #2,
    contrast = #3,
    evaluate={%
      real \sunx, \suny, \sunz, \lightC;
      \sunx = sin(\sLongit)*cos(\sLatit);
      \suny = sin(\sLatit);
      \sunz = cos(\sLongit)*cos(\sLatit);
    }
  }
}
\pgfkeys{/tikz/.cd,
  latitude/.store in=\aLatit,  % observer's latitude
  latitude=0
}
\pgfkeys{/tikz/.cd,
  longitude/.store in=\aLongit,  % observer's longitude
  longitude=0  % corresponds to x=0
}
\pgfkeys{/tikz/.cd,
  sun latitude/.store in=\sLatit,
  sun latitude = 80
}
\pgfkeys{/tikz/.cd,
  sun longitude/.store in=\sLongit, 
  sun longitude=90
}
\pgfkeys{/tikz/.cd,
  contrast/.store in=\lightC,  % light contrast \in [0, 1]
  contrast=.5
}
\tikzmath{%
  function hornFaceColor(\i, \j, \N, \M) {%
    % Verifies if seen when the cone has no base; the color number is
    % modified by -2000.
    real \ang;
    \p@x = 1/\rB +(\i-.5)/\N*(1/\rT -1/\rB);
    \r@ = 1/\p@x;
    \t = 360*((\j-.5)/\M);
    \ux = \vx*cos(\t) +\wx*sin(\t);
    \uy = \vy*cos(\t) +\wy*sin(\t);
    \uz = \vz*cos(\t) +\wz*sin(\t);
    % modification needed when the radii are different
    \ang = atan(\r@*\r@); % angle with (Oy)
    \ux = \ux*cos(\ang) +\nx*sin(\ang);
    \uy = \uy*cos(\ang) +\ny*sin(\ang);
    \uz = \uz*cos(\ang) +\nz*sin(\ang);    
    \res = \ux*\tox + \uy*\toy + \uz*\toz;
    \tmp = int(100*\lightC*(\ux*\sunx + \uy*\suny + \uz*\sunz));
    if \res>0 then {% if seen
      return \tmp;
    } else {return {-2000 +\tmp};};    
  };
}
\makeatother
\tikzset{%
  pics/horn/.style args={xB=#1, xT=#2}{%
    % The interval for which the horn will be drawn (the curve y=1/x
    % rotated around the (Ox) axis).
    code={%
      \colorlet{mainColor}{.}
      \colorlet{innerColor}{-mainColor!50!gray}
      \colorlet{leftRGB{1}}{white}
      \colorlet{leftRGB{0}}{mainColor!50!black}
      \colorlet{leftRGB{3}}{white}
      \colorlet{leftRGB{2}}{innerColor!50!black}
      \tikzmath{%  
        real \rB, \rT;
        \rB = 1/#1;
        \rT = 1/#2;
        integer \Nx, \Nyz, \k, \j, \prevj, \i, \previ;
        \Nx = 50;
        \Nyz = 64;
        real \tmpBT, \tmpx, \tmpy, \tmpz, \tmpcst, \cstForColor;
        real \px, \nx, \ny, \nz, \vx, \vy, \vz, \wx, \wy, \wz;
        \nx = 1;
        \ny = 0;
        \nz = 0;
        \vx = 0;
        \vy = 1;
        \vz = 0;
        \wx = 0;
        \wy = 0;
        \wz = 1;
        %% points \P{\i,\j}
        % \i for x direction and
        % \j for directions in the plane (Oyz)
        for \i in {0, ..., \Nx}{%
          \px = #1 +\i/\Nx*(#2 -#1);
          \r = 1/\px;
          for \j in {0, ..., \Nyz}{%
            \t = \j/\Nyz*360;
            \Px{\i,\j} = \px +\r*\vx*cos(\t) +\r*\wx*sin(\t);
            \Py{\i,\j} = \r*\vy*cos(\t) +\r*\wy*sin(\t);
            \Pz{\i,\j} = \r*\vz*cos(\t) +\r*\wz*sin(\t);
          };
        };
        %% faces
        for \i in {1, ..., \Nx}{%
          \previ = \i -1;
          for \j in {1, ..., \Nyz}{%
            \prevj = \j -1;
            \cstForColor = hornFaceColor(\i, \j, \Nx, \Nyz);
            if \cstForColor>-999 then {%
              if \cstForColor>=0. then { \k = 1; } else {%
                \k = 0;  % in the shade for leftRGB{}
                \cstForColor = int(abs(\cstForColor));
              };
              {%
                \filldraw[leftRGB{\k}!\cstForColor!mainColor, line width=.001pt]
                (\Px{\previ,\prevj}, \Py{\previ,\prevj}, \Pz{\previ,\prevj})
                -- (\Px{\previ,\j}, \Py{\previ,\j}, \Pz{\previ,\j})
                -- (\Px{\i,\j}, \Py{\i,\j}, \Pz{\i,\j})
                -- (\Px{\i,\prevj}, \Py{\i,\prevj}, \Pz{\i,\prevj})
                -- cycle;
              };            
            } else {%
              \cstForColor = -(\cstForColor +2000);  % back to -value
              if \cstForColor>=0. then {%
                \k = 3;
                \cstForColor = \cstForColor;
              } else {%
                \k = 2;  % in the shade for leftRGB{}
                \cstForColor = int(abs(\cstForColor));
              };
              {%
                \begin{pgfonlayer}{background} 
                  \filldraw[leftRGB{\k}!\cstForColor!innerColor,
                  line width=.001pt]
                  (\Px{\previ,\prevj}, \Py{\previ,\prevj}, \Pz{\previ,\prevj})
                  -- (\Px{\previ,\j}, \Py{\previ,\j}, \Pz{\previ,\j})
                  -- (\Px{\i,\j}, \Py{\i,\j}, \Pz{\i,\j})
                  -- (\Px{\i,\prevj}, \Py{\i,\prevj}, \Pz{\i,\prevj})
                  -- cycle;
                \end{pgfonlayer}
              };
            };
          };
        };
      }  % end tikzmath
    }
  },
}


\begin{tikzpicture}[view={-30}{15}, sun={-50}{45}{.75}]
  \pgfdeclarelayer{background}
  \pgfsetlayers{background,main}
  
  \draw[->] (0, 0, 0) -- (2, 0, 0) node[shift={(.2, 0, 0)}, above] {$x$};
  \draw[->] (0, 0, 0) -- (0, 2, 0) node[shift={(0, .2, 0)}] {$y$};
  \draw[->] (0, 0, 0) -- (0, 0, 2) node[shift={(0, 0, .2)}, left] {$z$};

  \path (0, 0) pic[black!80!red!70, scale=1.5]
  {horn={xB=.95, xT=7}};
\end{tikzpicture}
\end{document}
6
  • 7
    Can't believe somebody was insane enough to actually do this in tikz :)-.
    – cfr
    Commented Jun 28 at 5:06
  • Many, many thanks for this answer!
    – DDS
    Commented Jun 28 at 7:55
  • 1
    @ch thanks for the compliment!
    – Daniel N
    Commented Jun 28 at 14:47
  • 1
    So wait, did you just implement a rudimentary ray-tracer? Wow! Commented Jun 29 at 1:28
  • How may the displayed axes be removed? Thanks.
    – DDS
    Commented Jul 31 at 3:33
13

I don't know if this helps, but I was able to do it with pgfplots as follows;

enter image description here

\documentclass{beamer}
\beamertemplatenavigationsymbolsempty
\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{compat=1.18}
\begin{document}
\begin{frame}
\centering
\begin{tikzpicture}[scale=0.5]
\begin{axis}[
hide axis,
view={-30}{30},
scale=3,
unit vector ratio=1 1 1
]
\addplot3[
surf,
color=black,
opacity=0.2,
samples=40,
domain = 1:15,
%restrict z to domain=-4:4,
restrict x to domain=1:15,
]({x},{(1/x)*cos(deg(y))},{(1/x)*sin(deg(y))});
\end{axis}
\end{tikzpicture}
\end{frame}
\end{document}
2
  • 1
    you could also just use the plot path with a parametric function in plain tikz too
    – Jasper
    Commented Jun 28 at 18:50
  • 1
    +1 Thank you for this very nice answer.
    – DDS
    Commented Jun 28 at 21:00
2

I was wondering how one could preserve the shadow effects of the source graphic.

Unfortunately I can only find the original graphic in png format.

enter image description here

So I turned the png graphic into an svg file using 'Vector Magic'.

This is the result:

enter image description here

Download-svg-File-GitHub

In LaTeX:

enter image description here

% arara: pdflatex: { shell: yes }

\documentclass{article}
\usepackage{svg}
\usepackage[margin=11mm]{geometry}
\begin{document}
\begin{figure}
  \centering
  \includesvg[scale=0.35]{GabrielsHorn}
  \caption{Gabriels Horn}
\end{figure}
\end{document}

You must log in to answer this question.

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