18

I want to create a straight line between two nodes in a tikzpicture interrupted in the middle by a dotted portion. This should work whatever the orientation of the line.

Here is an expected output:

enter image description here

The best would be to have a line style defined for this instead of each time using clip to remove the straight line and then tweak a dotted one to fit the hole. A minimal code for the solution would look like

\documentclass[a4paper, 12pt]{report}

\usepackage{tikz}

\tikzset{
    middle dotted line/.style={
        thick, 
        % YOUR HELP HERE
    }
}% end of tikzset


\begin{document}

\begin{tikzpicture}
    \node (A) at (0, 3) {};
    \node (B) at (5, 3) {};
    \node (C) at (0, 2) {};
    \node (D) at (5, 0) {};
    \draw[middle dotted line] (A) -- (B);
    \draw[middle dotted line] (C) -- (D);
\end{tikzpicture}

\end{document}
2
  • 1
    Welcome to TeX.SE!
    – Mensch
    Apr 5 at 13:13
  • 1
    I take it \draw (A) -- (B) node[midpoint, fill=white, sloped]{$\cdots$};` won't do? Apr 5 at 15:26

8 Answers 8

12

Inspired by Mark Wibrow

\documentclass[tikz, border=5]{standalone}
\usetikzlibrary{decorations.pathreplacing,calc}
\tikzset{%
  middle dotted line/.style={
    decoration={show path construction, 
      lineto code={
          \draw[#1] (\tikzinputsegmentfirst) --($(\tikzinputsegmentfirst)!.3333!(\tikzinputsegmentlast)$);,
          \draw[dotted,#1] ($(\tikzinputsegmentfirst)!.3333!(\tikzinputsegmentlast)$)--($(\tikzinputsegmentfirst)!.6666!(\tikzinputsegmentlast)$);,
          \draw[#1] ($(\tikzinputsegmentfirst)!.6666!(\tikzinputsegmentlast)$)--(\tikzinputsegmentlast);,
      }
    },
    decorate
  },
}
\begin{document}

\begin{tikzpicture}
\draw[middle dotted line] (0,0)--(4,1);
\draw[middle dotted line={line width=1.5pt}] (1,1)--(3,4);
\end{tikzpicture}

\end{document}

enter image description here

11

Using the spath3 library (at time of writing, needs the development version from github).

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

\begin{document}

\begin{tikzpicture}
\draw[spath/save=path,ultra thick, red] (0,0) -- +(3,0);
\draw[spath/split at keep start={path}{1/3},spath/use=path];
\draw[spath/split at keep end={path}{2/3},spath/use=path];
\draw[spath/split at keep middle={path}{1/3}{2/3},spath/use=path,dotted];

\draw[spath/save=path,ultra thick, red] (0,-1) .. controls +(6,-1) and +(-6,-1) .. +(3,0);
\draw[spath/split at keep start={path}{1/3},spath/use=path];
\draw[spath/split at keep end={path}{2/3},spath/use=path];
\draw[spath/split at keep middle={path}{1/3}{2/3},spath/use=path,dotted];

\end{tikzpicture}
\end{document}

It works by splitting the path into three components and then rendering each component with a different style. As Henri mentions in the comments, to have a genuine solid and dotted line requires (at least) two draw commands - one for each style. I use one for each segment since combining the first and last segments would actually lead to slightly more complicated code.

As it uses the spath3 library, it works on arbitrary paths.

Here's the result:

solid and dotted lines

4
  • This should be the accepted answer, because it works on arbitrarily curved paths. The currently accepted answer only works on straight paths. Apr 6 at 13:01
  • 1
    It's a forlorn hope, I know, but it would be useful if the downvoter explained what they felt was bad about this answer. I try to be helpful on this site and when I miss the mark then the only way I'll improve is if I know what went wrong. Apr 7 at 5:52
  • Maybe use a lighter color instead of red. I only saw the black line and dots at second glance ... Sep 27 at 0:17
  • 1
    @JasperHabicht good suggestion - next time I'm on a suitable machine I'll hange it to a lighter shade Sep 27 at 5:54
5

Without guru's tricks try this:

\documentclass[tikz, border=1cm]{standalone}
\begin{document}
\begin{tikzpicture}
    \path (0,0) coordinate (A) (3,5) coordinate (B);
    \path (A)--(B) node [inner sep=0pt,pos=.333] (C) {};
    \path (A)--(B) node [inner sep=0pt,pos=.666] (D) {};
    \draw (A)--(C);
    \draw (D)--(B);
    \draw[dotted] (C)--(D);
\end{tikzpicture}
\end{document}

Output:

enter image description here

4
  • If you use the calc library then you can do your coordinate calculations all in one go without creating extra nodes. (It feels sufficiently close to this answer that I don't want to post a separate answer just for that, but you could add an example using calc) Apr 5 at 17:40
  • @AndrewStacey Thanks for your suggestion. But I post my answer just for its simplicity, without the help of others packages or libraries. I know that I can calculate the coordinates of my intermediates points C(1,5/3) and D(2,10/3): the parameter pos of Tikz make the job! Apr 5 at 21:55
  • Fair enough. I've added a separate calc answer then as I think it's a useful example to show how the calc library can shorten the code. Apr 5 at 22:57
  • 1
    an code-cleaning suggestion: using coordinate is more suitable than node. \documentclass[tikz, border=1cm]{standalone} \begin{document} \begin{tikzpicture} \path (0,0) coordinate (A) --(3,5) coordinate (B) coordinate[pos=1/3] (C) coordinate[pos=2/3] (D); \draw (A)--(C) (D)--(B); \draw[dotted] (C)--(D); \end{tikzpicture} \end{document}
    – Black Mild
    Apr 6 at 9:44
5

With the subpath function and time along a path, Asymptote handles this task very easy, for any curve.

enter image description here

unitsize(1cm);

// for curvy curves
path p=(0,-1) .. controls (0,-1)+(6,-1) and (3,-1)+(-6,-1) .. (3,-1);
real t1=.4,t2=.6;   // times on path
path q1=subpath(p,0,t1);
path q2=subpath(p,t1,t2);
path q3=subpath(p,t2,1);
draw(q1^^q3,magenta);
draw(q2,magenta+dotted);

// of course it works for straight lines
path pline=(0,0)--(3,1);
real s1=.3,s2=.7;   // times on path

path q1line=subpath(pline,0,s1);
path q2line=subpath(pline,s1,s2);
path q3line=subpath(pline,s2,1);
draw(q1line^^q3line,blue);
draw(q2line,blue+dotted);

shipout(bbox(5mm,invisible));
1
  • 1
    It's great seeing how other technologies approach problems. Now all we need is a pstricks solution to complete the set! Apr 6 at 13:39
3

A minor variation of Raffaele Santoro's answer using the calc library to shorten the code. It produces the same output.

\documentclass[tikz, border=1cm]{standalone}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
    \path (0,0) coordinate (A) (3,5) coordinate (B);
    \draw (A)--($(A)!1/3!(B)$) ($(A)!2/3!(B)$) -- (B);
    \draw[dotted] ($(A)!1/3!(B)$) -- ($(A)!2/3!(B)$);
\end{tikzpicture}
\end{document}
1
  • The code can be shortened even more when this is needed more than once: \tikzset{middle dotted line/.style args={#1 and #2}{to path={--($(\tikztostart)!#1!(\tikztotarget)$) coordinate(@s) ($(\tikztostart)!#2!(\tikztotarget)$) coordinate(@e) --(\tikztotarget) (@s)edge[line to,dotted](@e)}}} and then use it with \draw (A) to[middle dotted line=1/3 and 2/3] (B);. Sep 27 at 9:19
2

A solution using decorations which also works with curved paths:

\documentclass[border=10mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations}

\pgfdeclaremetadecoration{middle part dotted}{initial}{
    \state{initial}[width={\pgfmetadecoratedpathlength/3}, next state=medial]{
        \decoration{curveto}
        \afterdecoration{
            \pgfusepath{stroke}
        }
    }
    \state{medial}[width={\pgfmetadecoratedpathlength/3}, next state=final]{
        \beforedecoration{
            \pgfsetdash{{\pgflinewidth}{2pt}}{0pt}
        }
        \decoration{curveto}
        \afterdecoration{
            \pgfusepath{stroke}
        }
    }
    \state{final}{
        \beforedecoration{
            \pgfsetdash{}{0pt}
        }
        \decoration{curveto}
    }
}

\begin{document}

\begin{tikzpicture}

\draw[decorate, decoration={middle part dotted}] (0,0) -- +(3,0);

\draw[decorate, decoration={middle part dotted}] (0,-1) .. controls +(6,-1) and +(-6,-1) .. +(3,0);

\end{tikzpicture}

\end{document}

enter image description here

2

This needs a bit more math to make the pattern balanced/symmetrical …

Idea: Use the decorations library to get the length of the path and create a custom dash pattern that fits.

More work is needed for any dash pattern (that has more than one on and one off part) or any combination of dash patterns. Besides fractions of the path lengths one could even use absolute lengths (say, if only 1cm should be dotted and not 30% of the middle).

Code

\documentclass[tikz,border=5mm]{standalone}
\usetikzlibrary{decorations}
\makeatletter
\def\pgfutil@Repeat#1#2{#2\ifnum#1>0
  \expandafter\pgfutil@firstofone\else\expandafter\pgfutil@gobble\fi
  {\expandafter\pgfutil@Repeat\expandafter{\the\numexpr#1-1\relax}{#2}}}
\tikzset{
  dash between/.code args={#1 and #2}{%
    \tikz@addoption{%
      \pgfgetpath\currentpath
      \pgfprocessround{\currentpath}{\currentpath}%
      \pgf@decorate@parsesoftpath{\currentpath}{\currentpath}%
      \pgfmathsetlengthmacro\firstpart{(#1)*\pgf@decorate@totalpathlength}%
      \pgfmathsetlengthmacro\secondpart{(#2-(#1))*\pgf@decorate@totalpathlength}%
      \pgfmathsetlengthmacro\thirdpart{(1-(#2))*\pgf@decorate@totalpathlength}%
      \edef\thirdpart{{\thirdpart}{0pt}}%
      \edef\firstpart{{\firstpart}{0pt}}%
      \pgfmathsetlengthmacro\secondpartlength{\pgfkeysvalueof{/tikz/dash between on}
                                            +(\pgfkeysvalueof{/tikz/dash between off})}%
      \pgfmathtruncatemacro\repetitions{\secondpart/\secondpartlength}%
      \pgfmathsetlengthmacro\secondexpand{\secondpart/\repetitions-\secondpartlength}%
      \edef\secondexpand{\the\dimexpr\pgfkeysvalueof{/tikz/dash between off}+\secondexpand\relax}%
      \edef\secondpart{%
        \pgfutil@Repeat{\the\numexpr\repetitions-1\relax}%
          {{\pgfkeysvalueof{/tikz/dash between on}}{\secondexpand}}%
      }%
      \edef\tikz@temp{\firstpart\secondpart\thirdpart}%
      \expandafter\pgfsetdash\expandafter{\tikz@temp}{+0pt}%
    }
  }
}
\makeatother
\tikzset{
  dash between style/.is choice,
  dash between style/dotted/.style        ={dash between on=\pgflinewidth,dash between off=2pt},
  dash between style/densely dotted/.style={dash between on=\pgflinewidth,dash between off=1pt},
  dash between style/loosely dotted/.style={dash between on=\pgflinewidth,dash between off=4pt},
  dash between style/dashed/.style        ={dash between on=3pt,dash between off=2pt},
  dash between style/loosely dashed/.style={dash between on=3pt,dash between off=6pt},
  dash between style/densely dashed/.style={dash between on=3pt,dash between off=2pt},
  dash between style/no/.style={dash between on=0pt, dash between off=1pt},% dirty
  dash between on/.initial=\pgflinewidth,
  dash between off/.initial=2pt,
  %
  middle dotted line/.style={
    thick,
    dash between=.35 and .65}}
\begin{document}
\begin{tikzpicture}
    \coordinate (A) at (0, 3)
     coordinate (B) at (5, 3)
     coordinate (C) at (0, 2)
     coordinate (D) at (5, 0);
    \draw[middle dotted line] (A) -- (B);
    \draw[middle dotted line] (C) -- (D);
    \draw[
      ultra thick,
      dash between=1/3 and 2/3,
      dash between style=dotted,
    ] (0,-1) .. controls +(6,-1) and +(-6,-1) .. +(3,0);
\end{tikzpicture}
\end{document}

Output

enter image description here

1
2

Yet an other solution- I just add the three parts with a postaction decoration:

\documentclass[tikz, border=1cm]{standalone}
\usetikzlibrary{decorations.pathmorphing} 
\begin{document}
\tikzset{
middle part dotted/.style={
draw=none,
postaction={draw, decoration={curveto, post=moveto, post length=2/3*\pgfmetadecoratedpathlength}, decorate},
postaction={draw, dotted, decoration={curveto, pre=moveto, pre length=1/3*\pgfmetadecoratedpathlength, post=moveto, post length=1/3*\pgfmetadecoratedpathlength}, decorate},
postaction={draw, decoration={curveto, pre=moveto, pre length=2/3*\pgfmetadecoratedpathlength}, decorate},
},
}
\begin{tikzpicture}
\draw[middle part dotted] (0,0) -- +(3,0);
\draw[middle part dotted] (0,-1) .. controls +(6,-1) and +(-6,-1) .. +(3,0);
\end{tikzpicture}
\end{document}

Line and curve with middle part dotted

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.