2

I just started with chemfig package and am enjoying it very much.

I made a quick search here in the community, but found no answer. So, let's go to the question: How to draw a fullerene structure using chemfig package?

Edit: please, try answering with simple code, or with code as simple as possible.

Just as for illustration, the fullerene structure is the one in the figure below.

enter image description here

  • I do not know if chemfig supports this. But it would be a rather basic task for tikz-3dplot, see e.g. tex.stackexchange.com/a/468073. – user194703 Aug 30 '19 at 1:29
3

Using only chemfig and this image as reference, I was able to make this:

\documentclass{article}
\usepackage{chemfig}

\setchemfig{
    bond offset = 0.75pt,
    double bond sep = 2.5pt,
    bond style = {line width = 0.75pt}
}

\begin{document}
    \definesubmol{fragment}{
        -[::126]-[::-54](=_#(2pt,2pt)[::180])
        -[::-70](-[::-56.2,1.07]=^#(2pt,2pt)[::180,1.07])
        -[::110,0.6](-[::-148,0.60](=^[::180,0.35])-[::-18,1.1])
        -[::50,1.1](-[::18,0.60]=_[::180,0.35])
        -[::50,0.6]
        -[::110]
    }

    \chemfig{
        (-[::+18,0.85,,,draw=none]!{fragment})
        (-[::+90,0.85,,,draw=none]!{fragment})
        (-[::162,0.85,,,draw=none]!{fragment})
        (-[::234,0.85,,,draw=none]!{fragment})
        (-[::306,0.85,,,draw=none]!{fragment})
    }
\end{document}

Result

Buckminsterfullerene

| improve this answer | |
10

This may not be the answer you are expecting. It is to show that, with a recent addendum to TikZ by Henri Menke, it is possible to more systematically distinguish between visible and hidden faces. This recent addendum allows us to retrieve the 3d coordinates of a symbolic point. These coordinates can then be used for vector operations.

  1. The vertices and their connections are obtained with Mathematica using N[PolyhedronData["TruncatedIcosahedron", "GraphicsComplex"]].
  2. For each of the faces one can compute an outward pointing normal, nA.
  3. The normal n of the screen is given by the last row of the rotation matrix in equation (2.1) of the tikz=3dplot manual.
  4. If the projection of nA on n is positive, the face is visible, otherwise hidden.

\documentclass[tikz,border=3mm]{standalone}
\usepackage{tikz-3dplot}
\usetikzlibrary{backgrounds}
\makeatletter
% retrieves the 3D coordinates
\def\RawCoord(#1){\csname tikz@dcl@coord@#1\endcsname}%
\def\scalprod#1=#2.#3;{%
\edef\coordA{\RawCoord#2}%
\edef\coordB{\RawCoord#3}%
\pgfmathsetmacro\pgfutil@tmpa{scalarproduct({\coordA},{\coordB})}
\edef#1{\pgfutil@tmpa}}%
\makeatother 
\newcommand{\spaux}[6]{(#1)*(#4)+(#2)*(#5)+(#3)*(#6)}  
\pgfmathdeclarefunction{scalarproduct}{2}{% scalar product of two 3-vectors
  \begingroup%
  \pgfmathparse{\spaux#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}  
% projections
\pgfmathdeclarefunction{xcomp3}{3}{% x component of a 3-vector
\begingroup%
  \pgfmathparse{#1}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{ycomp3}{3}{% y component of a 3-vector
\begingroup%
  \pgfmathparse{#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}  
\pgfmathdeclarefunction{zcomp3}{3}{% z component of a 3-vector
\begingroup%
  \pgfmathparse{#3}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
% allows us to do linear combinations
\def\lincomb#1=#2*#3+#4*#5;{%
\path[overlay] let \p1=#3,\p2=#5 in 
({(#2)*(xcomp3\coord1)+(#4)*(xcomp3\coord2)},%
 {(#2)*(ycomp3\coord1)+(#4)*(ycomp3\coord2)},%
 {(#2)*(zcomp3\coord1)+(#4)*(zcomp3\coord2)}) coordinate #1;}
% vector product
\def\vecprod#1=#2x#3;{%
\path[overlay] let \p1=#2,\p2=#3 in 
 ({vpx({\coord1},{\coord2})},%
 {vpy({\coord1},{\coord2})},%
 {vpz({\coord1},{\coord2})}) coordinate #1;}
% vector product auxiliary functions
\newcommand{\vpauxx}[6]{(#2)*(#6)-(#3)*(#5)}     
\newcommand{\vpauxy}[6]{(#4)*(#3)-(#1)*(#6)}
\newcommand{\vpauxz}[6]{(#1)*(#5)-(#2)*(#4)}
% vector product pgf functions
\pgfmathdeclarefunction{vpx}{2}{% x component of vector product
  \begingroup%
  \pgfmathparse{\vpauxx#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{vpy}{2}{% y component of vector product
  \begingroup%
  \pgfmathparse{\vpauxy#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{vpz}{2}{% z component of vector product
  \begingroup%
  \pgfmathparse{\vpauxz#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}

\begin{document}
\tdplotsetmaincoords{70}{0}
\begin{tikzpicture}[tdplot_main_coords,line cap=round]
\path foreach \Coord [count=\X] in {(-0.16246,-2.11803,1.27598),
 (-0.16246,2.11803,1.27598),(0.16246,-2.11803,-1.27598),(0.16246,2.11803,-1.27598),
 (-0.262866,-0.809017,-2.32744),(-0.262866,-2.42705,-0.425325),(-0.262866,0.809017,-2.32744),
 (-0.262866,2.42705,-0.425325),(0.262866,-0.809017,2.32744),(0.262866,-2.42705,0.425325),
 (0.262866,0.809017,2.32744),(0.262866,2.42705,0.425325),(0.688191,-0.5,-2.32744),
 (0.688191,0.5,-2.32744),(1.21392,-2.11803,0.425325),(1.21392,2.11803,0.425325),
 (-2.06457,-0.5,1.27598),(-2.06457,0.5,1.27598),(-1.37638,-1.,1.80171),
 (-1.37638,1.,1.80171),(-1.37638,-1.61803,-1.27598),(-1.37638,1.61803,-1.27598),
 (-0.688191,-0.5,2.32744),(-0.688191,0.5,2.32744),(1.37638,-1.,-1.80171),
 (1.37638,1.,-1.80171),(1.37638,-1.61803,1.27598),(1.37638,1.61803,1.27598),
 (-1.7013,0.,-1.80171),(1.7013,0.,1.80171),(-1.21392,-2.11803,-0.425325),
 (-1.21392,2.11803,-0.425325),(-1.96417,-0.809017,-1.27598),(-1.96417,0.809017,-1.27598),
 (2.06457,-0.5,-1.27598),(2.06457,0.5,-1.27598),(2.22703,-1.,-0.425325),
 (2.22703,1.,-0.425325),(2.38949,-0.5,0.425325),(2.38949,0.5,0.425325),
 (-1.11352,-1.80902,1.27598),(-1.11352,1.80902,1.27598),(1.11352,-1.80902,-1.27598),
 (1.11352,1.80902,-1.27598),(-2.38949,-0.5,-0.425325),(-2.38949,0.5,-0.425325),
 (-1.63925,-1.80902,0.425325),(-1.63925,1.80902,0.425325),(1.63925,-1.80902,-0.425325),
 (1.63925,1.80902,-0.425325),(1.96417,-0.809017,1.27598),(1.96417,0.809017,1.27598),
 (0.850651,0.,2.32744),(-2.22703,-1.,0.425325),(-2.22703,1.,0.425325),
 (-0.850651,0.,-2.32744),(-0.525731,-1.61803,-1.80171),(-0.525731,1.61803,-1.80171),
 (0.525731,-1.61803,1.80171),(0.525731,1.61803,1.80171)}
  {\Coord coordinate (p\X) \pgfextra{\xdef\NumVertices{\X}}};
 %\message{number of vertices is \NumVertices^^J} 
 % normal of screen 
 \path[overlay] ({sin(\tdplotmaintheta)*sin(\tdplotmainphi)},
    {-1*sin(\tdplotmaintheta)*cos(\tdplotmainphi)},
    {cos(\tdplotmaintheta)}) coordinate (n); 
 \foreach \poly in 
 {{53, 11, 24, 23, 9}, {51, 39, 40, 52, 30}, {60, 28, 16, 12, 2}, {20, 
  42, 48, 55, 18}, {19, 17, 54, 47, 41}, {1, 10, 15, 27, 59}, {36, 26,
   44, 50, 38}, {4, 58, 22, 32, 8}, {34, 29, 33, 45, 46}, {21, 57, 3, 
  6, 31}, {37, 49, 43, 25, 35}, {13, 5, 56, 7, 14}, {9, 59, 27, 51, 
  30, 53}, {53, 30, 52, 28, 60, 11}, {11, 60, 2, 42, 20, 24}, {24, 20,
   18, 17, 19, 23}, {23, 19, 41, 1, 59, 9}, {13, 25, 43, 3, 57, 
  5}, {5, 57, 21, 33, 29, 56}, {56, 29, 34, 22, 58, 7}, {7, 58, 4, 44,
   26, 14}, {14, 26, 36, 35, 25, 13}, {40, 38, 50, 16, 28, 52}, {16, 
  50, 44, 4, 8, 12}, {12, 8, 32, 48, 42, 2}, {48, 32, 22, 34, 46, 
  55}, {55, 46, 45, 54, 17, 18}, {54, 45, 33, 21, 31, 47}, {47, 31, 6,
   10, 1, 41}, {10, 6, 3, 43, 49, 15}, {15, 49, 37, 39, 51, 27}, {39, 
  37, 35, 36, 38, 40}}
 {\pgfmathtruncatemacro{\ione}{{\poly}[0]}
  \pgfmathtruncatemacro{\itwo}{{\poly}[1]}
  \pgfmathtruncatemacro{\ithree}{{\poly}[2]}
  \lincomb(dA)=1*(p\itwo)+(-1)*(p\ione);
  \lincomb(dB)=1*(p\itwo)+(-1)*(p\ithree);
  % normal of local current polygon 
  \vecprod(nA)=(dA)x(dB);
  \scalprod\nproj=(nA).(p\ione);
  \pgfmathtruncatemacro{\jtest}{sign(\nproj)}
  % make sure that the normal points outwards
  \ifnum\jtest<0
   \vecprod(nA)=(dB)x(dA);
  \fi
  % compute projection the normal of the polygon on the normal of screen    
  \scalprod\myproj=(nA).(n);
  \pgfmathtruncatemacro{\itest}{sign(\myproj)}
  \ifnum\itest>-1 
   \draw[thick] plot[samples at=\poly,variable=\x](p\x) -- cycle; 
  \else
   \begin{scope}[on background layer] 
    \draw[gray!20] plot[samples at=\poly,variable=\x](p\x) -- cycle;  
   \end{scope}
  \fi
  }  
\end{tikzpicture}
\end{document}

enter image description here

That way, the view angles can be chosen at will, as demonstrated in this animation.

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{tikz-3dplot}
\usetikzlibrary{backgrounds}
\makeatletter
% retrieves the 3D coordinates
\def\RawCoord(#1){\csname tikz@dcl@coord@#1\endcsname}%
\def\scalprod#1=#2.#3;{%
\edef\coordA{\RawCoord#2}%
\edef\coordB{\RawCoord#3}%
\pgfmathsetmacro\pgfutil@tmpa{scalarproduct({\coordA},{\coordB})}
\edef#1{\pgfutil@tmpa}}%
\makeatother 
\newcommand{\spaux}[6]{(#1)*(#4)+(#2)*(#5)+(#3)*(#6)}  
\pgfmathdeclarefunction{scalarproduct}{2}{% scalar product of two 3-vectors
  \begingroup%
  \pgfmathparse{\spaux#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}  
% projections
\pgfmathdeclarefunction{xcomp3}{3}{% x component of a 3-vector
\begingroup%
  \pgfmathparse{#1}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{ycomp3}{3}{% y component of a 3-vector
\begingroup%
  \pgfmathparse{#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}  
\pgfmathdeclarefunction{zcomp3}{3}{% z component of a 3-vector
\begingroup%
  \pgfmathparse{#3}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
% allows us to do linear combinations
\def\lincomb#1=#2*#3+#4*#5;{%
\path[overlay] let \p1=#3,\p2=#5 in 
({(#2)*(xcomp3\coord1)+(#4)*(xcomp3\coord2)},%
 {(#2)*(ycomp3\coord1)+(#4)*(ycomp3\coord2)},%
 {(#2)*(zcomp3\coord1)+(#4)*(zcomp3\coord2)}) coordinate #1;}
% vector product
\def\vecprod#1=#2x#3;{%
\path[overlay] let \p1=#2,\p2=#3 in 
 ({vpx({\coord1},{\coord2})},%
 {vpy({\coord1},{\coord2})},%
 {vpz({\coord1},{\coord2})}) coordinate #1;}
% vector product auxiliary functions
\newcommand{\vpauxx}[6]{(#2)*(#6)-(#3)*(#5)}     
\newcommand{\vpauxy}[6]{(#4)*(#3)-(#1)*(#6)}
\newcommand{\vpauxz}[6]{(#1)*(#5)-(#2)*(#4)}
% vector product pgf functions
\pgfmathdeclarefunction{vpx}{2}{% x component of vector product
  \begingroup%
  \pgfmathparse{\vpauxx#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{vpy}{2}{% y component of vector product
  \begingroup%
  \pgfmathparse{\vpauxy#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{vpz}{2}{% z component of vector product
  \begingroup%
  \pgfmathparse{\vpauxz#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}

\begin{document}
\foreach \Angle in {0,10,...,350}
{\tdplotsetmaincoords{90+30*sin(\Angle)}{\Angle}
\begin{tikzpicture}[tdplot_main_coords,line cap=round]
\path[tdplot_screen_coords,use as bounding box] (-3,-3) rectangle (3,3);
\path foreach \Coord [count=\X] in {(-0.16246,-2.11803,1.27598),
 (-0.16246,2.11803,1.27598),(0.16246,-2.11803,-1.27598),(0.16246,2.11803,-1.27598),
 (-0.262866,-0.809017,-2.32744),(-0.262866,-2.42705,-0.425325),(-0.262866,0.809017,-2.32744),
 (-0.262866,2.42705,-0.425325),(0.262866,-0.809017,2.32744),(0.262866,-2.42705,0.425325),
 (0.262866,0.809017,2.32744),(0.262866,2.42705,0.425325),(0.688191,-0.5,-2.32744),
 (0.688191,0.5,-2.32744),(1.21392,-2.11803,0.425325),(1.21392,2.11803,0.425325),
 (-2.06457,-0.5,1.27598),(-2.06457,0.5,1.27598),(-1.37638,-1.,1.80171),
 (-1.37638,1.,1.80171),(-1.37638,-1.61803,-1.27598),(-1.37638,1.61803,-1.27598),
 (-0.688191,-0.5,2.32744),(-0.688191,0.5,2.32744),(1.37638,-1.,-1.80171),
 (1.37638,1.,-1.80171),(1.37638,-1.61803,1.27598),(1.37638,1.61803,1.27598),
 (-1.7013,0.,-1.80171),(1.7013,0.,1.80171),(-1.21392,-2.11803,-0.425325),
 (-1.21392,2.11803,-0.425325),(-1.96417,-0.809017,-1.27598),(-1.96417,0.809017,-1.27598),
 (2.06457,-0.5,-1.27598),(2.06457,0.5,-1.27598),(2.22703,-1.,-0.425325),
 (2.22703,1.,-0.425325),(2.38949,-0.5,0.425325),(2.38949,0.5,0.425325),
 (-1.11352,-1.80902,1.27598),(-1.11352,1.80902,1.27598),(1.11352,-1.80902,-1.27598),
 (1.11352,1.80902,-1.27598),(-2.38949,-0.5,-0.425325),(-2.38949,0.5,-0.425325),
 (-1.63925,-1.80902,0.425325),(-1.63925,1.80902,0.425325),(1.63925,-1.80902,-0.425325),
 (1.63925,1.80902,-0.425325),(1.96417,-0.809017,1.27598),(1.96417,0.809017,1.27598),
 (0.850651,0.,2.32744),(-2.22703,-1.,0.425325),(-2.22703,1.,0.425325),
 (-0.850651,0.,-2.32744),(-0.525731,-1.61803,-1.80171),(-0.525731,1.61803,-1.80171),
 (0.525731,-1.61803,1.80171),(0.525731,1.61803,1.80171)}
  {\Coord coordinate (p\X) \pgfextra{\xdef\NumVertices{\X}}};
 %\message{number of vertices is \NumVertices^^J} 
 % normal of screen 
 \path[overlay] ({sin(\tdplotmaintheta)*sin(\tdplotmainphi)},
    {-1*sin(\tdplotmaintheta)*cos(\tdplotmainphi)},
    {cos(\tdplotmaintheta)}) coordinate (n);
 \foreach \poly in 
 {{53, 11, 24, 23, 9}, {51, 39, 40, 52, 30}, {60, 28, 16, 12, 2}, {20, 
  42, 48, 55, 18}, {19, 17, 54, 47, 41}, {1, 10, 15, 27, 59}, {36, 26,
   44, 50, 38}, {4, 58, 22, 32, 8}, {34, 29, 33, 45, 46}, {21, 57, 3, 
  6, 31}, {37, 49, 43, 25, 35}, {13, 5, 56, 7, 14}, {9, 59, 27, 51, 
  30, 53}, {53, 30, 52, 28, 60, 11}, {11, 60, 2, 42, 20, 24}, {24, 20,
   18, 17, 19, 23}, {23, 19, 41, 1, 59, 9}, {13, 25, 43, 3, 57, 
  5}, {5, 57, 21, 33, 29, 56}, {56, 29, 34, 22, 58, 7}, {7, 58, 4, 44,
   26, 14}, {14, 26, 36, 35, 25, 13}, {40, 38, 50, 16, 28, 52}, {16, 
  50, 44, 4, 8, 12}, {12, 8, 32, 48, 42, 2}, {48, 32, 22, 34, 46, 
  55}, {55, 46, 45, 54, 17, 18}, {54, 45, 33, 21, 31, 47}, {47, 31, 6,
   10, 1, 41}, {10, 6, 3, 43, 49, 15}, {15, 49, 37, 39, 51, 27}, {39, 
  37, 35, 36, 38, 40}}
 {\pgfmathtruncatemacro{\ione}{{\poly}[0]}
  \pgfmathtruncatemacro{\itwo}{{\poly}[1]}
  \pgfmathtruncatemacro{\ithree}{{\poly}[2]}
  \lincomb(dA)=1*(p\itwo)+(-1)*(p\ione);
  \lincomb(dB)=1*(p\itwo)+(-1)*(p\ithree);
  % normal of local current polygon 
  \vecprod(nA)=(dA)x(dB);
  \scalprod\nproj=(nA).(p\ione);
  \pgfmathtruncatemacro{\jtest}{sign(\nproj)}
  % make sure that the normal points outwards
  \ifnum\jtest<0
   \vecprod(nA)=(dB)x(dA);
  \fi
  % compute projection the normal of the polygon on the normal of screen    
  \scalprod\myproj=(nA).(n);
  \pgfmathtruncatemacro{\itest}{sign(\myproj)}
  \ifnum\itest>-1 
   \draw[thick] plot[samples at=\poly,variable=\x](p\x) -- cycle; 
  \else
   \begin{scope}[on background layer] 
    \draw[gray!20] plot[samples at=\poly,variable=\x](p\x) -- cycle;  
   \end{scope}
  \fi
  }  
\end{tikzpicture}}
\end{document}

enter image description here

Remarks:

  1. This automatic discrimination of visible vs. hidden faces works of course for arbitrary polyhedra, and does not require Mathematica or any other external program. It only requires the knowledge of the 3d locations of the vertices.
  2. I am not a chemist. If there is a simple rule where double lines should be, this can be added.
  3. It might be worthwhile to improve the parsing commands for scalar and vector products and so on, and to store them in a library. Unfortunately, I am not a parsing expert, and even if I was I could not add a library since I cannot deal with GitHub.

ADDENDUM: Looking at mol2fig, which has been kindly provided by quark67 (wait, why do a quark and Schrödinger's cat have to do all this chemistry? ;-) I guessed a rule for the double lines. I am not a chemist, so most likely this is wrong. The rule I guessed is that each hexagon has to have 3 double lines. In order not to have triple lines, we need a membership test for the vertices, for which I slightly improved (?) memberQ. Also it helps to be able to compute the dimensions of the arrays without adding extra \foreachs. To this end, I employ an improved (?) version of the undocumented dim function which comes with pgfmathfunctions.misc.code.tex, memberQ is also based on this function. The result is (with major credits going to quark67)

\documentclass[tikz,border=3mm]{standalone}
\usepackage{tikz-3dplot}
\usetikzlibrary{backgrounds}
\makeatletter
% slightly improved (?) version of dim from pgfmathfunctions.misc.code.tex
% at least in this application dim does not give the right results
\pgfmathdeclarefunction{mdim}{1}{%
  \begingroup
    \pgfmath@count=0\relax
    \expandafter\pgfmath@mdim@i#1\pgfmath@token@stop
    \edef\pgfmathresult{\the\pgfmath@count}%
    \pgfmath@smuggleone\pgfmathresult%
  \endgroup}
\def\pgfmath@mdim@i#1{%
    \ifx\pgfmath@token@stop#1%
    \else
      \advance\pgfmath@count by 1\relax
      \expandafter\pgfmath@mdim@i
    \fi}  
%membership test    
\pgfmathdeclarefunction{memberQ}{2}{%
  \begingroup%
    \edef\pgfutil@tmpb{0}%memberQ({\lstPast},\inow)
    \edef\pgfutil@tmpa{#2}%
    \expandafter\pgfmath@member@i#1\pgfmath@token@stop
    \edef\pgfmathresult{\pgfutil@tmpb}%
    \pgfmath@smuggleone\pgfmathresult%
  \endgroup}
\def\pgfmath@member@i#1{%
    \ifx\pgfmath@token@stop#1%
    \else
      \edef\pgfutil@tmpc{#1}%
      \ifx\pgfutil@tmpc\pgfutil@tmpa\relax%
      \gdef\pgfutil@tmpb{1}%
      \fi%
      \expandafter\pgfmath@member@i
    \fi}        
% retrieves the 3D coordinates
\def\RawCoord(#1){\csname tikz@dcl@coord@#1\endcsname}%
\def\scalprod#1=#2.#3;{%
\edef\coordA{\RawCoord#2}%
\edef\coordB{\RawCoord#3}%
\pgfmathsetmacro\pgfutil@tmpa{scalarproduct({\coordA},{\coordB})}
\edef#1{\pgfutil@tmpa}}%
\makeatother 
\newcommand{\spaux}[6]{(#1)*(#4)+(#2)*(#5)+(#3)*(#6)}  
\pgfmathdeclarefunction{scalarproduct}{2}{% scalar product of two 3-vectors
  \begingroup%
  \pgfmathparse{\spaux#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}  
% projections
\pgfmathdeclarefunction{xcomp3}{3}{% x component of a 3-vector
\begingroup%
  \pgfmathparse{#1}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{ycomp3}{3}{% y component of a 3-vector
\begingroup%
  \pgfmathparse{#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}  
\pgfmathdeclarefunction{zcomp3}{3}{% z component of a 3-vector
\begingroup%
  \pgfmathparse{#3}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
% allows us to do linear combinations
\def\lincomb#1=#2*#3+#4*#5;{%
\path[overlay] let \p1=#3,\p2=#5 in 
({(#2)*(xcomp3\coord1)+(#4)*(xcomp3\coord2)},%
 {(#2)*(ycomp3\coord1)+(#4)*(ycomp3\coord2)},%
 {(#2)*(zcomp3\coord1)+(#4)*(zcomp3\coord2)}) coordinate #1;}
% vector product
\def\vecprod#1=#2x#3;{%
\path[overlay] let \p1=#2,\p2=#3 in 
 ({vpx({\coord1},{\coord2})},%
 {vpy({\coord1},{\coord2})},%
 {vpz({\coord1},{\coord2})}) coordinate #1;}
% vector product auxiliary functions
\newcommand{\vpauxx}[6]{(#2)*(#6)-(#3)*(#5)}     
\newcommand{\vpauxy}[6]{(#4)*(#3)-(#1)*(#6)}
\newcommand{\vpauxz}[6]{(#1)*(#5)-(#2)*(#4)}
% vector product pgf functions
\pgfmathdeclarefunction{vpx}{2}{% x component of vector product
  \begingroup%
  \pgfmathparse{\vpauxx#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{vpy}{2}{% y component of vector product
  \begingroup%
  \pgfmathparse{\vpauxy#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{vpz}{2}{% z component of vector product
  \begingroup%
  \pgfmathparse{\vpauxz#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}

\begin{document}
\tdplotsetmaincoords{70}{0}
\begin{tikzpicture}[tdplot_main_coords,line cap=round,line join=round]
\path foreach \Coord [count=\X] in {(-0.16246,-2.11803,1.27598),
 (-0.16246,2.11803,1.27598),(0.16246,-2.11803,-1.27598),(0.16246,2.11803,-1.27598),
 (-0.262866,-0.809017,-2.32744),(-0.262866,-2.42705,-0.425325),(-0.262866,0.809017,-2.32744),
 (-0.262866,2.42705,-0.425325),(0.262866,-0.809017,2.32744),(0.262866,-2.42705,0.425325),
 (0.262866,0.809017,2.32744),(0.262866,2.42705,0.425325),(0.688191,-0.5,-2.32744),
 (0.688191,0.5,-2.32744),(1.21392,-2.11803,0.425325),(1.21392,2.11803,0.425325),
 (-2.06457,-0.5,1.27598),(-2.06457,0.5,1.27598),(-1.37638,-1.,1.80171),
 (-1.37638,1.,1.80171),(-1.37638,-1.61803,-1.27598),(-1.37638,1.61803,-1.27598),
 (-0.688191,-0.5,2.32744),(-0.688191,0.5,2.32744),(1.37638,-1.,-1.80171),
 (1.37638,1.,-1.80171),(1.37638,-1.61803,1.27598),(1.37638,1.61803,1.27598),
 (-1.7013,0.,-1.80171),(1.7013,0.,1.80171),(-1.21392,-2.11803,-0.425325),
 (-1.21392,2.11803,-0.425325),(-1.96417,-0.809017,-1.27598),(-1.96417,0.809017,-1.27598),
 (2.06457,-0.5,-1.27598),(2.06457,0.5,-1.27598),(2.22703,-1.,-0.425325),
 (2.22703,1.,-0.425325),(2.38949,-0.5,0.425325),(2.38949,0.5,0.425325),
 (-1.11352,-1.80902,1.27598),(-1.11352,1.80902,1.27598),(1.11352,-1.80902,-1.27598),
 (1.11352,1.80902,-1.27598),(-2.38949,-0.5,-0.425325),(-2.38949,0.5,-0.425325),
 (-1.63925,-1.80902,0.425325),(-1.63925,1.80902,0.425325),(1.63925,-1.80902,-0.425325),
 (1.63925,1.80902,-0.425325),(1.96417,-0.809017,1.27598),(1.96417,0.809017,1.27598),
 (0.850651,0.,2.32744),(-2.22703,-1.,0.425325),(-2.22703,1.,0.425325),
 (-0.850651,0.,-2.32744),(-0.525731,-1.61803,-1.80171),(-0.525731,1.61803,-1.80171),
 (0.525731,-1.61803,1.80171),(0.525731,1.61803,1.80171)}
  {\Coord coordinate (p\X) \pgfextra{\xdef\NumVertices{\X}}};
 %\message{number of vertices is \NumVertices^^J} 
 % normal of screen 
 \path[overlay] ({sin(\tdplotmaintheta)*sin(\tdplotmainphi)},
    {-1*sin(\tdplotmaintheta)*cos(\tdplotmainphi)},
    {cos(\tdplotmaintheta)}) coordinate (n); 
 \edef\lstPast{0}
 \foreach \poly in 
 {{53, 11, 24, 23, 9}, {51, 39, 40, 52, 30}, {60, 28, 16, 12, 2}, {20, 
  42, 48, 55, 18}, {19, 17, 54, 47, 41}, {1, 10, 15, 27, 59}, {36, 26,
   44, 50, 38}, {4, 58, 22, 32, 8}, {34, 29, 33, 45, 46}, {21, 57, 3, 
  6, 31}, {37, 49, 43, 25, 35}, {13, 5, 56, 7, 14}, {9, 59, 27, 51, 
  30, 53}, {53, 30, 52, 28, 60, 11}, {11, 60, 2, 42, 20, 24}, {24, 20,
   18, 17, 19, 23}, {23, 19, 41, 1, 59, 9}, {13, 25, 43, 3, 57, 
  5}, {5, 57, 21, 33, 29, 56}, {56, 29, 34, 22, 58, 7}, {7, 58, 4, 44,
   26, 14}, {14, 26, 36, 35, 25, 13}, {40, 38, 50, 16, 28, 52}, {16, 
  50, 44, 4, 8, 12}, {12, 8, 32, 48, 42, 2}, {48, 32, 22, 34, 46, 
  55}, {55, 46, 45, 54, 17, 18}, {54, 45, 33, 21, 31, 47}, {47, 31, 6,
   10, 1, 41}, {10, 6, 3, 43, 49, 15}, {15, 49, 37, 39, 51, 27}, {39, 
  37, 35, 36, 38, 40}}
 {\pgfmathtruncatemacro{\ione}{{\poly}[0]}
  \pgfmathtruncatemacro{\itwo}{{\poly}[1]}
  \pgfmathtruncatemacro{\ithree}{{\poly}[2]}
  \lincomb(dA)=1*(p\itwo)+(-1)*(p\ione);
  \lincomb(dB)=1*(p\itwo)+(-1)*(p\ithree);
  % normal of local current polygon 
  \vecprod(nA)=(dA)x(dB);
  \scalprod\nproj=(nA).(p\ione);
  \pgfmathtruncatemacro{\jtest}{sign(\nproj)}
  % make sure that the normal points outwards
  \ifnum\jtest<0
   \vecprod(nA)=(dB)x(dA);
  \fi
  % compute projection the normal of the polygon on the normal of screen    
  \scalprod\myproj=(nA).(n);
  \pgfmathtruncatemacro{\itest}{sign(\myproj)}
  \ifnum\itest>-1 
   \draw[thick] plot[samples at=\poly,variable=\x](p\x) -- cycle; 
  \else
   \begin{scope}[on background layer] 
    \draw[gray!20] plot[samples at=\poly,variable=\x](p\x) -- cycle;  
   \end{scope}
  \fi
  \pgfmathtruncatemacro{\mydim}{mdim(\poly)}
  \ifnum\mydim=6
   \foreach \XX in {0,...,5} {\pgfmathtruncatemacro{\YY}{{\poly}[\XX]}
   \path (p\YY) coordinate (aux\XX);}
   \path (barycentric cs:aux0=1,aux1=1,aux2=1,aux3=1,aux4=1,aux5=1) 
    coordinate (aux);
   \ifnum\itest>-1 
    \foreach \XX in {0,2,4}
     {\pgfmathtruncatemacro{\inow}{{\poly}[\XX]}
      \pgfmathtruncatemacro{\inext}{{\poly}[\XX+1]}
      \pgfmathtruncatemacro{\ktest}{memberQ({\lstPast},\inow)+memberQ({\lstPast},\inext)}
      \ifnum\ktest=0
       \draw[thick] ($(p\inow)!0.1!(aux)$) --
        ($(p\inext)!0.1!(aux)$);
      \fi}
   \else
    \begin{scope}[on background layer] 
    \foreach \XX in {0,2,4}
     {\pgfmathtruncatemacro{\inow}{{\poly}[\XX]}
      \pgfmathtruncatemacro{\inext}{{\poly}[\XX+1]}
      \pgfmathtruncatemacro{\ktest}{memberQ({\lstPast},\inow)+memberQ({\lstPast},\inext)}
      \ifnum\ktest=0
       \draw[gray!20] ($(aux\XX)!0.1!(aux)$) --   ($(aux\the\numexpr\XX+1)!0.1!(aux)$);
      \fi}
    \end{scope}
   \fi
   % keep track of past vertices such that we avoid triple lines
   \foreach \VV in \poly
   {\xdef\lstPast{\lstPast,\VV}} 
  \fi
  }  
\end{tikzpicture}
\end{document}

enter image description here

Like the above, this is fully rotatable. (I added the animation here since there is not enough space.)

| improve this answer | |
  • 1
    @Brasil I am aware of this. As I wrote, if there is a (preferably simple) prescription that indicates where to put double lines, this can be implemented. If you expect me to try to infer the rules from the literature, I regret to disappoint you. (I doubt that chemfig has such molecules built in, but might well be wrong.) – user194703 Sep 1 '19 at 19:40
  • 1
    Just checked the example library of chemfig. The main issue is that chemig mostly supports 2D drawings. – BambOo Sep 1 '19 at 19:49
  • 2
    @Schrödinger'scat Perhaps the package mol2chemfig can help. It has a web interface at py-chemist.com/mol_2_chemfig/home. In the search field, enter buckminsterfullerene (the real name of the molecule C60) + ENTER key, wait until the raw chemical formula is displayed, then click CONVERT. You obtain the chemfig code and a preview of the output. Great, but unfortunately the chemfig code must be tweaked in order to hide the invisible faces... – quark67 Sep 2 '19 at 1:01
  • 1
    @quark67 Thanks! I thin you could write a separate answer on this. I personally do not really care so much about the chemistry application but about how far one can tweak TikZ to provide us with some 3d-like features. – user194703 Sep 2 '19 at 1:24
  • 2
    @Schrödinger'scat Very great work! Your assumption seems true according to this Wikimedia picture of buckminsterfullerene. As Carbon is tetravalent, all vertex (position of carbon atom) must have 4 bounds (a double bound is 2 bounds). Your picture is so correct. – quark67 Sep 2 '19 at 17:37
5

Sorry for the additional answer, there is not enough space in the above one to add this. (I did make some minor efforts in explaining the code through comments.)

\documentclass[tikz,border=3mm]{standalone}
\usepackage{tikz-3dplot}
\usetikzlibrary{backgrounds}
\makeatletter
% slightly improved (?) version of dim from pgfmathfunctions.misc.code.tex
% at least in this application dim does not give the right results
% it is far from perfect 
% the problem with both variants is the last item
\pgfmathdeclarefunction{mdim}{1}{%
  \begingroup
    \pgfmath@count=0\relax
    \expandafter\pgfmath@mdim@i#1\pgfmath@token@stop
    \edef\pgfmathresult{\the\pgfmath@count}%
    \pgfmath@smuggleone\pgfmathresult%
  \endgroup}
\def\pgfmath@mdim@i#1{%
    \ifx\pgfmath@token@stop#1%
    \else
      \advance\pgfmath@count by 1\relax
      \expandafter\pgfmath@mdim@i
    \fi}  
%membership test    
\pgfmathdeclarefunction{memberQ}{2}{%
  \begingroup%
    \edef\pgfutil@tmpb{0}%memberQ({\lstPast},\inow)
    \edef\pgfutil@tmpa{#2}%
    \expandafter\pgfmath@member@i#1\pgfmath@token@stop
    \edef\pgfmathresult{\pgfutil@tmpb}%
    \pgfmath@smuggleone\pgfmathresult%
  \endgroup}
\def\pgfmath@member@i#1{%
    \ifx\pgfmath@token@stop#1%
    \else
      \edef\pgfutil@tmpc{#1}%
      \ifx\pgfutil@tmpc\pgfutil@tmpa\relax%
      \gdef\pgfutil@tmpb{1}%
      \fi%
      \expandafter\pgfmath@member@i
    \fi}        
% retrieves the 3D coordinates
\def\RawCoord(#1){\csname tikz@dcl@coord@#1\endcsname}%
\def\scalprod#1=#2.#3;{%
\edef\coordA{\RawCoord#2}%
\edef\coordB{\RawCoord#3}%
\pgfmathsetmacro\pgfutil@tmpa{scalarproduct({\coordA},{\coordB})}
\edef#1{\pgfutil@tmpa}}%
\makeatother 
\newcommand{\spaux}[6]{(#1)*(#4)+(#2)*(#5)+(#3)*(#6)}  
\pgfmathdeclarefunction{scalarproduct}{2}{% scalar product of two 3-vectors
  \begingroup%
  \pgfmathparse{\spaux#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}  
% projections
\pgfmathdeclarefunction{xcomp3}{3}{% x component of a 3-vector
\begingroup%
  \pgfmathparse{#1}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{ycomp3}{3}{% y component of a 3-vector
\begingroup%
  \pgfmathparse{#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}  
\pgfmathdeclarefunction{zcomp3}{3}{% z component of a 3-vector
\begingroup%
  \pgfmathparse{#3}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
% allows us to do linear combinations
\def\lincomb#1=#2*#3+#4*#5;{%
\path[overlay] let \p1=#3,\p2=#5 in 
({(#2)*(xcomp3\coord1)+(#4)*(xcomp3\coord2)},%
 {(#2)*(ycomp3\coord1)+(#4)*(ycomp3\coord2)},%
 {(#2)*(zcomp3\coord1)+(#4)*(zcomp3\coord2)}) coordinate #1;}
% vector product
\def\vecprod#1=#2x#3;{%
\path[overlay] let \p1=#2,\p2=#3 in 
 ({vpx({\coord1},{\coord2})},%
 {vpy({\coord1},{\coord2})},%
 {vpz({\coord1},{\coord2})}) coordinate #1;}
% vector product auxiliary functions
\newcommand{\vpauxx}[6]{(#2)*(#6)-(#3)*(#5)}     
\newcommand{\vpauxy}[6]{(#4)*(#3)-(#1)*(#6)}
\newcommand{\vpauxz}[6]{(#1)*(#5)-(#2)*(#4)}
% vector product pgf functions
\pgfmathdeclarefunction{vpx}{2}{% x component of vector product
  \begingroup%
  \pgfmathparse{\vpauxx#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{vpy}{2}{% y component of vector product
  \begingroup%
  \pgfmathparse{\vpauxy#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}
\pgfmathdeclarefunction{vpz}{2}{% z component of vector product
  \begingroup%
  \pgfmathparse{\vpauxz#1#2}%
  \pgfmathsmuggle\pgfmathresult\endgroup}

\begin{document}
\foreach \Angle in {0,5,...,355}
{\tdplotsetmaincoords{90+30*sin(2*\Angle)}{\Angle}
 \begin{tikzpicture}[tdplot_main_coords,line cap=round,line join=round]
  \path[tdplot_screen_coords,use as bounding box] (-3,-3) rectangle (3,3);
  % define the vertices, they are generated by Mathematica
  \edef\lstVertices{(-0.16246,-2.11803,1.27598),
 (-0.16246,2.11803,1.27598),(0.16246,-2.11803,-1.27598),(0.16246,2.11803,-1.27598),
 (-0.262866,-0.809017,-2.32744),(-0.262866,-2.42705,-0.425325),(-0.262866,0.809017,-2.32744),
 (-0.262866,2.42705,-0.425325),(0.262866,-0.809017,2.32744),(0.262866,-2.42705,0.425325),
 (0.262866,0.809017,2.32744),(0.262866,2.42705,0.425325),(0.688191,-0.5,-2.32744),
 (0.688191,0.5,-2.32744),(1.21392,-2.11803,0.425325),(1.21392,2.11803,0.425325),
 (-2.06457,-0.5,1.27598),(-2.06457,0.5,1.27598),(-1.37638,-1.,1.80171),
 (-1.37638,1.,1.80171),(-1.37638,-1.61803,-1.27598),(-1.37638,1.61803,-1.27598),
 (-0.688191,-0.5,2.32744),(-0.688191,0.5,2.32744),(1.37638,-1.,-1.80171),
 (1.37638,1.,-1.80171),(1.37638,-1.61803,1.27598),(1.37638,1.61803,1.27598),
 (-1.7013,0.,-1.80171),(1.7013,0.,1.80171),(-1.21392,-2.11803,-0.425325),
 (-1.21392,2.11803,-0.425325),(-1.96417,-0.809017,-1.27598),(-1.96417,0.809017,-1.27598),
 (2.06457,-0.5,-1.27598),(2.06457,0.5,-1.27598),(2.22703,-1.,-0.425325),
 (2.22703,1.,-0.425325),(2.38949,-0.5,0.425325),(2.38949,0.5,0.425325),
 (-1.11352,-1.80902,1.27598),(-1.11352,1.80902,1.27598),(1.11352,-1.80902,-1.27598),
 (1.11352,1.80902,-1.27598),(-2.38949,-0.5,-0.425325),(-2.38949,0.5,-0.425325),
 (-1.63925,-1.80902,0.425325),(-1.63925,1.80902,0.425325),(1.63925,-1.80902,-0.425325),
 (1.63925,1.80902,-0.425325),(1.96417,-0.809017,1.27598),(1.96417,0.809017,1.27598),
 (0.850651,0.,2.32744),(-2.22703,-1.,0.425325),(-2.22703,1.,0.425325),
 (-0.850651,0.,-2.32744),(-0.525731,-1.61803,-1.80171),(-0.525731,1.61803,-1.80171),
 (0.525731,-1.61803,1.80171),(0.525731,1.61803,1.80171)}
 % the faces are generated with Mathematica, too
 \edef\lstFaces{{53, 11, 24, 23, 9}, {51, 39, 40, 52, 30}, {60, 28, 16, 12, 2}, {20, 
  42, 48, 55, 18}, {19, 17, 54, 47, 41}, {1, 10, 15, 27, 59}, {36, 26,
   44, 50, 38}, {4, 58, 22, 32, 8}, {34, 29, 33, 45, 46}, {21, 57, 3, 
  6, 31}, {37, 49, 43, 25, 35}, {13, 5, 56, 7, 14}, {9, 59, 27, 51, 
  30, 53}, {53, 30, 52, 28, 60, 11}, {11, 60, 2, 42, 20, 24}, {24, 20,
   18, 17, 19, 23}, {23, 19, 41, 1, 59, 9}, {13, 25, 43, 3, 57, 
  5}, {5, 57, 21, 33, 29, 56}, {56, 29, 34, 22, 58, 7}, {7, 58, 4, 44,
   26, 14}, {14, 26, 36, 35, 25, 13}, {40, 38, 50, 16, 28, 52}, {16, 
  50, 44, 4, 8, 12}, {12, 8, 32, 48, 42, 2}, {48, 32, 22, 34, 46, 
  55}, {55, 46, 45, 54, 17, 18}, {54, 45, 33, 21, 31, 47}, {47, 31, 6,
   10, 1, 41}, {10, 6, 3, 43, 49, 15}, {15, 49, 37, 39, 51, 27}, {39, 
  37, 35, 36, 38, 40}}
 % get the vertices into TikZ 
 % it is important to use the syntax \path (<coordinate>) coordinate (<name>);
 % \coordinate (<name>) at (<coordinate>); won't work 
  \path foreach \Coord [count=\X] in \lstVertices
    {\Coord coordinate (p\X) \pgfextra{\xdef\NumVertices{\X}}};
   %\message{number of vertices is \NumVertices^^J} 
   % normal of screen 
   \path[overlay] ({sin(\tdplotmaintheta)*sin(\tdplotmainphi)},
      {-1*sin(\tdplotmaintheta)*cos(\tdplotmainphi)},
      {cos(\tdplotmaintheta)}) coordinate (n); 
  % this list collects the vertices we already connected
  % its purpose is to avoid double counting   
   \edef\lstPast{0}
  % this is the main drawing routine 
   \foreach \poly in \lstFaces
   {\pgfmathtruncatemacro{\ione}{{\poly}[0]}
    \pgfmathtruncatemacro{\itwo}{{\poly}[1]}
    \pgfmathtruncatemacro{\ithree}{{\poly}[2]}
    \lincomb(dA)=1*(p\itwo)+(-1)*(p\ione);
    \lincomb(dB)=1*(p\itwo)+(-1)*(p\ithree);
    % normal of local current polygon 
    \vecprod(nA)=(dA)x(dB);
    \scalprod\nproj=(nA).(p\ione);
    \pgfmathtruncatemacro{\jtest}{sign(\nproj)}
    % make sure that the normal points outwards
    \ifnum\jtest<0
     \vecprod(nA)=(dB)x(dA);
    \fi
    % compute projection the normal of the polygon on the normal of screen    
    \scalprod\myproj=(nA).(n);
    \pgfmathtruncatemacro{\itest}{sign(\myproj)}
    \ifnum\itest>-1 
     \draw[thick] plot[samples at=\poly,variable=\x](p\x) -- cycle; 
    \else
     \begin{scope}[on background layer] 
      \draw[gray!20] plot[samples at=\poly,variable=\x](p\x) -- cycle;  
     \end{scope}
    \fi
    \pgfmathtruncatemacro{\mydim}{mdim(\poly)}
    \ifnum\mydim=6
     \foreach \XX in {0,...,5} {
     \pgfmathtruncatemacro{\YY}{{\poly}[\XX]}
     \path (p\YY) coordinate (aux\XX);}
     \path (barycentric cs:aux0=1,aux1=1,aux2=1,aux3=1,aux4=1,aux5=1) 
      coordinate (aux);
     \foreach \XX in {0,2,4} 
     {\pgfmathtruncatemacro{\inow}{{\poly}[\XX]}
      \pgfmathtruncatemacro{\inext}{{\poly}[\XX+1]}
      \pgfmathtruncatemacro{\ktest}{memberQ({\lstPast},\inow)+memberQ({\lstPast},\inext)}
      % membership test: if we already have connected the vertex in
      % a hexagon, \ktest will be >0, so no double line
      \ifnum\ktest=0
       \ifnum\itest>-1 
        \draw[thick] ($(p\inow)!0.1!(aux)$) -- ($(p\inext)!0.1!(aux)$);
       \else
        \begin{scope}[on background layer]       
         \draw[gray!20] ($(p\inow)!0.1!(aux)$) -- ($(p\inext)!0.1!(aux)$);
        \end{scope}
       \fi 
      \fi}
     % keep track of past vertices such that we avoid triple lines
     \foreach \VV in \poly
     {\xdef\lstPast{\lstPast,\VV}} 
    \fi
    }  
 \end{tikzpicture}}
\end{document}

enter image description here

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.