0

In TikZ: How to draw a pattern at the border of a not closed tikz path, the solution draws a hatched pattern beside a line. I would like to adapt it to also fill in the space behind the pattern to cover up things drawn before the hatching, but adding a "fill" in the place that seems logical to me (by comment % MODIFIED: added fill=white in the MWE) doesn't work. MWE:

\documentclass[tikz]{standalone}
\usepackage{tikz}
\usetikzlibrary{patterns}
\usetikzlibrary{decorations,backgrounds}

% BEGIN from https://tex.stackexchange.com/questions/518580/tikz-how-to-draw-a-pattern-at-the-border-of-a-not-closed-tikz-path
\newcounter{tmp}
\newif\ifpathisclosed

\makeatletter
\def\pgfdecoratedcontourdistance{0pt}
\pgfset{
    decoration/contour distance/.code=%
    \pgfmathsetlengthmacro\pgfdecoratedcontourdistance{#1}}
\pgfdeclaredecoration{contour lineto closed}{start}{%
    \state{start}[
    next state=draw,
    width=0pt,
    persistent precomputation=\let\pgf@decorate@firstsegmentangle\pgfdecoratedangle]{%
        %\xdef\myList{}\xdef\mySecondList{}%
        \setcounter{tmp}{0}%
        \global\pathisclosedfalse%
        \pgfpathmoveto{\pgfpointlineattime{.5}
            {\pgfqpoint{0pt}{\pgfdecoratedcontourdistance}}
            {\pgfqpoint{\pgfdecoratedinputsegmentlength}{\pgfdecoratedcontourdistance}}}%
    }%
    \state{draw}[next state=draw, width=\pgfdecoratedinputsegmentlength]{%
        \ifpgf@decorate@is@closepath@%
        \pgfmathsetmacro\pgfdecoratedangletonextinputsegment{%
            -\pgfdecoratedangle+\pgf@decorate@firstsegmentangle}%
        \fi
        \pgfmathsetlengthmacro\pgf@decoration@contour@shorten{%
            -\pgfdecoratedcontourdistance*cot(-\pgfdecoratedangletonextinputsegment/2+90)}%
        \pgfpathlineto
        {\pgfpoint{\pgfdecoratedinputsegmentlength+\pgf@decoration@contour@shorten}
            {\pgfdecoratedcontourdistance}}%
        \stepcounter{tmp}%
        \pgfcoordinate{muemmel\number\value{tmp}}{\pgfpoint{\pgfdecoratedinputsegmentlength+\pgf@decoration@contour@shorten}
            {\pgfdecoratedcontourdistance}}%
        \pgfcoordinate{feep\number\value{tmp}}{\pgfpoint{\pgfdecoratedinputsegmentlength}{0pt}}%      
        \ifnum\value{tmp}=1\relax%
        \pgfcoordinate{muemmel0}{\pgfpoint{0pt}{\pgfdecoratedcontourdistance}}%
        \pgfcoordinate{feep0}{\pgfpoint{0pt}{0pt}}%      
        \xdef\myList{(muemmel\number\value{tmp})}%
        \xdef\mySecondList{(feep\number\value{tmp})}%
        \else
        \xdef\myList{\myList -- (muemmel\number\value{tmp})}%
        \xdef\mySecondList{(feep\number\value{tmp}) -- \mySecondList}%
        \fi 
        \ifpgf@decorate@is@closepath@%
        \pgfpathclose
        \global\pathisclosedtrue%
        \fi
    }%
    \state{final}{%\typeout{\myList,\mySecondList}%
        \ifpathisclosed%
        \xdef\myList{\myList -- cycle}% 
        \xdef\mySecondList{\mySecondList -- cycle}%
        %\typeout{closed \mySecondList \myList }
        \else
        %\typeout{\number\value{tmp}}%
        \xdef\myList{(muemmel0) -- \myList -- cycle}% 
        \xdef\mySecondList{\mySecondList -- (feep0) --}%
        %\typeout{not closed \mySecondList \myList }%   
        \fi
    }%
}
\makeatother
\tikzset{
    contour/.style={
        decoration={
            name=contour lineto closed,
            contour distance=#1
        },
        decorate}
}
    
\tikzset{EDR/.style={
        preaction={draw=red,line width=1pt},
        preaction={decoration={contour lineto closed, contour distance=6pt},
            decorate,
        },
        postaction={
            insert path={%
                \pgfextra{%
                    \pgfinterruptpath
                    \path[pattern=north west lines, pattern color=red,even odd rule] 
                    \mySecondList \myList 
                    ;
                    \endpgfinterruptpath}
        }},
}}
    
% END from https://tex.stackexchange.com/questions/518580/tikz-how-to-draw-a-pattern-at-the-border-of-a-not-closed-tikz-path

\tikzset{EDRB/.style={
        preaction={draw=red,line width=1pt},
        preaction={decoration={contour lineto closed, contour distance=6pt},
            decorate,
        },
        postaction={
            insert path={%
                \pgfextra{%
                    \pgfinterruptpath
                    \path[fill=white,pattern=north west lines, pattern color=red,even odd rule] % MODIFIED: added fill=white
                    \mySecondList \myList 
                    ;
                    \endpgfinterruptpath}
        }},
}}

\begin{document}
    
    \begin{tikzpicture}
        \node at (3,3.5) {Current};
        
            \draw[ultra thick,blue] (0,3) -- (6,2);
            \path [EDR,draw, red, line width=1pt] (0,3) -- ++(0,-1) -- ++(6,0) -- ++(0,1) 
                -- ++(-3.125cm-6pt,0);
                
        \begin{scope}[shift={(0,-3)}]
            
            \node at (3,3.5) {Desired};
            
            \begin{scope}
                \clip (6pt,3) rectangle (6cm-6pt,2cm+6pt);
                \draw[ultra thick,blue] (0,3) -- (6,2);
            \end{scope}

            \path [EDR,draw, red, line width=1pt] (0,3) -- ++(0,-1) -- ++(6,0) -- ++(0,1) 
            -- ++(-3.125cm-6pt,0);
            
        \end{scope}
    
        \begin{scope}[shift={(0,-6)}]
            
            \node at (3,3.5) {Attempt: ``EDRB'' Style};
            
            \draw[ultra thick,blue] (0,3) -- (6,2);
            
            \path [EDR,draw, red, line width=1pt] (0,3) -- ++(0,-1) -- ++(6,0) -- ++(0,1) 
            -- ++(-3.125cm-6pt,0);
            
        \end{scope}
    \end{tikzpicture}

    
\end{document}

MWE Output:

enter image description here

Is there a way to add a fill behind the pattern?

6
  • So much code … is the blue line overpainted by non-opaque layer before it?
    – MS-SPO
    Commented Jun 8, 2023 at 14:50
  • I'm not sure I understand what you mean about a non-opaque layer. I'm not actively using layers - in the "Desired" image I got the behaviour that I want using a clip rectangle, but I don't want to have to do that manually. I agree that it is a lot of code, but it is almost entirely copied from the linked question. As far as I can tell, it is the minimal working example. Commented Jun 8, 2023 at 14:54
  • It means that blue is overpainted by the red regions, so blue can‘t shine through. Just like with regular drawing programs, which use layers. // If that‘s your minimal code, something is wrong. And no, I didn‘t spend more than a glance on it. But it‘s longer multiple times compared to code from the pgfmanual.
    – MS-SPO
    Commented Jun 8, 2023 at 15:18
  • Hmm, two fills: one white and one patterned? Or clip the blue line in a scope., Commented Jun 8, 2023 at 17:25
  • If I'm seeing this correctly, the blue shouldn't merely get covered/clipped against the patterned area but actually shortened since the top part doesn't show up above the patterned area. On the other end, the blue line should look like it's clipped. This won't be easy to recreate. Commented Jun 8, 2023 at 18:34

1 Answer 1

2

I was able to hack together something that works. The changes required were:

  1. Add a separate \path in the \pgfextra{} block for applying the filling
  2. Add a new draw to the postaction list so that the edge line is above the fill (without this the edge line is below the fill so half its width is obscured

The MWE is then:

\documentclass[tikz]{standalone}
\usepackage{tikz}
\usetikzlibrary{patterns}
\usetikzlibrary{decorations,backgrounds}

% BEGIN from https://tex.stackexchange.com/questions/518580/tikz-how-to-draw-a-pattern-at-the-border-of-a-not-closed-tikz-path
\newcounter{tmp}
\newif\ifpathisclosed

\makeatletter
\def\pgfdecoratedcontourdistance{0pt}
\pgfset{
    decoration/contour distance/.code=%
    \pgfmathsetlengthmacro\pgfdecoratedcontourdistance{#1}}
\pgfdeclaredecoration{contour lineto closed}{start}{%
    \state{start}[
    next state=draw,
    width=0pt,
    persistent precomputation=\let\pgf@decorate@firstsegmentangle\pgfdecoratedangle]{%
        %\xdef\myList{}\xdef\mySecondList{}%
        \setcounter{tmp}{0}%
        \global\pathisclosedfalse%
        \pgfpathmoveto{\pgfpointlineattime{.5}
            {\pgfqpoint{0pt}{\pgfdecoratedcontourdistance}}
            {\pgfqpoint{\pgfdecoratedinputsegmentlength}{\pgfdecoratedcontourdistance}}}%
    }%
    \state{draw}[next state=draw, width=\pgfdecoratedinputsegmentlength]{%
        \ifpgf@decorate@is@closepath@%
        \pgfmathsetmacro\pgfdecoratedangletonextinputsegment{%
            -\pgfdecoratedangle+\pgf@decorate@firstsegmentangle}%
        \fi
        \pgfmathsetlengthmacro\pgf@decoration@contour@shorten{%
            -\pgfdecoratedcontourdistance*cot(-\pgfdecoratedangletonextinputsegment/2+90)}%
        \pgfpathlineto
        {\pgfpoint{\pgfdecoratedinputsegmentlength+\pgf@decoration@contour@shorten}
            {\pgfdecoratedcontourdistance}}%
        \stepcounter{tmp}%
        \pgfcoordinate{muemmel\number\value{tmp}}{\pgfpoint{\pgfdecoratedinputsegmentlength+\pgf@decoration@contour@shorten}
            {\pgfdecoratedcontourdistance}}%
        \pgfcoordinate{feep\number\value{tmp}}{\pgfpoint{\pgfdecoratedinputsegmentlength}{0pt}}%      
        \ifnum\value{tmp}=1\relax%
        \pgfcoordinate{muemmel0}{\pgfpoint{0pt}{\pgfdecoratedcontourdistance}}%
        \pgfcoordinate{feep0}{\pgfpoint{0pt}{0pt}}%      
        \xdef\myList{(muemmel\number\value{tmp})}%
        \xdef\mySecondList{(feep\number\value{tmp})}%
        \else
        \xdef\myList{\myList -- (muemmel\number\value{tmp})}%
        \xdef\mySecondList{(feep\number\value{tmp}) -- \mySecondList}%
        \fi 
        \ifpgf@decorate@is@closepath@%
        \pgfpathclose
        \global\pathisclosedtrue%
        \fi
    }%
    \state{final}{%\typeout{\myList,\mySecondList}%
        \ifpathisclosed%
        \xdef\myList{\myList -- cycle}% 
        \xdef\mySecondList{\mySecondList -- cycle}%
        %\typeout{closed \mySecondList \myList }
        \else
        %\typeout{\number\value{tmp}}%
        \xdef\myList{(muemmel0) -- \myList -- cycle}% 
        \xdef\mySecondList{\mySecondList -- (feep0) --}%
        %\typeout{not closed \mySecondList \myList }%   
        \fi
    }%
}
\makeatother
\tikzset{
    contour/.style={
        decoration={
            name=contour lineto closed,
            contour distance=#1
        },
        decorate}
}

\tikzset{D/.style={
        preaction={draw=blue,line width=1pt},
        preaction={decoration={contour lineto closed, contour distance=6pt},
            decorate,
        },
        postaction={
            insert path={%
                \pgfextra{%
                    \pgfinterruptpath
                    \path[fill=white,even odd rule] 
                    \mySecondList \myList 
                    ;
                    \path[pattern=north west lines, pattern color=red,even odd rule] 
                    \mySecondList \myList 
                    ;
                    \endpgfinterruptpath}
        }},
}}
\tikzset{EDR/.style={
        preaction={draw=red,line width=1pt},
        preaction={decoration={contour lineto closed, contour distance=6pt},
            decorate,
        },
        postaction={
            insert path={%
                \pgfextra{%
                    \pgfinterruptpath
                    \path[fill=white,even odd rule] 
                    \mySecondList \myList 
                    ;
                    \path[pattern=north west lines, pattern color=red,even odd rule] 
                    \mySecondList \myList 
                    ;
                    \endpgfinterruptpath}
        }},
}}
    
% END from https://tex.stackexchange.com/questions/518580/tikz-how-to-draw-a-pattern-at-the-border-of-a-not-closed-tikz-path

\tikzset{
    EDRB fill color/.initial=white, % NEW
    EDRB fill color/.get=\EDRBfillcolor, % NEW
    EDRB fill color/.store in=\EDRBfillcolor, % NEW
    EDRB/.style={
        preaction={decoration={contour lineto closed, contour distance=6pt},
            decorate,
        },
        postaction={
            insert path={%
                \pgfextra{%
                    \pgfinterruptpath
                    \path[fill=\EDRBfillcolor,even odd rule] % NEW
                    \mySecondList \myList % NEW
                    ; % NEW
                    \path[pattern=north west lines,pattern color=red,even odd rule]
                    \mySecondList \myList 
                    ;
                    \endpgfinterruptpath}
            },
            draw=red,% NEW
        },
    }
}

\begin{document}
    
    \begin{tikzpicture}
        \node at (3,3.5) {Current};
        
            \draw[ultra thick,blue] (0,3) -- (6,2);
            \path [EDR,draw, red, line width=1pt] (0,3) -- ++(0,-1) -- ++(6,0) -- ++(0,1) 
                -- ++(-3.125cm-6pt,0);
                
        \begin{scope}[shift={(0,-3)}]
            
            \node at (3,3.5) {Desired};
            
            \begin{scope}
                \clip (6pt,3) rectangle (6cm-6pt,2cm+6pt);
                \draw[ultra thick,blue] (0,3) -- (6,2);
            \end{scope}

            \path [EDR,draw, red, line width=1pt] (0,3) -- ++(0,-1) -- ++(6,0) -- ++(0,1) 
            -- ++(-3.125cm-6pt,0);
            
        \end{scope}
    
        \begin{scope}[shift={(0,-6)}]
            
            \node at (3,3.5) {Acceptable: ``EDRB'' Style};
            
            \draw[ultra thick,blue] (0,3) -- (6,2);
            
            \path [EDRB, line width=1pt] (0,3) -- ++(0,-1) -- ++(6,0) -- ++(0,1) 
            -- ++(-3.125cm-6pt,0);
            
        \end{scope}
    \end{tikzpicture}

    
\end{document}

Output: enter image description here

The portion of the blue that extends outside of the hatching at the top left is something that I can live with.

You must log in to answer this question.

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