31

This question led to a new package:
spath3

(Technically, this question led to a subpackage of the spath3 package; the spath3 package provides some foundations on which a TikZ library knots was built and the TikZ library is bundled with the spath3 library.)

See also the blog post: How can I draw a knot in TeX?

I need to use TikZ to draw some knot/braid diagrams. These will be built from families of curves, which appear to cross over each other in places. At each crossing, it needs to be obvious which line is going over which, and so I'm using the TikZ double functionality, to draw a fat white line underneath the thin black lines representing the curves. That way, if I draw 'on top' line segments after 'on the bottom' line segments, I'll get the correct effect.

But still, drawing these diagrams is a pain. In particular, I might have two continuous curves in one diagram, with the first curve going on top of the second in some places, and beneath it in others. So I can't simply draw the 'lower' curve first: I have to draw different segments of the curves individually.

For ease of programming, I'd really like to have all the code for each curve together. One way I could achieve this is if I could change the layer independently for each segment of a multi-point path command. The idea would be something like this:

\draw (1) to [layer=middlelayer] (2) to [layer=toplayer] (3) to [layer=bottomlayer] (4);

Is this possible? Or is there a better way to achieve the effect I want?

Unfortunately, my diagrams need to be much more freeform than those produced by Andrew Stacey's braid package. They're not of the form of generators of the braid group stacked neatly on top of one another. Here's an example of the sort of thing I need:

enter image description here

Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751
Jamie Vicary
  • 11,098
  • 6
    We've got a resident braids expert, Andrew Stacey, who wrote a nice package for drawing braids diagrams in TikZ. He's written a post on the TeX.sx blog: http://tex.blogoverflow.com/2011/09/the-braids-package/ – Jake Oct 20 '11 at 09:19
  • 4
    This is a job for Andrew Stacey. :) – Paulo Cereda Oct 20 '11 at 09:28
  • 4
    We had a question a while ago on drawing braids (which is http://tex.stackexchange.com/q/16897/86) and which led to a package which is now on CTAN (http://www.ctan.org/pkg/braids). It (probably) needs PGF2.10. There's a slightly enhanced version at the TeX-SX launchpad site (http://bazaar.launchpad.net/~tex-sx/tex-sx/development/files), but I'll push the changes to CTAN when I've tested them a bit more. The blog post that Jake links has some details, the CTAN/launchpad have full documentation. If there's anything that it doesn't do that you'd like, please ask and I'll try to implement it. – Andrew Stacey Oct 20 '11 at 09:36
  • 1
    Thanks everyone for your comments. I've edited the post to clarify that Andrew Stacey's package, although very cool, doesn't do the job I need! – Jamie Vicary Oct 21 '11 at 08:27
  • 4
    That's not a braid, that's a tangle! For more general knot-like diagrams, we also have http://tex.stackexchange.com/q/17181/86. – Andrew Stacey Oct 21 '11 at 09:10
  • It being that much harder to specify a knot or a tangle than a braid, that code hasn't progressed as far as the braid one. I use it, but I'm not yet convinced that it's the best way. Your ideas look interesting, but my initial experiments don't work and I'm not sure why! – Andrew Stacey Oct 21 '11 at 09:36
  • If would be really cool if it was possible to draw the curves, let TikZ calculate all the intersection points automatically, and then tell it to redraw the intersections according to an under/over specification... do you think this is in the realm of plausibility? – Jamie Vicary Oct 21 '11 at 10:22
  • Not to sit on the same table with @AndrewStacey since he is way beyond me but while he is coming up with the full solution I want to draw your attention to the double line option (page 160 bottom example in the manual v.2.10) for the over/under effect. By this I mean, if you can divide your image into sections, you can connect different Braid sections while handling the knot-like parts manually. Or when you compute the intersections, you can define an even odd rule type argument. It might work in a pinch. Sorry again for the vagueness. – percusse Oct 21 '11 at 12:37
  • 1
    Jamie: What you describe is certainly possible, but I'm still unsure as to how workable it would be for the user. The question as to how to input and to render a knot/tangle diagram is unclear to me. Would you be willing to chat a bit about it first to see if it's possible to work out a design? I've asked the moderators to "unfreeze" the "From Answers to Packages" chat room for this as it would be a better location than these comments. – Andrew Stacey Oct 21 '11 at 13:10
  • (Link to chat room: http://chat.stackexchange.com/rooms/409/from-answers-to-packages assuming it gets unfrozed.) – Andrew Stacey Oct 21 '11 at 13:11
  • It's now unfrozen. Anyone who wants to help figure out the best way to do knot diagrams in TikZ/PGF is welcome to join me there. (@percusse that includes you) – Andrew Stacey Oct 21 '11 at 17:09

1 Answers1

22

Update: This is now available on CTAN (see above). Bugfixes and improvements will generally be available on github before being uploaded to CTAN. Note that that repository is quite cluttered, the relevant .dtx file is called spath3.dtx. This generates all the necessary files.


Update: The code has undergone considerable revision following extensive discussion with Jamie in the chat room From Answers to Packages. The code can now be downloaded from the TeX-SX Launchpad Project. The necessary files are knots.dtx and spath.dtx. The file knots_test.tex contains some example code. To create the .sty files from the .dtx, run tex <file>.dtx. Alternatively, running latex (or pdflatex) on knots_test.tex with shell escapes enabled will automatically generate the .sty files.


Okay, here's my first attempt at implementing what we discussed in chat. It is certainly a bit rough around the edges, and I give absolutely no guarantees that it won't TeX your cat instead.

Code:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{intersections}

\makeatletter
\newcounter{knot@strings}
\newif\ifknot@draftmode
\tikzoption{save knot path}{\tikz@addmode{\pgfsyssoftpath@getcurrentpath\knot@tmppath\expandafter\global\expandafter\let#1=\knot@tmppath}}
\tikzoption{use knot path}{\tikz@addmode{\expandafter\pgfsyssoftpath@setcurrentpath#1}}
\tikzset{
  knot/draft mode/.is if=knot@draftmode
}
\newenvironment{knot}[1][]{%
  \tikzset{#1}%
  \setcounter{knot@strings}{0}}{%
  \foreach \knot@str in {1,...,\the\value{knot@strings}} {
    \expandafter\expandafter\expandafter\draw\expandafter\expandafter\expandafter[\csname knot@string@opts@\knot@str\endcsname,use knot path=\csname knot@string@\knot@str\endcsname] (0,0);
    \ifknot@draftmode
    \begingroup
    \let\pgfsyssoftpath@movetotoken=\pgfqpoint
    \let\pgfsyssoftpath@linetotoken=\pgfqpoint
    \let\pgfsyssoftpath@curvetotoken=\pgfqpoint
    \let\pgfsyssoftpath@curvetosupportatoken=\pgfqpoint
    \let\pgfsyssoftpath@curvetosupportbtoken=\pgfqpoint
    \csname knot@string@\knot@str\endcsname
    \global\pgf@xa=\pgf@x
    \global\pgf@ya=\pgf@y
    \endgroup
    \node[circle,fill=white,fill opacity=.5] at (\pgf@xa,\pgf@ya) {\knot@str};
\fi
  }
  \pgfmathtruncatemacro{\knot@stam}{\the\value{knot@strings}-1}
  \foreach \knot@sta in {1,...,\knot@stam} {
    \pgfmathtruncatemacro{\knot@stap}{\knot@sta + 1}
    \foreach \knot@stb in {\knot@stap,...,\the\value{knot@strings}} {
      \pgfintersectionofpaths{\expandafter\pgfsetpath\csname knot@string@\knot@sta\endcsname}{\expandafter\pgfsetpath\csname knot@string@\knot@stb\endcsname}
    \foreach \intsect in {1,...,\pgfintersectionsolutions} {
      \pgfpointintersectionsolution{\intsect}
      \pgfgetlastxy{\intsectx}{\intsecty}
        \ifknot@draftmode
        \node[circle,fill=white,fill opacity=.5] at (\intsectx,\intsecty)         {\knot@sta-\knot@stb-\intsect};
\else
\@ifundefined{knot@crossing@\knot@sta-\knot@stb-\intsect}{
%\message{\knot@sta-\knot@stb-\intsect not defined}
}{
\pgfmathtruncatemacro{\knot@under}{\csname knot@crossing@\knot@sta-\knot@stb-\intsect\endcsname == \knot@sta ? \knot@stb : \knot@sta}
\expandafter\let\expandafter\knot@over\csname knot@crossing@\knot@sta-\knot@stb-\intsect\endcsname
\pgfscope
\clip (\intsectx,\intsecty) circle[radius=10pt];
    \expandafter\expandafter\expandafter\draw\expandafter\expandafter\expandafter[\csname knot@string@opts@\knot@under\endcsname,use knot path=\csname knot@string@\knot@under\endcsname] (0,0);
    \expandafter\expandafter\expandafter\draw\expandafter\expandafter\expandafter[\csname knot@string@opts@\knot@over\endcsname,use knot path=\csname knot@string@\knot@over\endcsname,white,line width=3\pgflinewidth] (0,0);
    \expandafter\expandafter\expandafter\draw\expandafter\expandafter\expandafter[\csname knot@string@opts@\knot@over\endcsname,use knot path=\csname knot@string@\knot@over\endcsname] (0,0);
\endpgfscope
}
\fi
      }
    }
  }
}
\newcommand{\strand}[1][]{%
  \stepcounter{knot@strings}%
  \expandafter\def\csname knot@string@opts@\the\value{knot@strings}\endcsname{#1}%
\path[save knot path=\csname knot@string@\the\value{knot@strings}\endcsname]}
\newcommand{\crossing}[2]{%
\expandafter\def\csname knot@crossing@#1\endcsname{#2}}

\makeatother

\begin{document}
\begin{tikzpicture}
\begin{knot}[knot/draft mode]
\strand[red,ultra thick]  (0,0) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1);
\strand[blue,ultra thick] (0,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1);
\crossing{1-2-1}{1}
\crossing{1-2-2}{2}
\crossing{1-2-3}{1}
\crossing{1-2-4}{2}
\end{knot}
\end{tikzpicture}

\begin{tikzpicture}
\begin{knot}
\strand[red,ultra thick]  (0,0) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1);
\strand[blue,ultra thick] (0,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1);
\crossing{1-2-1}{1}
\crossing{1-2-2}{2}
\crossing{1-2-3}{1}
\crossing{1-2-4}{2}
\end{knot}
\end{tikzpicture}
\end{document}

Result (draft version on top):

knots by intersection

Hopefully the syntax is obvious enough from the example. (It looks a lot nicer in the original PDF; the conversion to PNG is awful!). There are lots of places for improvement, but I thought I'd see if this was on the right track before polishing it.

Andrew Stacey
  • 153,724
  • 43
  • 389
  • 751
  • Andrew, comments posted in the forum. – Jamie Vicary Oct 22 '11 at 14:05
  • Thanks! It doesn't work with scale other than 1, so I wasn't seeing the crossings. With no scale argument, everything is beautiful. – Jack Schmidt Nov 08 '11 at 19:44
  • @JackSchmidt: Where were you putting the scale? Can you post an example in the chatroom http://chat.stackexchange.com/rooms/409/from-answers-to-packages (if you cut-and-paste code then you can post large blocks). It ought to work with transformations (in the sense that if it doesn't then I should fix it so that it does). The chatroom is a better location for discussing features and bugs. – Andrew Stacey Nov 08 '11 at 19:51
  • Hello, I just discovered this wonderful package. Please note that when you copy and paste the manual code, a space is always placed between two consecutive characters. Thus the command \strand becomes `\ s t r a n d. Could you modify the documentation to make it easier to copy and paste? – AndréC Aug 04 '19 at 11:54
  • @AndréC That came up with another of my packages. If you're on github, could you raise an issue there so I remember to fix it? It's at https://github.com/loopspace/spath3 – Andrew Stacey Aug 04 '19 at 18:07
  • @LoopSpace done: https://github.com/loopspace/spath3/issues/5 – AndréC Aug 04 '19 at 18:39
  • @AndréC Thanks. – Andrew Stacey Aug 04 '19 at 20:33