6

enter image description here

I am trying to use TiKZ to draw a Borromean knot with 3 Wasp figures. I am getting the wrong superpositions.
A single wasp is produced by the following code. Can someone help me to write a TiKZ code for this Brunnian entanglement with 12 crossings.

Wasp:

\documentclass[border=5mm]{standalone}
\usepackage{tikz}

\begin{document}
\begin{tikzpicture}

    % Estilos para o anel do mosaico
    \tikzset{
        vespa ring/.style={
            draw=red, 
            line width=11pt, 
            line join=round
        },
        white stripe/.style={
            draw=white, 
            line width=2pt,
            % Removida a opacidade para evitar manchas
            line join=round
        }
    }

    % Macro final da "Cintura de Vespa" com cabeça circular e elevada
    \newcommand{\vespawaist}{
        (0.8, 0) % Cintura direita
        .. controls (0.8, 1.0) and (2.0, 1.8) .. (1.5, 2.4) % Ombro
        .. controls (1.0, 3.0) and (-1.0, 3.0) .. (-1.5, 2.4) % Topo elevado
        .. controls (-2.0, 1.8) and (-0.8, 1.0) .. (-0.8, 0) % Cintura esquerda
        .. controls (-0.8, -1.0) and (-2.0, -1.8) .. (-1.5, -2.4) % Ombro inferior
        .. controls (-1.0, -3.0) and (1.0, -3.0) .. (1.5, -2.4) % Base elevada
        .. controls (2.0, -1.8) and (0.8, -1.0) .. (0.8, 0) % Fecha
        -- cycle
    }

    % Desenho em camadas para garantir o branco puro
    \draw[vespa ring] \vespawaist;
    \draw[white stripe] \vespawaist;

\end{tikzpicture}
\end{document}
1
  • 2
    see the knots and spath3 packages. Commented 10 hours ago

2 Answers 2

4

This is not exactly the same as the figure in your picture, but it is very close. If you would like to reproduce the exact version shown there, let me know and I will try to improve it further.

\documentclass[border=5pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{intersections}

\begin{document}

\begin{tikzpicture}[line join=round,line cap=round]

\tikzset{
  ribbon/.style={
    line width=11pt,
    line join=round,
    line cap=round
  },
  stripe/.style={
    draw=white,
    line width=2pt,
    line join=round,
    line cap=round
  }
}

\colorlet{RedCol}{red}
\colorlet{GreenCol}{green!60!black}
\colorlet{BlueCol}{blue!75!black}

% Radius of the local redraw region at each crossing
\def\CrossRad{0.7}

% Outline of a single "wasp" figure
\def\WaspPath{
  (0.8,0)
  .. controls (0.8,1.0) and (2.0,1.8) .. (1.5,2.4)
  .. controls (1.0,3.0) and (-1.0,3.0) .. (-1.5,2.4)
  .. controls (-2.0,1.8) and (-0.8,1.0) .. (-0.8,0)
  .. controls (-0.8,-1.0) and (-2.0,-1.8) .. (-1.5,-2.4)
  .. controls (-1.0,-3.0) and (1.0,-3.0) .. (1.5,-2.4)
  .. controls (2.0,-1.8) and (0.8,-1.0) .. (0.8,0)
  -- cycle
}

% Draw the entire ribbon
% #1 = transformation
% #2 = color
\newcommand{\DrawRibbon}[2]{%
  \begin{scope}[#1]
    \draw[ribbon,draw=#2] \WaspPath;
    \draw[stripe] \WaspPath;
  \end{scope}
}

% Locally redraw the upper ribbon near a single crossing point
% #1 = transformation of the upper figure
% #2 = color of the upper figure
% #3 = name of the intersection point
\newcommand{\TopAt}[3]{%
  \begin{scope}
    \clip (#3) circle[radius=\CrossRad];
    \begin{scope}[#1]
      \draw[ribbon,draw=#2] \WaspPath;
      \draw[stripe] \WaspPath;
    \end{scope}
  \end{scope}
}

% --- Invisible named paths used to find intersections ---
\path[name path=R] \WaspPath;
\path[name path=G,rotate=120] \WaspPath;
\path[name path=B,rotate=240] \WaspPath;

% Intersections:
% rg1..rg4 = R with G
% rb1..rb4 = R with B
% gb1..gb4 = G with B
\path[name intersections={of=R and G, by={rg1,rg2,rg3,rg4}}];
\path[name intersections={of=R and B, by={rb1,rb2,rb3,rb4}}];
\path[name intersections={of=G and B, by={gb1,gb2,gb3,gb4}}];

% --- Initial drawing of all three ribbons ---
\DrawRibbon{}{RedCol}
\DrawRibbon{rotate=120}{GreenCol}
\DrawRibbon{rotate=240}{BlueCol}

% --- Correct over/under order at all 12 crossings ---
% Green goes over red at all four of their crossings
\foreach \P in {rg1,rg2,rg3,rg4}{
  \TopAt{rotate=120}{GreenCol}{\P}
}

% Red goes over blue at all four of their crossings
\foreach \P in {rb1,rb2,rb3,rb4}{
  \TopAt{}{RedCol}{\P}
}

% Blue goes over green at all four of their crossings
\foreach \P in {gb1,gb2,gb3,gb4}{
  \TopAt{rotate=240}{BlueCol}{\P}
}

\end{tikzpicture}

\end{document}

enter image description here

Edit.
I have corrected the code. The picture now matches the one in the question exactly. However, this made the code somewhat more cumbersome, so I have kept my previous code and the previous picture.

\documentclass[tikz,border=5pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{intersections}

\begin{document}

\begin{tikzpicture}[line join=round,line cap=round]

\tikzset{
  ribbon/.style={
    line width=11pt,
    line join=round,
    line cap=round
  },
  stripe/.style={
    draw=white,
    line width=2pt,
    line join=round,
    line cap=round
  }
}

\colorlet{RedCol}{red}
\colorlet{GreenCol}{green!60!black}
\colorlet{BlueCol}{blue!75!black}

% Radius of the local redraw region at each crossing
\def\CrossRad{0.7}

% Outline of a single "wasp" figure
\def\WaspPath{
  (0.8,0)
  .. controls (0.8,1.0) and (2.0,1.8) .. (1.5,2.4)
  .. controls (1.0,3.0) and (-1.0,3.0) .. (-1.5,2.4)
  .. controls (-2.0,1.8) and (-0.8,1.0) .. (-0.8,0)
  .. controls (-0.8,-1.0) and (-2.0,-1.8) .. (-1.5,-2.4)
  .. controls (-1.0,-3.0) and (1.0,-3.0) .. (1.5,-2.4)
  .. controls (2.0,-1.8) and (0.8,-1.0) .. (0.8,0)
  -- cycle
}

% Draw the entire ribbon
% #1 = transformation
% #2 = color
\newcommand{\DrawRibbon}[2]{%
  \begin{scope}[#1]
    \draw[ribbon,draw=#2] \WaspPath;
    \draw[stripe] \WaspPath;
  \end{scope}
}

% Locally redraw the upper ribbon near a single crossing point
% #1 = transformation of the upper figure
% #2 = color of the upper figure
% #3 = name of the intersection point
\newcommand{\TopAt}[3]{%
  \begin{scope}
    \clip (#3) circle[radius=\CrossRad];
    \begin{scope}[#1]
      \draw[ribbon,draw=#2] \WaspPath;
      \draw[stripe] \WaspPath;
    \end{scope}
  \end{scope}
}

% Invisible named paths used to find intersections
\path[name path=R] \WaspPath;
\path[name path=G,rotate=120] \WaspPath;
\path[name path=B,rotate=240] \WaspPath;

% Intersections:
% rg1..rg4 = R with G
% rb1..rb4 = R with B
% gb1..gb4 = G with B
\path[name intersections={of=R and G, by={rg1,rg2,rg3,rg4}}];
\path[name intersections={of=R and B, by={rb1,rb2,rb3,rb4}}];
\path[name intersections={of=G and B, by={gb1,gb2,gb3,gb4}}];

% Initial drawing of all three ribbons
\DrawRibbon{}{RedCol}
\DrawRibbon{rotate=120}{GreenCol}
\DrawRibbon{rotate=240}{BlueCol}

% Over/under order at the 12 crossings

% rg1: red over green
\TopAt{}{RedCol}{rg1}

% the other rg crossings: green over red
\TopAt{rotate=120}{GreenCol}{rg2}
\TopAt{rotate=120}{GreenCol}{rg3}
\TopAt{rotate=120}{GreenCol}{rg4}

% rb1: red over blue
\TopAt{}{RedCol}{rb1}

% rb2: blue over red
\TopAt{rotate=240}{BlueCol}{rb2}

% rb3: blue over red
\TopAt{rotate=240}{BlueCol}{rb3}

% rb4: blue over red
\TopAt{rotate=240}{BlueCol}{rb4}

% gb1: green over blue
\TopAt{rotate=120}{GreenCol}{gb1}

% the other gb crossings: blue over green
\TopAt{rotate=240}{BlueCol}{gb2}
\TopAt{rotate=240}{BlueCol}{gb3}
\TopAt{rotate=240}{BlueCol}{gb4}


%\foreach \P in  {rg1,rg2,rg3,rg4,rb1,rb2,rb3,rb4,gb1,gb2,gb3,gb4}{\node (1) at (\P) {\P};}
\end{tikzpicture}

\end{document}

enter image description here

2
  • I'm too lazy not to use knots :-) Commented 3 hours ago
  • @cfr Your code is excellent. I was interested in doing this without knots, but if my approach had failed, I would have used it. :) Commented 2 hours ago
3

I recommend the knots library.

Steps:

  1. Combine the styles used for the red and white lines by judicious use of double and have vespa ring use an argument.
      \tikzset{
        vespa ring/.style={
          draw=#1, 
          line width=4.5pt, 
          line join=round
          double=white,
          double distance=2pt,
        },
        vespa ring/.default=red,
      }
    
  2. Remove the repeated endpoint from \vespawaist, as this causes trouble in lots of cases and convert the macro to use polar coordinates.
      \newcommand{\vespawaist}{
        (0:0.8) % Cintura direita
        .. controls (51:1.28) and (42:2.69) .. (58:2.83) % Ombro
        .. controls (72:3.16) and (108:3.16) .. (122:2.83) % Topo elevado
        .. controls (138:2.69) and (129:1.28) .. (180:0.8) % Cintura esquerda
        .. controls (-129:1.28) and (-138:2.69) .. (-122:2.83) % Ombro inferior
        .. controls (-108:3.16) and (-72:3.16) .. (-58:2.83) % Base elevada
        .. controls (-42:2.69) and (-51:1.28) ..
         cycle
      }
    
  3. Now have \vespawaist take an argument specifying the angle of rotation so we can use it for all 3 strands.
      \newcommand{\vespawaist}[1]{
        (#1:0.8) % Cintura direita
        .. controls (51+#1:1.28) and (42+#1:2.69) .. (58+#1:2.83) % Ombro
        .. controls (72+#1:3.16) and (108+#1:3.16) .. (122+#1:2.83) % Topo elevado
        .. controls (138+#1:2.69) and (129+#1:1.28) .. (180+#1:0.8) % Cintura esquerda
        .. controls (-129+#1:1.28) and (-138+#1:2.69) .. (-122+#1:2.83) % Ombro inferior
        .. controls (-108+#1:3.16) and (-72+#1:3.16) .. (-58+#1:2.83) % Base elevada
        .. controls (-42+#1:2.69) and (-51+#1:1.28) ..
         cycle
      }
    
  4. Once the path is correct, we begin rendering the knot. We render 1 strand with 0 for the red. Then we add strands for blue and green using 60 and 120.
      \begin{knot}
        \strand [
          only when rendering/.style={
            vespa ring,
          },
        ] (0,0) \vespawaist{0};
        \strand [
          only when rendering/.style={
            vespa ring=blue,
          },
        ] (0,0) \vespawaist{60};
        \strand [
          only when rendering/.style={
            vespa ring=green!50!black,
          },
        ] (0,0) \vespawaist{120};
      \end{knot}
    
  5. At this point it is best to comment out the third strand and render the first two with draft mode=crossings to get the numbers of the intersections where we want to flip the crossings. This tells us we need to flip 2 and 4.
      \begin{knot}[draft mode=crossings,
        ]
        \strand [
          only when rendering/.style={
            vespa ring,
          },
        ] (0,0) \vespawaist{0};
        \strand [
          only when rendering/.style={
            vespa ring=blue,
          },
        ] (0,0) \vespawaist{60};
        \flipcrossings{2,4}
      \end{knot}
    
  6. Now we add the third strand, get the numbers and flip the crossings.
        \strand [
          only when rendering/.style={
            vespa ring=green!50!black,
          },
        ] (0,0) \vespawaist{120};
        \flipcrossings{7,10,}
      \end{knot}
    
  7. The result isn't quite right. To tidy things up, we need to increase clip radius to account for the unusually hefty strands. We also specify background color. Finally, we comment out draft mode to remove the intersection labels.
      \begin{knot}[%draft mode=crossings,
          clip radius=20pt,
          background color=white,
        ]
        \strand [
          only when rendering/.style={
            vespa ring,
          },
        ] (0,0) \vespawaist{0};
        \strand [
          only when rendering/.style={
            vespa ring=blue,
          },
        ] (0,0) \vespawaist{60};
        \flipcrossings{2,4}
        \strand [
          only when rendering/.style={
            vespa ring=green!50!black,
          },
        ] (0,0) \vespawaist{120};
        \flipcrossings{7,10,}
      \end{knot}
    

The finished knot with labelled crossings:

with labels

and the final knot withe labels removed:

rendered strands

Complete code:

% Source - https://tex.stackexchange.com/q/762416
% Posted by Julio Michael Stern, modified by community. See post 'Timeline' for change history
% Retrieved 2026-05-01, License - CC BY-SA 4.0

\documentclass[border=5mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{knots}

\begin{document}
\begin{tikzpicture}

  % Estilos para o anel do mosaico
  \tikzset{
    vespa ring/.style={
      draw=#1, 
      line width=4.5pt, 
      line join=round
      double=white,
      double distance=2pt,
    },
    vespa ring/.default=red,
  }

  % Macro final da "Cintura de Vespa" com cabeça circular e elevada
  \newcommand{\vespawaist}[1]{
    (#1:0.8) % Cintura direita
    .. controls (51+#1:1.28) and (42+#1:2.69) .. (58+#1:2.83) % Ombro
    .. controls (72+#1:3.16) and (108+#1:3.16) .. (122+#1:2.83) % Topo elevado
    .. controls (138+#1:2.69) and (129+#1:1.28) .. (180+#1:0.8) % Cintura esquerda
    .. controls (-129+#1:1.28) and (-138+#1:2.69) .. (-122+#1:2.83) % Ombro inferior
    .. controls (-108+#1:3.16) and (-72+#1:3.16) .. (-58+#1:2.83) % Base elevada
    .. controls (-42+#1:2.69) and (-51+#1:1.28) ..
     cycle
  }

  \begin{knot}[%draft mode=crossings,
      clip radius=20pt,
      background color=white,
    ]
    \strand [
      only when rendering/.style={
        vespa ring,
      },
    ] (0,0) \vespawaist{0};
    \strand [
      only when rendering/.style={
        vespa ring=blue,
      },
    ] (0,0) \vespawaist{60};
    \flipcrossings{2,4}
    \strand [
      only when rendering/.style={
        vespa ring=green!50!black,
      },
    ] (0,0) \vespawaist{120};
    \flipcrossings{7,10,}
  \end{knot}
\end{tikzpicture}
\end{document}
1
  • The geometry is good. The solution using the knot library is elegant. However, in order to obtain the correct Brunnian or Borromean knot, the following rules must hold: (e1) Blue always goes over Green and under Red; (e2) Green always goes over Red and under Blue; (e3) Red always goes over Blue and under Green. Commented 35 mins ago

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.