Menu

#105 stretchable dash patterns to fit path/segment lengths

Next Release
open
nobody
5
2017-06-22
2017-06-22
No

There are cases where one would want a dash pattern to be stretchable/shrinkable to fit (in some way) in the path/segment length. For example, consider the the example https://tex.stackexchange.com/questions/133271/can-tikz-dashed-lines-emulate-pstricks-dashed-lines where the on part of the dash should both start and end the path (changing the off length and/or the on length to make the pattern exactly fit in the path length). A more complicated example is a square with edges that should be symmetric.

These can be accommodated with code, such as the one below, but would be better built into tikz/pgf.

For example, dash pattern on/off lengths could be stretchable/shrinkable like standard TeX lengths, such as on 10pt plus 1pt minus 1pt off 10pt plus 3pt minus 3 pt. This would signal to the underlying pgf code that the pattern should be stretched or shrunk to fit within a path length or within the length of each segment (whether path or segment could be specified by some option, such as dash path or dash segment or similar). (Note: glue such as plus 1fil or plus 3fill should be accommodated, which would imply that all of the stretch would happen in the on/off phase with that infinite stretchability.)

Further, dash phase would be stretched/expanded similar to the corresponding part of the dash pattern. For example, dash pattern=on 10pt plus 1pt minus 1pt off 5pt,dash phase=5pt would stretch/shrink the dash phase by half of whatever the on part of the pattern is stretched/shrunk; dash pattern=on 5pt plus 1pt minus 1pt off 5pt plus 1pt minus 1pt,dash phase=7.5pt would be stretched/shrunk by as much as the on part is stretched/shrunk plus HALF of how much the off part is stretched/shrunk.

Finally, to ensure being able to match at a path/segment end, a corresponding dash endphase (or similar) could be used to change the phase at the end of the path/segment. For example, consider the nice symmetry of the blue square in the example below; this could be done by exactly computing the correct dash pattern and dash phase based on the exact lengths of the sides of the square, but the user shouldn't have to do this calculation to get a nice layout. A dash endphase equal to the off phase length would allow the on phase end at the end of the path/segment. (So the blue box in the example below would be recreated with dash pattern=on 10pt off 6.5pt plus 3.5pt minus 3.5pt,dash phase=2.5pt,dash endphase=9pt,dash segment.)

(Note: the example below isn't intended to be perfect, and it is not clear that it is perfectly accurate for complicated curve segments.)

example image

\documentclass[tikz,border=5pt]{standalone}

\usetikzlibrary{decorations.pathreplacing}% for "show path construction"
\tikzset{
    cheating dash/.code args={on #1 off #2 ends #3}{%
        % mostly borrowed from V5 in answer to https://tex.stackexchange.com/questions/133271/can-tikz-dashed-lines-emulate-pstricks-dashed-lines
        \csname tikz@addoption\endcsname{%Use csname so catcode of @ doesn't have do be changed.
            \pgfgetpath\currentpath%
            \pgfprocessround{\currentpath}{\currentpath}%
            \csname pgf@decorate@parsesoftpath\endcsname{\currentpath}{\currentpath}%
            \pgfmathparse{max(#1-#3,0)}\let\dashphase=\pgfmathresult%
            \pgfmathparse{\csname pgf@decorate@totalpathlength\endcsname-#1+2*\dashphase}\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}}{\dashphase pt}}%
    },
    cheating dash per segment/.style args={on #1 off #2 ends #3}{
        /utils/exec=\csname tikz@options\endcsname,%inherit options/.code={[\csname tikz@options\endcsname]},inherit options,
        decoration={show path construction,
            %moveto code={},
            lineto code={\draw [cheating dash=on #1 off #2 ends #3] (\tikzinputsegmentfirst) -- (\tikzinputsegmentlast);},
            curveto code={\draw [cheating dash=on #1 off #2 ends #3] (\tikzinputsegmentfirst) .. controls (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb) .. (\tikzinputsegmentlast);},
            closepath code={\draw [cheating dash=on #1 off #2 ends #3] (\tikzinputsegmentfirst) -- (\tikzinputsegmentlast);}
        },
        decorate,
    },
}

\begin{document}
\begin{tikzpicture}[every node/.style={above,align=center,font=\tiny},mylines/.style={ultra thick,line cap=rect,line join=square}]

\draw[mylines,red,dash pattern=on 10pt off 6.5pt] (0,0) rectangle (2,2);
\node[red] at (1,2) {normal dash pattern\\on 10pt off 6.5pt};

\tikzset{xshift=2.5cm}

\draw[mylines,green!50!black,cheating dash=on 10pt off 6.5pt ends 7.5pt] (0,0) rectangle (2,2);
\node[green!50!black] at (1,2) {expandable dash gaps\\over entire path};

\tikzset{xshift=2.5cm}

\draw[mylines,blue,cheating dash per segment=on 10pt off 6.5pt ends 7.5pt] (0,0) rectangle (2,2);
\node[blue] at (1,2) {expandable dash gaps\\per segment};

\end{tikzpicture}
\end{document}

Discussion