6

I'm trying to make a knot diagram and to visualize the deformation of a knot, I'd like to split the path from a certain point and have it continue in two directions. I've attempted to get what I want by decorating the knot path at a certain position with another Hobby path, like so:

\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{hobby,decorations.markings}

\begin{document}
        \begin{tikzpicture}[scale=2,use Hobby shortcut]
        \draw[line width=3,white,double=black,double distance=1,postaction={decorate,decoration={markings,mark=at position 0.8 with {
        \draw[use Hobby shortcut,dashed,line width=1,black] (0,0) .. (0.5,-0.5) .. (0,-1) .. (-3.3,0.3);
        }}},closed]
(180:1) .. (240:0.9)
        .. ([blank=soft]300:0.3) .. (120:0.3)
        .. ([blank=soft]60:0.9) .. (0:1) .. (300:0.9)   .. (240:0.3)
        .. ([blank=soft,]60:0.3) .. (120:0.9) [save Hobby path={trefoil}];
        \draw[line width=3,white,double=black,double distance=1,restore and use Hobby path={trefoil}{invert soft blanks,disjoint}];
        \end{tikzpicture}
\end{document}

And this is what it looks like right now:

It's pretty, but not what I want :(

This is actually close to what I want (I just need to connect the dashed line to the bottom left part of the knot)! However, the trial and error process of finding the right coordinates to draw the dashed line is very tedious.

Here is what I'm looking for, specifically:

  • Is it possible to split the path in two from a position of my choosing some other way?
  • Is it possible to make the split smooth (i.e. the tangent of the dashed line matches the tangent of the knot)?
  • Is it possible to somehow retrieve the angle of the knot path at a certain point? Then it might be possible to draw a Hobby path starting out with a certain angle from the knot.
  • If all else fails and I'm resigned to drawing a path as a decoration (as I'm doing now), is it possible to use "global" coordinates to draw the decoration? Right now, the coordinate (0,0) of the decoration denotes the current position on the knot. Is there a way to change this so that it means the origin?

This might be a lot to ask. On the other hand, all this might have a very simple solution that I overlooked or haven't been able to find. I would really appreciate any help towards making this possible (also, if some things are not clear I'd be happy to clarify).

P.S. While searching for an answer, it dawned on me that this may not specifically be a Hobby problem, hence the parentheses in the title...

  • Have you considered using the knots library? Or celtic, by the author of the Hobby library? That is, do you have any particular reason for redesigning the wheel in a hexagonal shape? – cfr May 28 '17 at 18:38
  • Yay! knots and hobby combined. All you need is tikzmark and this would be my favourite question ... more seriously, splitting a bezier curve at a point would be a useful thing to be able to do (which is what you're effectively asking for). I've been pondering implementing that in the library underlying the knots library. – Loop Space May 28 '17 at 18:51
  • @cfr I've played around with the knots library, but I don't see how that makes the job any easier here. I'd love to be wrong, though! – Ontwikseltsaar May 28 '17 at 19:02
  • @LoopSpace I suppose that is what I'm asking for! I always have trouble putting my questions into words. Might you know of a way to bypass the problem as in bullet points 3 and 4? – Ontwikseltsaar May 28 '17 at 19:04
  • 1
    @Ontwikseltsaar Just like to note - for the record - that this appears to have been the question that pushed me over 100k. Thanks for asking it! – Loop Space Jun 1 '17 at 16:29
6

Nearly all the pieces were somewhere in the TikZ and hobby code, it just required a bit of assembling the jigsaw pieces and fixing two bugs in the hobby code. The bug fixes will (eventually) make their way into the hobby code. The rest could be quite useful somewhere as well (I've a few ideas wrt the knots library).

In brief, the new bit is to make it possible to place a node at an intersection as if the pos key had been specified. This means that the node can be rotated to lie along the curve at that point (using the coordinates provided by the intersections library doesn't allow for this last bit). Then its east/west anchors lie on a line tangential to the curve at that point and can be used to define control points for a new curve.

The intersections library provides all the necessary information, the key was in extracting it. See the code below for comments on how this is done.

The hobby bugs related to placing nodes on parts of a hobby-generated curve. These are now fixed in the version of hobby on github (run tex hobby.dtx to generate the files) so I've removed them from the code below.

\documentclass[a4paper]{article}
%\url{https://tex.stackexchange.com/q/372089/86}
\usepackage{tikz}
\usetikzlibrary{hobby,intersections,calc}

\makeatletter
\tikzset{
  place at intersection with code/.code={%
    % Test to see if the named path exists, if not display a message
    \pgfutil@ifundefined{tikz@intersect@path@name@#1}{\message{Path `#1' not found.}}{\find@intersection@point{#1}}
  },
  place at intersection with/.style={
    % This option defines the intersection point
    place at intersection with code=#1,
    node contents={},
    % The next two mean that the placed node will align itself with the curve
    sloped,
    allow upside down=true
  }
}

\def\find@intersection@point#1{%
  % Do everything inside a group so as not to upset things
  \begingroup
  % First step is to reconstruct the last path segment, using the \tikz@timer stuff
  \def\intersection@pathsegment{}%
  % Save the current path
  \pgfsyssoftpath@getcurrentpath\intersection@temppath
  % And initialise with an empty path
  \pgfsyssoftpath@setcurrentpath\intersection@pathsegment%
  % Move to the starting point
  \pgfpathmoveto{\tikz@timer@start}%
  % The rest depends on the type of segment we just had
  \ifx\tikz@timer\tikz@timer@curve
  % Bezier curve
  \pgfpathcurveto{\tikz@timer@cont@one}{\tikz@timer@cont@two}{\tikz@timer@end}%
  \else
  \ifx\tikz@timer@line
  % Straight line
  \pgfpathlineto{\tikz@timer@end}%
  \else
  \ifx\tikz@timer\tikz@timer@hvline
  % Horizontal-Vertical line
  \tikz@timer@start
  \pgf@ya=\pgf@y
  \tikz@timer@end
  \pgf@xa=\pgf@x
  \pgfpathlineto{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
  \pgfpathlineto{\tikz@timer@end}%
  \else
  \ifx\tikz@timer\tikz@timer@vhline
  % Vertical-horizontal line
  \tikz@timer@start
  \pgf@xa=\pgf@x
  \tikz@timer@end
  \pgf@ya=\pgf@y
  \pgfpathlineto{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
  \pgfpathlineto{\tikz@timer@end}%
  \else
  \ifx\tikz@timer\tikz@timer@arc
  % Need to find out how to reconstruct an arc ...
  \fi
  \fi
  \fi
  \fi
  \fi
  % Get the newly created path segment
  \pgfsyssoftpath@getcurrentpath\intersection@pathsegment
  % Restore the original path
  \pgfsyssoftpath@setcurrentpath\intersection@temppath%
  % Sorting the intersections is the trigger for remembering the intersection point as a parameter along the path
  \pgfintersectionsortbyfirstpath
  % Call the intersection algorithm
  \pgfintersectionofpaths{\pgfsetpath\intersection@pathsegment}{\expandafter\pgfsetpath\csname tikz@intersect@path@name@#1\endcsname}%
  %
  \ifnum\pgfintersectionsolutions>0\relax
  % If we got an intersection, store the parameter corresponding to the first one
  \xdef\intersection@time{\csname pgf@g@intersect@solution@1@time@a\endcsname}%
  \else
  % If not, say so and default to the start of the segment
  \message{No intersection found}%
  \gdef\intersection@time{0}%
  \fi
  \endgroup
  % Set the position of the current node to the found parameter
  \tikzset{pos=\intersection@time}%
}
\makeatother

\begin{document}

\begin{tikzpicture}[scale=2,use Hobby shortcut]
% This is the path we'll use to define our cutting points.  The 'overlay' option means that it doesn't affect the bounding box
\path[name path=c,overlay] (-.3,0) -- +(0,1) -- +(0,-1);
% Define our hobby curve
\draw[line width=3,white,double=black,double distance=1,closed]
(180:1) .. (240:0.9)
% The 'place at intersection with=c' key puts this node at the intersection of this segment with path 'c'
% Even though we've specified `node contents={}` in the style, we still need the trailing {} due to how nodes are collected on paths
.. node[place at intersection with=c,name=dpt] {}  ([blank=soft]300:0.3) .. (120:0.3) 
.. ([blank=soft]60:0.9) .. (0:1) .. (300:0.9)   .. (240:0.3)
.. ([blank=soft,]60:0.3) .. node[place at intersection with=c,name=upt] {}  (120:0.9) [save Hobby path={trefoil}];
\draw[line width=3,white,double=black,double distance=1,restore and use Hobby path={trefoil}{invert soft blanks,disjoint}];
% Once we have our nodes placed, we can use the anchors to define the 'exit' paths.  The centre node is at the intersection point and the east-west line goes along the tangent line.
\draw[dashed] (upt.center) .. controls ($(upt.center)!2cm!(upt.east)$) and +(0,2) .. (1.5,0) .. controls +(0,-2) and  ($(dpt.center)!2cm!(dpt.west)$) .. (dpt.center);
\end{tikzpicture}
\end{document}

I hope this is what you were after.

Reidemeister moves

  • That's exactly what I was looking for. I can't even begin to comprehend what that wall of code does exactly, but removing it sure makes the paths do wacky things. I see it also works like a charm with \strand in the knots environment, thank you! – Ontwikseltsaar May 30 '17 at 9:46
  • 1
    I was typing on a tablet so didn't want to type too many characters. I'll add a commentary when back at my computer. – Loop Space May 30 '17 at 16:24
  • 2
    @Ontwikseltsaar I've added some comments to the code. Also, the fixes to the hobby package are now integrated into the code on github so I've removed them from this answer. Eventually, they'll make their way to CTAN. – Loop Space May 31 '17 at 9:48
  • 1
    @cfr Thanks for the reminder. Happened to catch me when I had a few spare minutes so providing I have successfully negotiated the CTAN upload process, it's in the pipeline (also the hobby and tqft packages). – Loop Space Jun 1 '17 at 10:02
  • @cfr CTAN appears to have the latest versions (might take time to propagate to mirrors and percolate into TL). – Loop Space Jun 1 '17 at 16:28

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.