[Tex/LaTex] Decorate path precisely on intersection(s) with TikZ – avoiding crossing lines

tikz-decorationstikz-pgf

How can I (Is it possible to) make a decoration style which takes as arguement one path (or possibly more, that would be perfect), and makes an arc, snake, ellipse (whatever), where the paths intersect each other? I've seen These two ways of doing this:

But they are rather exaustive especially if one path has more than one intersection with the other, then, this can become very exaustive. Furthemore, they're mostly restricted to straight lines.

First attempt:

Inspired by Emma's answer to this question I was able to put together a MWE, but it's still far from good as it does not support curved paths and the lines do not join (if multiple coordinates are use to construct the path). Other than that, there's no control over the jump size…

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.pathreplacing,intersections,calc}
\tikzset{
    over path/.style={
        decoration={show path construction, lineto code={
          \path[name path=this path] (\tikzinputsegmentfirst) -- (\tikzinputsegmentlast);
          \path[name intersections={of=this path and #1, total=\t}, /utils/exec={\global\let\t=\t}]%
                                    let \n1={int(\t+1)} in%
                                    (\tikzinputsegmentfirst) coordinate (int-0)%
                                    foreach \i in {1,...,\n1}{%
                                      \ifnum \i<\n1%
                                          (intersection-\i) coordinate (int-\i)%
                                      \else
                                          (\tikzinputsegmentlast) coordinate (int-\n1)%
                                      \fi};
          \draw (\tikzinputsegmentfirst) foreach[remember=\i as \last (initially 0)] \i in {1,...,\t}{%
            let \p1=($(int-\last)-(int-\i)$), \n1={veclen(\x1,\y1)}, \n2={abs(4pt)}, \n3={\i+1}  in%
            [rounded corners=\n2/4] -- ($(int-\last)!\n1-\n2!(int-\i)$) to[bend left=90, looseness=1.7] ($(int-\last)!\n1+\n2!(int-\n3)$)} -- (\tikzinputsegmentlast);
                }
            },
            decorate
        }
    }

\begin{document}
\begin{tikzpicture}
    \coordinate (a) at (-1,0.5);
    \coordinate (b) at (8,0.5);
    \coordinate (c) at (3,-0.5);
    \draw[ultra thick, name path=sine, domain=-1:8, smooth, samples=50] plot (\x,{sin(\x r)});
    \draw[over path=sine] (a) -- (c) |- (b);
\end{tikzpicture}
\end{document}

enter image description here

Bad line join:

enter image description here

Best Answer

Here's a technique using the spath3 library. It works as follows:

  1. Split the over path where it intersects the under path.
  2. Insert gaps in the over path at these points.
  3. Splice in an arc into these gaps (joining it to the existing path so the joins are seamless).
  4. Split the under path where it intersects with the new over path.
  5. Insert small gaps in the under path at these points.

The development version (on github -- soon to be on CTAN) contains a version of the splicing code that ensures that the arc is always "upright". This will be on CTAN fairly soon.

Here's the result:

Bridges over sine waves

\documentclass{article}
%\url{https://tex.stackexchange.com/q/334483/86}
\usepackage{tikz}
\usetikzlibrary{spath3,intersections}


\begin{document}
\begin{tikzpicture}
\coordinate (a) at (-1,0.5);
\coordinate (b) at (8,0.5);
\coordinate (c) at (3,-0.5);
\path[
  ultra thick,
  spath/save=sine,
  domain=-1:8,
  smooth,
  samples=50
] plot (\x,{sin(\x r)});
\path[spath/save=over] (a) -- (c) |- (b);

\path[spath/save=arc] (0,0) arc[radius=1cm, start angle=180, delta angle=-180];

\tikzset{
  spath/split at intersections with={over}{sine},
  spath/insert gaps after components={over}{8pt},
  spath/join components with={over}{arc},
  spath/split at intersections with={sine}{over},
  spath/insert gaps after components={sine}{4pt},
}

\draw[spath/use=sine];
\draw[spath/use=over];
\end{tikzpicture}
\end{document}