5

I have something similar to the following code (apologies for the long example but this is fairly sensitive to precise numbers):

\documentclass{article}
\usepackage{tikz}

\usetikzlibrary{
  knots,
  hobby,
}

\tikzset{
    knot diagram/every knot diagram/.append style={
        consider self intersections=true,
        ignore endpoint intersections=false,
        end tolerance=1pt,
        clip draw radius=12pt
    },
  knot diagram/only when rendering/.style={
        ultra thick,
        draw=blue!40!black,
        double=white,
        double distance=12pt
    }
}

\begin{document}
\begin{tikzpicture}
    \begin{knot}[
        draft mode=crossings,
        flip crossing/.list={2,4,6,7,10,12}
        ]
    \strand
        ([closed]0.5,0.5) to[out=-5,in=-135]
        (1.8,0.8) to[out=45,in=-135]
        (4.25,3.25) to[out=45,in=135]
        (5.75,3.25) to[out=-45,in=135]
        (8.2,0.8) to[out=-45,in=-175]
        (9.5,0.5) to[out=85,in=-45]
        %
        (9.2,1.8) to[out=135,in=-45]
        (7.75,3.25) to[out=135,in=45]
        (6.25,3.25) to[out=-135,in=45]
        (3.75,0.75) to[out=-135,in=-45]
        (2.25,0.75) to[out=135,in=-45]
        (0.8,2.2) to[out=135,in=-95]
        (0.5,3.5) to[out=5,in=135]
        %
        (1.8,3.2) to[out=-45,in=135]
        (4.25,0.75) to[out=-45,in=-135]
        (5.75,0.75) to[out=45,in=-135]
        (8.2,3.2) to[out=45,in=175]
        (9.5,3.5) to[out=-85,in=45]
        %
        (9.2,2.2) to[out=-135,in=45]
        (7.75,0.75) to[out=-135,in=-45]
        (6.25,0.75) to[out=135,in=-45]
        (3.75,3.25) to[out=135,in=45]
        (2.25,3.25) to[out=-135,in=45]
        (0.8,1.8) to[out=-135,in=95] cycle;
    \end{knot}
    \useasboundingbox (-0.5,-0.5) rectangle (10.5,4.5);
\end{tikzpicture}

\begin{tikzpicture}
    \begin{knot}[
        draft mode=crossings,
        flip crossing/.list={2,4,6,7,10,12}
        ]
    \strand
        ([closed]0.5,0.5) to[out=-5,in=-135]
        (1.9,0.9) to[out=45,in=-135]
        (4.25,3.25) to[out=45,in=135]
        (5.75,3.25) to[out=-45,in=135]
        (8.1,0.9) to[out=-45,in=-175]
        (9.5,0.5) to[out=85,in=-45]
        %
        (9.1,1.9) to[out=135,in=-45]
        (7.75,3.25) to[out=135,in=45]
        (6.25,3.25) to[out=-135,in=45]
        (3.75,0.75) to[out=-135,in=-45]
        (2.25,0.75) to[out=135,in=-45]
        (0.9,2.1) to[out=135,in=-95]
        (0.5,3.5) to[out=5,in=135]
        %
        (1.9,3.1) to[out=-45,in=135]
        (4.25,0.75) to[out=-45,in=-135]
        (5.75,0.75) to[out=45,in=-135]
        (8.1,3.1) to[out=45,in=175]
        (9.5,3.5) to[out=-85,in=45]
        %
        (9.1,2.1) to[out=-135,in=45]
        (7.75,0.75) to[out=-135,in=-45]
        (6.25,0.75) to[out=135,in=-45]
        (3.75,3.25) to[out=135,in=45]
        (2.25,3.25) to[out=-135,in=45]
        (0.9,1.9) to[out=-135,in=95] cycle;
    \end{knot}
    \useasboundingbox (-0.5,-0.5) rectangle (10.5,4.5);
\end{tikzpicture}
\end{document}

Each knot shows the issue in different ways, perhaps with different causes, and an incidental issue (with the first/last point). Here's the output:

Broken knots with tikz knots package

Please note that I can't use the celtic package because I don't want alternating knots - as far as I know there is no way to do that with that package. (These knots look non-alternating because the crossings have gone wonky due to the first/last point issue)

The main issue is the partial crossings. In the first knot, I have no idea why this is happening; in the second knot there are control points very close to another arc and I thought this was causing the problem by restricting the clipping circle, since moving those control points away made this problem disappear (chronologically the first knot was created in an attempt to solve this issue). But the first knot has no such issue; it's just appeared.

This just seems to be an issue with drawing. It only happens with double enabled which isn't actually documented in knots documentation, so I have few avenues with which to proceed.

One interesting thing to note is the orientation of the flat sides of the aliasing artifacts from the clip circles - in particular the flipping of it for crossing 10 in the first image (9 in the second... I'm not sure why those crossing numbers change)

The issue with the first and last point creating a crossing doesn't happen in my actual document so I am not as worried about that.

1
  • Try to use 50pt for clip draw radious with that value I get all the cuts without the problems that I see in your captures. but it only works for the first one...
    – J Leon V.
    Commented Jan 26, 2021 at 17:06

3 Answers 3

3

There are a couple of things going on here, one of which is related to how best to draw pictures like this (which I should probably add to the documentation), and one of which reveals a small bug in the knots package.

When drawing a celtic-style knot with the knots package then there are a few issues with how double paths turn out. Over time, via a few questions here, then a best way of doing these knots has emerged which isn't the obvious way.

More importantly, though, is that because your control points are very close to intersections and your paths are very wide then you are falling foul of a bug in how end tolerance is used. This key is used to tell the knots library what "near an endpoint" means. This is used twice, once to throw out spurious intersections where one segment "intersects" the next segment. For this test, you want to set end tolerance to be quite small since your control points are close to the (genuine) intersection points (the option ignore endpoint intersections=false is what is causing the spurious intersection in the lower left corner; since none of your control points is actually at an intersection, by removing this option and keeping end tolerance small you can get rid of this intersection without affecting the rest of the picture).

But end tolerance is also used in another place. When the algorithm detects an intersection then it draws the upper strand again in the vicinity of the intersection. Normally, it just draws the relevant segment of the path. But if that segment ends near to the intersection point then it also draws the next (or previous, as applicable) segment. And the algorithm uses end tolerance again here. So with end tolerance set to be very small, this means that a segment that should be drawn is not because the intersection point is found to be further from the endpoint of the segment. What I should use here is the clipping radius.

This is, I think, what is going on with your half-crossing. There's a further step in the routine when checking self-intersections which (possibly) splits bézier curves in two to ensure that they don't themselves self-intersect. This is (probably) happening to that diagonal path and then, due to end tolerance being small, only part of that path is being rendered.

Here's your code:

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

\usetikzlibrary{
  knots,
  hobby,
}

\tikzset{
  knot diagram/every knot diagram/.append style={
    consider self intersections=true,
    %        ignore endpoint intersections=false,
    end tolerance=1pt,
    clip width=1,
    background color=blue!40!black,
    only when rendering/.style={
      basic strand
    },
    every intersection/.style={
      crossing strand
    },
  },
  basic strand/.style={
    ultra thick,
    draw=blue!40!black,
    double=white,
    double distance=10pt
  },
  crossing strand/.style={
    line width=13.2pt,
    only when rendering/.style={%
      draw=white,%
      line width=10pt,
      double=none,
    }
  }
}

\begin{document}
\begin{tikzpicture}
    \begin{knot}[
%        draft mode=crossings,
        flip crossing/.list={4,6,7,10,12}
        ]
    \strand
        (0.5,0.5) to[out=-5,in=-135]
        (1.8,0.8) to[out=45,in=-135]
        (4.25,3.25) to[out=45,in=135]
        (5.75,3.25) to[out=-45,in=135]
        (8.2,0.8) to[out=-45,in=-175]
        (9.5,0.5) to[out=85,in=-45]
        %
        (9.2,1.8) to[out=135,in=-45]
        (7.75,3.25) to[out=135,in=45]
        (6.25,3.25) to[out=-135,in=45]
        (3.75,0.75) to[out=-135,in=-45]
        (2.25,0.75) to[out=135,in=-45]
        (0.8,2.2) to[out=135,in=-95]
        (0.5,3.5) to[out=5,in=135]
        %
        (1.8,3.2) to[out=-45,in=135]
        (4.25,0.75) to[out=-45,in=-135]
        (5.75,0.75) to[out=45,in=-135]
        (8.2,3.2) to[out=45,in=175]
        (9.5,3.5) to[out=-85,in=45]
        %
        (9.2,2.2) to[out=-135,in=45]
        (7.75,0.75) to[out=-135,in=-45]
        (6.25,0.75) to[out=135,in=-45]
        (3.75,3.25) to[out=135,in=45]
        (2.25,3.25) to[out=-135,in=45]
        (0.8,1.8) to[out=-135,in=95] cycle;
    \end{knot}
    \useasboundingbox (-0.5,-0.5) rectangle (10.5,4.5);
\end{tikzpicture}

\begin{tikzpicture}
    \begin{knot}[
%        draft mode=crossings,
        flip crossing/.list={2,4,6,7,10,12}
        ]
    \strand
        ([closed]0.5,0.5) to[out=-5,in=-135]
        (1.9,0.9) to[out=45,in=-135]
        (4.25,3.25) to[out=45,in=135]
        (5.75,3.25) to[out=-45,in=135]
        (8.1,0.9) to[out=-45,in=-175]
        (9.5,0.5) to[out=85,in=-45]
        %
        (9.1,1.9) to[out=135,in=-45]
        (7.75,3.25) to[out=135,in=45]
        (6.25,3.25) to[out=-135,in=45]
        (3.75,0.75) to[out=-135,in=-45]
        (2.25,0.75) to[out=135,in=-45]
        (0.9,2.1) to[out=135,in=-95]
        (0.5,3.5) to[out=5,in=135]
        %
        (1.9,3.1) to[out=-45,in=135]
        (4.25,0.75) to[out=-45,in=-135]
        (5.75,0.75) to[out=45,in=-135]
        (8.1,3.1) to[out=45,in=175]
        (9.5,3.5) to[out=-85,in=45]
        %
        (9.1,2.1) to[out=-135,in=45]
        (7.75,0.75) to[out=-135,in=-45]
        (6.25,0.75) to[out=135,in=-45]
        (3.75,3.25) to[out=135,in=45]
        (2.25,3.25) to[out=-135,in=45]
        (0.9,1.9) to[out=-135,in=95] cycle;
    \end{knot}
    \useasboundingbox (-0.5,-0.5) rectangle (10.5,4.5);
\end{tikzpicture}
\end{document}

Celtic-style knots

I've just uploaded the fixed version to github.

(But I can still see slight artefacts on the upper one of these, which suggests that I might not have the fix quite right yet -- for anyone mathematically inclined, it might be due to me using the l^1-norm for speed instead of the l^2-norm so I might need a factor of sqrt(2) in there somewhere.)

4
  • I've just added a factor of sqrt(2) and the artefacts disappear. Commented Jan 26, 2021 at 20:28
  • Thanks for the swift response and fix! What is the \path with circles for? At the moment I am guessing it was just for showing where the control points were as opposed to the crossings..? Commented Jan 26, 2021 at 21:35
  • 1
    @ChrisLeSueur You're absolutely right - it was for figuring out where all the segments ended so I could see how close they were to the intersection points. It was originally a \fill which I changed to \path but as that removed them from the picture I forgot that they were there! I meant to remove them before posting. Commented Jan 26, 2021 at 21:38
  • I've removed them now. Commented Jan 26, 2021 at 21:40
4

I offer you a different way to do it, directly with tikz. My drawing is Celtic, because I don't really know how you do want to overlap the lines. There is an additional example for you to see how to overlap the lines arbitrarily.

This is my code:

\begin{document}
\begin{tikzpicture}[line join=round, line cap=round, thick, blue, fill=blue!10]
  % Coordinates
  \pgfmathsetmacro\r{sqrt(2)}  
  \foreach\x in {1,...,5} \foreach\y in {1,2}
  {
    \coordinate (\x-\y-O) at (1.5*\r*\x,1.5*\r*\y);
    \coordinate (\x-\y-N) at ($(\x-\y-O)+(0,0.5*\r)$);
    \coordinate (\x-\y-S) at ($(\x-\y-O)-(0,0.5*\r)$);
    \coordinate (\x-\y-E) at ($(\x-\y-O)+(0.5*\r,0)$);
    \coordinate (\x-\y-W) at ($(\x-\y-O)-(0.5*\r,0)$);
  }
  % Middle section
  \foreach\x in {2,3,4}
  {
    \pgfmathtruncatemacro\xx{\x-1}
    \filldraw (\x-1-N) ++ (225:1) arc (225:315:1) --++ (45:1.5) --++
              (315:0.5) --++ (225:1.5) --++ (0,0) arc (315:225:1.5) --cycle;
    \filldraw (\x-2-W) --++ (315:2.5) --++ (225:0.5) --++ (135:2.5) -- cycle;
    \filldraw (\xx-2-S) --++ (45:1.5) --++ (0,0) arc (135:45:1.5) --++
              (225:0.5) --++ (0,0) arc (45:135:1) --++ (225:1.5) -- cycle;
  }
  % Corners
  \foreach \x/\y/\c/\a in {1/1/N/0, 1/2/E/270, 5/1/W/90, 5/2/S/180}
  {
    \begin{scope}[shift={(\x-\y-\c)},rotate=\a]
      \filldraw (0,0) --++ (135:0.5) --++ (0,0) arc (135:180:1.5) --++ (270:1.5-0.5*\r) --++
                (0:1.5-0.5*\r) --++ (0,0) arc (270:315:1.5) --++ (45:1.5) --++ (135:0.5) --++
                (225:1.5) --++ (0,0) arc (315:270:1) --++ (180:1-0.5*\r) --++ (90:1-0.5*\r) --++
                (0,0) arc (180:135:1) -- cycle;
    \end{scope}
  }
  % Non-celtic example (superposed, comment it if you want)
  \filldraw[draw=red,fill=red!10] (2-1-N) --++ (225:1.5) --++ (0,0) arc (315:270:1.5) --++
                (180:1.5-0.5*\r) --++ (90:1.5-0.5*\r) --++ (0,0) arc (180:135:1.5) --++ (45:2) --++
                (0,0) arc (135:45:1.5) --++ (315:3) --++ (225:0.5) --++ (135:3) --++
                (0,0) arc (45:135:1) --++ (225:2) --++ (0,0) arc (135:180:1) --++
                (270:1-0.5*\r) --++ (0:1-0.5*\r) --++ (0,0) arc (270:315:1) --++ (45:1.5) -- cycle;
% Auxiliar grid of squares
%  \foreach\x in {1,...,5} \foreach\y in {1,2}
%  {
%    \draw[thin,black,dashed] (\x-\y-E) -- (\x-\y-N) -- (\x-\y-W) -- (\x-\y-S) -- cycle;
%  }
\end{tikzpicture}
\end{document}

And the drawing: enter image description here

1
  • Unfortunately I need to produce the same knot but with many different crossings, so I'm hoping to avoid having to manually create and order the segments. Nevertheless if there is no quicker way, thanks for demonstrating a good way to draw this by hand. Commented Jan 26, 2021 at 10:33
2

I am aware that the best solution to the problem is the one proposed by Andy, who uses the knots library, just as the OP wanted.

But I was not entirely happy with my previous solution, which was better suited to the Celtic case. So, for fun, I just drew another solution to the problem, more parameterized, with a change of axes, and better adapted to the non-Celtic case.

The new code:

\documentclass[border=2mm]{standalone}
\usepackage    {tikz}
\usetikzlibrary{calc}

\begin{document}
\begin{tikzpicture}[line join=round, line cap=round, thick, blue, rotate=-45]
  % Parameters
  \def\na{4}   % number of middle arcs
  \def\sp{1}   % separation between paths
  \def\wp{0.5} % width of the path
  % Distances
  \pgfmathsetmacro\sm {\sp+\wp}
  \pgfmathsetmacro\spd{sqrt(2)*\sp}
  \pgfmathsetmacro\wpd{sqrt(2)*\wp}
  \pgfmathsetmacro\smd{sqrt(2)*\sm}
  \pgfmathsetmacro\lc {0.5*(\spd-\sp)}
  % Central pattern (without crossings)
  \foreach\i in {1,...,\na}
  {
    \pgfmathsetmacro\x{\i*\sm}
    \coordinate (\i-O) at (\x,\x);                        % Square, center
    \coordinate (\i-N) at ($(\i-O)+(-0.5*\sm, 0.5*\sm)$); % Square, north 
    \coordinate (\i-S) at ($(\i-O)+( 0.5*\sm,-0.5*\sm)$); % ...
    \coordinate (\i-E) at ($(\i-O)+( 0.5*\sm, 0.5*\sm)$);
    \coordinate (\i-W) at ($(\i-O)+(-0.5*\sm,-0.5*\sm)$);
%    \node[red] at (\i-O) {\small\i-O};
%    \node[red] at (\i-N) {\small\i-N};
%    \node[red] at (\i-S) {\small\i-S};
%    \node[red] at (\i-E) {\small\i-E};
%    \node[red] at (\i-W) {\small\i-W};
    \draw ($(\i-O)-( 0.5*\sp, 0.5*\sp)$) rectangle ($(\i-O)+(0.5*\sp,0.5*\sp)$);
    \draw ($(\i-N)+(-0.5*\wp, 0.5*\wp)$) arc  (90:135:\sp+\wp);
    \draw ($(\i-N)+(-0.5*\wp, 0.5*\wp)$) arc (180:135:\sp+\wp);
    \draw ($(\i-S)+( 0.5*\wp,-0.5*\wp)$) arc (270:315:\sp+\wp);
    \draw ($(\i-S)+( 0.5*\wp,-0.5*\wp)$) arc   (0:-45:\sp+\wp);
    \draw ($(\i-E)+(-0.5*\wp, 0.5*\wp)$) --++ (-\sp,0) --++ (0,0) arc (180:135:\sp);
    \draw ($(\i-E)+( 0.5*\wp,-0.5*\wp)$) --++ (0,-\sp) --++ (0,0) arc (270:315:\sp);
    \draw ($(\i-W)+(-0.5*\wp, 0.5*\wp)$) --++  (0,\sp) --++ (0,0) arc  (90:135:\sp);
    \draw ($(\i-W)+( 0.5*\wp,-0.5*\wp)$) --++  (\sp,0) --++ (0,0) arc   (0:-45:\sp);
  }
  % Left corners
  \draw ($(1-W)+( 0.5*\wp,-0.5*\wp)$) arc (180:225:\sp) --++ (\lc,-\lc) --++ (\lc,\lc);
  \draw ($(1-W)+(-0.5*\wp, 0.5*\wp)$) arc (270:225:\sp) --++ (-\lc,\lc) --++ (\lc,\lc);
  \draw ($(1-W)+(-0.5*\wp,-0.5*\wp)$) arc (180:225:\sm) --++ ( 0.5*\wpd+\lc,-0.5*\wpd-\lc)
                                      --++ (0.5*\wpd+\lc,0.5*\wpd+\lc);
  \draw ($(1-W)+(-0.5*\wp,-0.5*\wp)$) arc (270:225:\sm) --++ (-0.5*\wpd-\lc, 0.5*\wpd+\lc)
                                      --++ (0.5*\wpd+\lc,0.5*\wpd+\lc);
  % Right corners
  \draw ($(\na-E)+(-0.5*\wp, 0.5*\wp)$) arc  (0:45:\sp) --++ (-\lc,\lc) --++ (-\lc,-\lc);
  \draw ($(\na-E)+( 0.5*\wp,-0.5*\wp)$) arc (90:45:\sp) --++ (\lc,-\lc) --++ (-\lc,-\lc);
  \draw ($(\na-E)+( 0.5*\wp, 0.5*\wp)$) arc  (0:45:\sm) --++ (-0.5*\wpd-\lc, 0.5*\wpd+\lc)
                                        --++ (-0.5*\wpd-\lc,-0.5*\wpd-\lc);
  \draw ($(\na-E)+( 0.5*\wp, 0.5*\wp)$) arc (90:45:\sm) --++ ( 0.5*\wpd+\lc,-0.5*\wpd-\lc)
                                        --++ (-0.5*\wpd-\lc,-0.5*\wpd-\lc);
  % NE Crossings
  \foreach \i/\C in {1/W, 1/S, 1/N, 2/S, 3/S, 3/N, 4/N, 4/S}
  {
    \draw ($(\i-\C)+(-0.5*\wp,-0.5*\wp)$) --++ (0,\wp);
    \draw ($(\i-\C)+( 0.5*\wp,-0.5*\wp)$) --++ (0,\wp);
  }
  % NW Crossings
  \foreach \i/\C in {2/W, 2/N, 3/W, 4/W, 4/E}
  {
    \draw ($(\i-\C)+(-0.5*\wp, 0.5*\wp)$) --++ (\wp,0);
    \draw ($(\i-\C)+(-0.5*\wp,-0.5*\wp)$) --++ (\wp,0);
  }
\end{tikzpicture}
\end{document}

And the (not so new) drawing: enter image description here

You must log in to answer this question.

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