12

I'm trying to use Tikz to draw climbing knots.

I adapted the code from this answer https://tex.stackexchange.com/a/283009/229885 to draw 3D-ish rope using Tikz paths (the basic idea is to superimpose many paths of different widths and colors to get a 3D look). It works great, but there is an issue that is bothering me and I don't know how to solve.

In the picture below you can see two knots. The pink one has been drawn using a single path and the white one has been drawn using two paths. I need to break the paths in order to be able to represent rope-crossings correctly, but when I do so I can see a "seam" where both paths meet.

example knots

Here is the code I used to create this picture:

\documentclass[tikz, border=2mm]{standalone}

%%% The "Rope" command %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                           %
% \Rope[further options]{color}{width}{path definition}                     %
%                                                                           %
\newcommand{\Rope}[4][]                                                     %
{   \pgfmathsetmacro{\RopeLevels}{25}                                       %
    \foreach \RopeLevel in {1,...,\RopeLevels}                              %
    {   \pgfmathsetmacro{\RopeShade}                                        %
            {100 * (\RopeLevel-0.5) / \RopeLevels}                          %
        \pgfmathsetlengthmacro{\RopeWidth}                                  %
            {sqrt(pow(#3, 2) - pow(#3 * (\RopeLevel-1) / \RopeLevels, 2))}  % 
        \draw[#2!\RopeShade!black, line width=\RopeWidth, #1] #4;           %
    }                                                                       %
}                                                                           %
%                                                                           %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
  \begin{tikzpicture}
    \Rope[rounded corners]{pink} {2mm}{ (0.0, 0.0) to (1.0, 1.0) to (0.0, 1.0) to (1.0, 0.0) }
    \Rope[rounded corners]{white}{2mm}{ (1.5, 0.0) to (2.5, 1.0) to (2.0, 1.0) }
    \Rope[rounded corners]{white}{2mm}{ (2.0, 1.0) to (1.5, 1.0) to (2.5, 0.0) }
  \end{tikzpicture}
\end{document}

I tried to define new arrow styles that extend the paths beyond the given coordinates, but the seam is still visible no matter what.

My questions are:

  • Is this seam real or an artifact created by my PDF reader? (I'm using Atril 1.24.0 on Xubuntu 20.04.1)
  • Is there any way to join Tikz paths seamlessly?
  • More in general, is there a better way to draw climbing knots in LaTeX?

Thank you in advance,

3
  • 1
    Welcome to tex.sx. Nov 29, 2020 at 16:14
  • Just to be clear, the problem does not arise as a result of the 3D-like drawing. Using a flat rope style like \newcommand{\Rope}[4][] { \draw[double=#2, thick, double distance=#3, white, #1] #4; } also creates an artificial "seam" where both paths met. I'm starting to suspect that these artifacts are, indeed, the PDF reader's fault, but I can't believe that there isn't a simple way to avoid them... Nov 30, 2020 at 15:29
  • 1
    I'm just linking to tex.stackexchange.com/q/188447/86 where the artefact issue is addressed. Nov 30, 2020 at 21:42

2 Answers 2

6

Here's a version that redraws the part of the path near the crossing to create the over-pass look. This is, in effect, what the knots package does. If you want to draw more complicated paths then that library might be worth looking into but in this case because we know where the intersection is then we don't need it.

\documentclass{article}
%\url{https://tex.stackexchange.com/q/572839/86}
\usepackage{tikz}

%%% The "Rope" command %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                           %
% \Rope[further options]{color}{width}{path definition}                     %
%                                                                           %
\newcommand{\Rope}[4][]                                                     %
{   \pgfmathsetmacro{\RopeLevels}{25}                                       %
    \foreach \RopeLevel in {1,...,\RopeLevels}                              %
    {   \pgfmathsetmacro{\RopeShade}                                        %
            {100 * (\RopeLevel-0.5) / \RopeLevels}                          %
        \pgfmathsetlengthmacro{\RopeWidth}                                  %
            {sqrt(pow(#3, 2) - pow(#3 * (\RopeLevel-1) / \RopeLevels, 2))}  % 
        \draw[#2!\RopeShade!black, line width=\RopeWidth, #1] #4;           %
    }                                                                       %
}                                                                           %
%                                                                           %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
  \begin{tikzpicture}
\Rope[rounded corners]{pink} {2mm}{ (0.0, 0.0) to (1.0, 1.0) to (0.0, 1.0) to (1.0, 0.0) }

\begin{scope}[xshift=1.5cm]
\Rope[rounded corners]{white} {2mm}{ (0.0, 0.0) to (1.0, 1.0) to (0.0, 1.0) to (1.0, 0.0) }
\clip (0.5,0.5) circle[radius=3mm];
\Rope[rounded corners]{white} {2mm}{ (0.0, 0.0) to (1.0, 1.0) }
\end{scope}
  \end{tikzpicture}
\end{document}

This produces the following image.

Rope crossing

Now, this does have artefacts at the boundary of the clip. This is only visible in the viewer and would not appear when printed out. It appears because the clipping path is not sharp and so when a line is multiply drawn, as in your rope style, then parts of the lower levels show through at the boundary.

One way to avoid this is to ensure that the under layers are clipped with a slightly smaller circle than the layers on top of them (adapting the idea at problem with "crop circles" in tikz knot library). As your paths are drawn inside a loop, we need to provide a hook to add the clipping path inside when it is needed. To do that, I've added a scope with an optional style inside your command. By passing a suitable clip to this then the artefacts can be removed.

\documentclass{article}
%\url{https://tex.stackexchange.com/q/572839/86}
\usepackage{tikz}

%%% The "Rope" command %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                           %
% \Rope[further options]{color}{width}{path definition}                     %
%                                                                           %
\newcommand{\Rope}[4][]                                                     %
{   \pgfmathsetmacro{\RopeLevels}{25}                                       %
    \foreach \RopeLevel in {1,...,\RopeLevels}                              %
    {   \pgfmathsetmacro{\RopeShade}                                        %
      {100 * (\RopeLevel-0.5) / \RopeLevels}                          %
        \pgfmathsetlengthmacro{\RopeWidth}                                  %
      {sqrt(pow(#3, 2) - pow(#3 * (\RopeLevel-1) / \RopeLevels, 2))}  %
      \begin{scope}[rope scope/.try]
      \draw[#2!\RopeShade!black, line width=\RopeWidth, #1] #4;           %
      \end{scope}
    }                                                                       %
}                                                                           %
%                                                                           %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
  \begin{tikzpicture}
\Rope[rounded corners]{pink} {2mm}{ (0.0, 0.0) to (1.0, 1.0) to (0.0, 1.0) to (1.0, 0.0) }

\begin{scope}[xshift=1.5cm]
\Rope[rounded corners]{white} {2mm}{ (0.0, 0.0) to (1.0, 1.0) to (0.0, 1.0) to (1.0, 0.0) }
\tikzset{
  rope scope/.code={
    \clip (0.5,0.5) circle[radius={3mm + \RopeLevel/25 pt}];
  }
}
\Rope[rounded corners]{white} {2mm}{ (0.0, 0.0) to (1.0, 1.0) }
\end{scope}
  \end{tikzpicture}
\end{document}

This produces:

Rope crossing without artefacts

3
  • The answer is essentially correct, in the sense that this is what the knot library does, but I'm still disappointed at the lack of "pixel-perfection" of the resulting images. I know that these "seams" are not going to appear in print, but the document I'm working on will be read on screen and I find disturbing the incapacity of my PDF reader to show both paths correctly melded (I'm starting to suspect that this may not be Tikz fault after all...). Nov 30, 2020 at 14:51
  • 1
    @CarlosLuna Fortunately, someone asked about those artefacts a while ago with regard to the knots package and so by adapting the answer to that question, it is possible to remove the artefacts even in the viewer. I've edited my answer to include that. Nov 30, 2020 at 21:39
  • Thank you very much for taking the time to make the final modification. I'm going to mark your contribution as the answer to my original question. Dec 2, 2020 at 18:59
8

You can shorten a path by a negative length, and thus lengthen it to create a small overlap. This trick cures things on many viewers but maybe not on all. At least not on all magnification levels.

\documentclass[tikz, border=2mm]{standalone}

%%% The "Rope" command %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                           %
% \Rope[further options]{color}{width}{path definition}                     %
%                                                                           %
\newcommand{\Rope}[4][]                                                     %
{   \pgfmathsetmacro{\RopeLevels}{25}                                       %
    \foreach \RopeLevel in {1,...,\RopeLevels}                              %
    {   \pgfmathsetmacro{\RopeShade}                                        %
            {100 * (\RopeLevel-0.5) / \RopeLevels}                          %
        \pgfmathsetlengthmacro{\RopeWidth}                                  %
            {sqrt(pow(#3, 2) - pow(#3 * (\RopeLevel-1) / \RopeLevels, 2))}  % 
        \draw[#2!\RopeShade!black, line width=\RopeWidth, #1] #4;           %
    }                                                                       %
}                                                                           %
%                                                                           %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
  \begin{tikzpicture}
    \Rope[rounded corners]{pink} {2mm}{ (0.0, 0.0) to (1.0, 1.0) to (0.0, 1.0) to (1.0, 0.0) }
    \Rope[rounded corners]{white}{2mm}{ (1.5, 0.0) to (2.5, 1.0) to (2.0, 1.0) }
    \Rope[rounded corners,shorten <=-0.2pt]{white}{2mm}{ (2.0, 1.0) to (1.5, 1.0) to (2.5, 0.0) }
  \end{tikzpicture}
\end{document}

enter image description here

This is maximal zoom under Preview on a Mac. Acrobat Reader is also seamless for most magnifications, but not for all.

It is, in principle, possible to get a "perfect" result by clipping out some of the old path. Here is an incredibly tuned version using a dash pattern.

\documentclass[tikz, border=2mm]{standalone}
%%% The "Rope" command %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                           %
% \Rope[further options]{color}{width}{path definition}                     %
%                                                                           %
\newcommand{\Rope}[4][]{%                                                   %
   \pgfmathsetmacro{\RopeLevels}{25}                                        %
    \foreach \RopeLevel in {1,...,\RopeLevels}                              %
    {   \pgfmathtruncatemacro{\RopeShade}                                   %
            {100 * (\RopeLevel-0.5) / \RopeLevels}                          %
        \pgfmathsetlengthmacro{\RopeWidth}                                  %
            {sqrt(pow(#3, 2) - pow(#3 * (\RopeLevel-1) / \RopeLevels, 2))}  % 
        \draw[#2!\RopeShade!black, line width=\RopeWidth, #1] #4;           %
    }                                                                       %
}                                                                           %
%                                                                           %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\begin{document}
  \begin{tikzpicture}
    \Rope[rounded corners]{pink} {2mm}{ (0.0, 0.0) to (1.0, 1.0) to (0.0, 1.0) to (1.0, 0.0) }
    \Rope[rounded corners]{white}{2mm}{ (1.5, 0.0) to (2.5, 1.0) to (1.5, 1.0) to (2.5, 0.0) }
    \Rope[rounded corners,dash pattern=on 79.02pt off 80pt]{white}{2mm}{ (1.5, 0.0) to (2.5, 1.0) to (1.5, 1.0) to (2.5, 0.0) }
  \end{tikzpicture}
\end{document}

This one does not seem to have any glitches for arbitrary magnifications or viewers (I only tested Preview and Acrobat Reader). Could one compute the 79.02pt? Yes, at least in this case. The cleaner way to go, though, is to use this post.

3
  • I tried something similar with an arrow definition: \pgfarrowsdeclare{meld}{meld}{\pgfarrowsrightextend{-0.1pt}}{}. But both, your method and mine, still show a "seam" in many magnification levels. In my question I forgot to mention that the "seam" sometimes disappears depending on the zoom level, but my objective is to remove it for all zoom levels rather than minimize its appearances. Nov 29, 2020 at 16:26
  • 1
    @CarlosLuna Yes, the above is only a cheap trick. Are you aware of the knots library? Another thing one can do is to draw the full thin, to protect the intersection are by an inverted clip, and draw again. This may have less artifacts but even there is no guarantee because some times clip produces its own artifacts...
    – user229669
    Nov 29, 2020 at 16:45
  • Yes, the knots library was my first attempt, but I didn't like the "clip circles" that appear in the knots and I was hopping there is a better way to do the job... Nov 30, 2020 at 14:54

You must log in to answer this question.

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