4

Consider this MWE:

\documentclass[tikz,margin=1cm,12pt]{standalone}
\usetikzlibrary{calc,decorations,angles}

% Cheating Dash from https://tex.stackexchange.com/questions/133271/can-tikz-dashed-lines-emulate-pstricks-dashed-lines
\tikzset{
    cheating dash/.code args={on #1 off #2}{
        % Use csname so catcode of @ doesn't have do be changed.
        \csname tikz@addoption\endcsname{%
            \pgfgetpath\currentpath%
            \pgfprocessround{\currentpath}{\currentpath}%
            \csname pgf@decorate@parsesoftpath\endcsname{\currentpath}{\currentpath}%
            \pgfmathparse{\csname pgf@decorate@totalpathlength\endcsname-#1}\let\rest=\pgfmathresult%
            \pgfmathparse{#1+#2}\let\onoff=\pgfmathresult%
            \pgfmathparse{max(floor(\rest/\onoff), 1)}\let\nfullonoff=\pgfmathresult%
            \pgfmathparse{max((\rest-\onoff*\nfullonoff)/\nfullonoff+#2, #2)}\let\offexpand=\pgfmathresult%
            \pgfsetdash{{#1}{\offexpand}}{0pt}}%
    }
}

\begin{document}
\begin{tikzpicture}[line cap=rect]

    \coordinate (A) at (1,1);
    \coordinate (B) at ($(A)+(60:1)$);
    \coordinate (C) at ($(A)+(0,1)$);
    \coordinate (D) at ($(A)+(90-15:0.7)$);

    \draw pic[draw=orange,angle radius=0.6cm] {angle=B--A--C};
    \draw [gray,cheating dash=on 2pt off 2pt] (A) -- (B);
    \draw [gray,cheating dash=on 2pt off 2pt] (A) -- (C);

    \node[inner sep=2pt] at (0.5,2) (Theta) {$\vartheta$}; 
    \draw[arrows=->] (Theta.east) -- (D);

\end{tikzpicture}
\end{document}

enter image description here How can I hide the dashed line segment that gets crossed by the arrow?

I know one way to solve this is to plot another white arrow (with larger line width) underneath the original arrow. But I am looking for a solution that doesn't involve this method.

I want TikZ to be able to 'detect' when any part of the arrow is crossing a dashed line and automatically hide those dashed line segments it crosses. This way, all the dashed line segments that remain should be of full length (because they aren't being covered by the arrow).

Is there a way to do this?

UPDATE

As an example of why I don't necessarily want a white halo/background behind the arrow, consider this modification: \coordinate (D) at (1.15,1.6);. The arrow misses the dashes, therefore no dashes need to be removed or covered!

enter image description here

1
  • 1
    to be honest I would prefer visually slashing the dash instead to emphasize that it is a different line.
    – percusse
    Commented May 15, 2018 at 17:35

1 Answer 1

4

SLIGHTLY MORE ELEGANT CODE: I erased my very first post, which was done already in a much more elegant way in Jake's answer. Here is a slightly more elegant version of my first real answer, which I kept because this has a slight chance of working accidentally for curved dashed lines, which this one does not. The advantage of this post is that it is a bit more user-friendly (yet there is a lot of room for improvement) and it does not accidentally wipe out other lines. The strategy is to "repath" the dashes and store them as mini paths. (Yes, it would be straightforward to add arrows to those etc. but there are much simpler ways of adding these arrows.) Then this code finds the intersections of the arrows and "wipes out" "offending" dashes.

\documentclass[tikz,margin=1cm,12pt]{standalone}
\usetikzlibrary{calc,decorations,angles,decorations.markings,intersections}

\newcounter{minipath}
\setcounter{minipath}{0}

\makeatletter
% Cheating Dash from https://tex.stackexchange.com/questions/133271/can-tikz-dashed-lines-emulate-pstricks-dashed-lines
\tikzset{
    cheating dash/.code args={on #1 off #2}{
        % Use csname so catcode of @ doesn't have do be changed.
        \csname tikz@addoption\endcsname{%
            \pgfgetpath\currentpath%
            \pgfprocessround{\currentpath}{\currentpath}%
            \csname pgf@decorate@parsesoftpath\endcsname{\currentpath}{\currentpath}%
            \pgfmathsetmacro{\rest}{\csname pgf@decorate@totalpathlength\endcsname-#1}%
            \pgfmathsetmacro{\mylen}{\csname pgf@decorate@totalpathlength\endcsname}
            \pgfmathsetmacro{\onoff}{#1+#2}%    
            \pgfmathsetmacro{\nfullonoff}{max(floor(\rest/\onoff), 1)}%
            \pgfmathsetmacro{\offexpand}{max((\rest-\onoff*\nfullonoff)/\nfullonoff+#2,#2)}%
            \pgfextra{\xdef\myoff{\offexpand}%
            \xdef\myon{#1}%
            \pgfmathtruncatemacro{\myN}{\nfullonoff+0.1}\xdef\myN{\myN}%
                \xdef\mylen{\mylen}}% \typeout{\myon,\myoff,\mylen,\myN}
            \pgfsetdash{{#1}{\offexpand}}{0pt}}%
    }
}

\tikzset{% from https://tex.stackexchange.com/a/26386/121799
  use path for main/.code={%
    \tikz@addmode{%
      \expandafter\pgfsyssoftpath@setcurrentpath\csname tikz@intersect@path@name@#1\endcsname
    }%
  },
  use path for actions/.code={%
    \expandafter\def\expandafter\tikz@preactions\expandafter{\tikz@preactions\expandafter\let\expandafter\tikz@actions@path\csname tikz@intersect@path@name@#1\endcsname}%
  },
  use path/.style={%
    use path for main=#1,
    use path for actions=#1,
  }
}
\makeatother

% Note that this this works ONLY FOR STRAIGHT LINES
% in principle, one COULD generalize it to arbitrary curves using the 
% tricks from bending library
\tikzset{store dashes/.style={postaction={decorate},decoration={markings,
mark=between positions 0 and 1 step {\myon+\myoff*1pt}
      with % \typeout{\pgfkeysvalueof{/pgf/decoration/mark info/sequence number}}
      {\pgfextra{\stepcounter{minipath}}
        %\draw[blue,name path=minipath-\theminipath] (0,0) -- (\myon,0);
        \path[name path=minipath-\theminipath] (0,0) -- (\myon,0);
      }
}}}

\tikzset{wipe out dashes/.code={\foreach \X in {1,...,\theminipath}
    {\path[name intersections={of={#1} and {minipath-\X}, name=x,
    total=\n}]\pgfextra{\xdef\NumInt{\n}};
    \ifnum\NumInt>0
    \draw[use path=minipath-\X,white,line width=1.1\pgflinewidth];
    \fi
    }}}

\begin{document}
\begin{tikzpicture}[line cap=rect]

    \coordinate (A) at (1,1);
    \coordinate (B) at ($(A)+(60:1)$);
    \coordinate (C) at ($(A)+(0,1)$);
    \coordinate (D) at ($(A)+(90-15:0.7)$);

    \draw pic[draw=orange,angle radius=0.6cm] {angle=B--A--C};
    \draw [gray,cheating dash=on 2pt off 2pt,store dashes] (A) -- (B) coordinate[pos=0.9] (H);
    \draw [gray,cheating dash=on 2pt off 2pt,store dashes] (A) -- (C);
    \node[inner sep=2pt] at (0.5,2) (Theta) {$\vartheta$}; 

    \path[name path global=myline] (Theta.east) -- (D);
    \path[wipe out dashes=myline,postaction={use path=myline,arrows=->,draw}];


\end{tikzpicture}
\end{document}

HISTORY: Just for fun: detect if the arrow path actually really intersects with one of the dashes. If so, the dash gets removed. It can be simplified. The most ugly thing at this point is that I need to know the length of the dashes. (In principle, this can also be avoided by redrawing the corresponding minipath in white, this would be even more effort. Done, see above.)

\documentclass[tikz,margin=1cm,12pt]{standalone}
\usetikzlibrary{calc,decorations,angles,decorations.markings,intersections}

\newcounter{minipath}
\setcounter{minipath}{0}

% Cheating Dash from https://tex.stackexchange.com/questions/133271/can-tikz-dashed-lines-emulate-pstricks-dashed-lines
\tikzset{
    cheating dash/.code args={on #1 off #2}{
        % Use csname so catcode of @ doesn't have do be changed.
        \csname tikz@addoption\endcsname{%
            \pgfgetpath\currentpath%
            \pgfprocessround{\currentpath}{\currentpath}%
            \csname pgf@decorate@parsesoftpath\endcsname{\currentpath}{\currentpath}%
            \pgfmathsetmacro{\rest}{\csname pgf@decorate@totalpathlength\endcsname-#1}%
            \pgfmathsetmacro{\mylen}{\csname pgf@decorate@totalpathlength\endcsname}
            \pgfmathsetmacro{\onoff}{#1+#2}%    
            \pgfmathsetmacro{\nfullonoff}{max(floor(\rest/\onoff), 1)}%
            \pgfmathsetmacro{\offexpand}{max((\rest-\onoff*\nfullonoff)/\nfullonoff+#2,#2)}%
            \pgfextra{\xdef\myoff{\offexpand}%
            \xdef\myon{#1}%
            \pgfmathtruncatemacro{\myN}{\nfullonoff+0.1}\xdef\myN{\myN}%
                \xdef\mylen{\mylen}}% \typeout{\myon,\myoff,\mylen,\myN}
            \pgfsetdash{{#1}{\offexpand}}{0pt}}%
    }
}

\tikzset{store dashes/.style={postaction={decorate},decoration={markings,
mark=between positions 0 and 1 step {\myon+\myoff*1pt}
      with % \typeout{\pgfkeysvalueof{/pgf/decoration/mark info/sequence number}}
      {\pgfextra{\stepcounter{minipath}}
        %\draw[blue,name path=minipath-\theminipath] (0,0) -- (\myon,0);
        \path[name path=minipath-\theminipath] (0,0) -- (\myon,0);
      }
}}}

\begin{document}
\begin{tikzpicture}[line cap=rect]

    \coordinate (A) at (1,1);
    \coordinate (B) at ($(A)+(60:1)$);
    \coordinate (C) at ($(A)+(0,1)$);
    \coordinate (D) at ($(A)+(90-15:0.7)$);

    \draw pic[draw=orange,angle radius=0.6cm] {angle=B--A--C};
    \draw [gray,cheating dash=on 2pt off 2pt,store dashes] (A) -- (B) coordinate[pos=0.9] (H);
    \draw [gray,cheating dash=on 2pt off 2pt,store dashes] (A) -- (C);
    \typeout{\theminipath}
    \node[inner sep=2pt] at (0.5,2) (Theta) {$\vartheta$}; 

    \path[name path=myline] (Theta.east) -- (D);
    \foreach \X in {1,...,\theminipath}
    {\fill[white]  [name intersections={of={myline} and {minipath-\X}, name=x, total=\n}]
    %\pgfextra{\typeout{\theminipath,\X,\n}}
    \ifnum\n>0
    \foreach \Y in {1,...,\n} { (x-\Y) circle(2pt)} %<- ugly: I need to know the length of the dash pattern here
    \fi
    ;}
    \draw[name path=myline,arrows=->] (Theta.east) -- (D);


\end{tikzpicture}
\end{document}

enter image description here

Depending on how things go I may find enough motivation to polish this code. At this point it just shows that it can be done in practice.

0

You must log in to answer this question.

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