[Tex/LaTex] TikZ decoration to draw a winding or twisting ribbon

decorationstikz-pgftikz-styles

I enjoy TikZ styles and decorations a lot, but I still dabble with them. I know how to draw ribbons with a style, if they aren't meant to twist. But I'd like to define a style that has a twist of the ribbon along the way, and the ribbon should have different colours on the front and on the back.
The ribbon should be able to bend along the curved path. Is this possible? Maybe by defining a decoration? Or maybe it is possible to insert Bézier curves along the path?

enter image description here

\documentclass{article}
\usepackage{tikz}
\begin{document}
    \tikzset{
        ribbon/.style={
            preaction={
                preaction={
                    draw,
                    line width=0.25cm,
                    white
                },
                draw,
                line width=0.2cm,
                black!30!#1
            },
            line width=0.15cm,
            #1
        },
        ribbon/.default=gray
    }
    Ribbons are easy, just with my previously defined style:

    \begin{tikzpicture}
        \draw[ribbon=green] (0,0) to[out=0,in=90] (2,0) to[out=270,in=270] (4,2);
        \draw[ribbon]       (0,1) to[out=30,in=150] (2,1) to[out=-30,in=120] (4,-1);
    \end{tikzpicture}

    How can I make a twisted ribbon a style?

    \newcommand{\myangle}{20}
    \begin{tikzpicture}[looseness=0.5]
        \fill[green] (0,-0.1) to[out=  0,in=180+\myangle] (2,0) to[out=180-\myangle,in=  0] (0,0.1);
        \fill[red]   (4,-0.1) to[out=180,in=   -\myangle] (2,0) to[out=    \myangle,in=180] (4,0.1);
        \draw        (0,-0.1) to[out=  0,in=180+\myangle] (2,0) to[out=    \myangle,in=180] (4,0.1);
        \node[inner sep=1.5pt,fill,white] at (2,0) {};
        \draw        (4,-0.1) to[out=180,in=   -\myangle] (2,0) to[out=180-\myangle,in=  0] (0,0.1);
    \end{tikzpicture}

    Ideally, I'd like to use this like so:
    \begin{verbatim}
        \draw[twistedribbon={red,green}] (0,0) -- (4,0);
    \end{verbatim}

\end{document}

Best Answer

Let us fix this meta-path.

\documentclass[border=9,tikz]{standalone}
\usetikzlibrary{decorations.pathmorphing}
\begin{document}



\tikzset{
    demo decoration/.style={
        gray,
        postaction={draw=red,decorate,decoration=#1}
    }
}
\begin{tikzpicture}[remember picture]
    \path(0,0)coordinate(A){}(3.7,0)coordinate(B){}(3.8,0)coordinate(C){};
    \draw[demo decoration=snake](A)to[bend left](B);
\end{tikzpicture}

First I created a decoration that draws a parallel line on the left of the meta-path.

\pgfdeclaredecoration{stay left}{initial}{
  \state{initial}[width=0,next state=stay above]
  {\pgfpathmoveto{\pgfqpoint{0pt}{\pgfdecorationsegmentamplitude}}}
  \state{stay above}[width=\pgfdecorationsegmentlength,next state=stay above]
  {\pgfpathlineto{\pgfqpoint{0pt}{\pgfdecorationsegmentamplitude}}}
  \state{final}
  {
    \pgfpathlineto{\pgfqpoint{0pt}{\pgfdecorationsegmentamplitude}}
    \pgfpathlineto{\pgfqpoint{\pgfdecoratedremainingdistance}{\pgfdecorationsegmentamplitude}}
    \pgfpathmoveto{\pgfpointdecoratedpathlast}
  }
}
\begin{tikzpicture}
    \draw[demo decoration=stay left](A)to[bend left](B);
\end{tikzpicture}

Next I created another one that cross from left to right.

\pgfdeclaredecoration{cross from left to right}{initial}{
  \state{initial}[width=0,next state=stay above]
  {\pgfpathmoveto{\pgfqpoint{0pt}{\pgfdecorationsegmentamplitude}}}
  \state{stay above}[width=.5\pgfdecorationsegmentlength,next state=stay above,
                     switch if less than=.5*\pgfdecoratedpathlength+\pgfdecorationsegmentlength to snake down 1]
  {\pgfpathlineto{\pgfqpoint{0pt}{\pgfdecorationsegmentamplitude}}}
  \state{snake down 1}[width=.5\pgfdecorationsegmentlength,next state=snake down 2]
  {\pgfpathlineto{\pgfqpoint{0pt}{\pgfdecorationsegmentamplitude}}}
  \state{snake down 2}[width=.5\pgfdecorationsegmentlength,next state=snake down 3]
  {\pgfpathlineto{\pgfqpoint{0pt}{.7\pgfdecorationsegmentamplitude}}}
  \state{snake down 3}[width=.5\pgfdecorationsegmentlength,next state=snake down 4]
  {\pgfpathlineto{\pgfqpoint{0pt}{0pt}}}
  \state{snake down 4}[width=.5\pgfdecorationsegmentlength,next state=stay below]
  {\pgfpathlineto{\pgfqpoint{0pt}{-.7\pgfdecorationsegmentamplitude}}}
  \state{stay below}[width=.5\pgfdecorationsegmentlength,next state=stay below]
  {\pgfpathlineto{\pgfqpoint{0pt}{-\pgfdecorationsegmentamplitude}}}
  \state{final}
  {
    \pgfpathlineto{\pgfqpoint{0pt}{-\pgfdecorationsegmentamplitude}}
    \pgfpathlineto{\pgfqpoint{\pgfdecoratedremainingdistance}{-\pgfdecorationsegmentamplitude}}
    \pgfpathmoveto{\pgfpointdecoratedpathlast}
  }
}
\begin{tikzpicture}
    \draw[demo decoration=cross from left to right](A)to[bend left](B);
\end{tikzpicture}

So if we draw such lines multiple times with different, we get a ribbon.

\tikzset{
    monochromatic ribbon/.style={
        repeat decoration CFLTR/.list={-5,-4,-3,-2,-1,0,1,2,3,4,5}
    },
    repeat decoration CFLTR/.style={
        preaction={draw,decorate,decoration={cross from left to right,amplitude=#1*.5\pgflinewidth}}
    }
}
\begin{tikzpicture}
    \draw[monochromatic ribbon](A)to[bend left](B);
\end{tikzpicture}

To color the front and back different, we need two half ribbons meeting each other.

\pgfdeclaredecoration{cross from left to middle}{initial}{
  \state{initial}[width=0,next state=stay above]
  {\pgfpathmoveto{\pgfqpoint{0pt}{\pgfdecorationsegmentamplitude}}}
  \state{stay above}[width=.5\pgfdecorationsegmentlength,
                     switch if less than=.5*\pgfdecoratedpathlength+\pgfdecorationsegmentlength to snake down 1]
  {\pgfpathlineto{\pgfqpoint{0pt}{\pgfdecorationsegmentamplitude}}}
  \state{snake down 1}[width=.5\pgfdecorationsegmentlength,next state=snake down 2]
  {\pgfpathlineto{\pgfqpoint{0pt}{\pgfdecorationsegmentamplitude}}}
  \state{snake down 2}[width=.5\pgfdecorationsegmentlength,next state=snake down 3]
  {\pgfpathlineto{\pgfqpoint{0pt}{.6\pgfdecorationsegmentamplitude}}}
  \state{snake down 3}[width=.5\pgfdecorationsegmentlength,next state=final]
  {\pgfpathlineto{\pgfqpoint{0pt}{0pt}}}
  \state{final}
  {
    \pgfpathmoveto{\pgfpointdecoratedpathlast}
  }
}
\pgfdeclaredecoration{cross from middle to right}{initial}{
  \state{initial}[width=0,next state=stay above]
  {}
  \state{stay above}[width=.5\pgfdecorationsegmentlength,
                     switch if less than=.5*\pgfdecoratedpathlength+\pgfdecorationsegmentlength to snake down 1]
  {}
  \state{snake down 1}[width=.5\pgfdecorationsegmentlength,next state=snake down 2]
  {}
  \state{snake down 2}[width=.5\pgfdecorationsegmentlength,next state=snake down 3]
  {}
  \state{snake down 3}[width=.5\pgfdecorationsegmentlength,next state=snake down 4]
  {\pgfpathmoveto{\pgfqpoint{0pt}{0pt}}}
  \state{snake down 4}[width=.5\pgfdecorationsegmentlength,next state=stay below]
  {\pgfpathlineto{\pgfqpoint{0pt}{-.7\pgfdecorationsegmentamplitude}}}
  \state{stay below}[width=.5\pgfdecorationsegmentlength,next state=stay below]
  {\pgfpathlineto{\pgfqpoint{0pt}{-\pgfdecorationsegmentamplitude}}}
  \state{final}
  {
    \pgfpathlineto{\pgfqpoint{0pt}{-\pgfdecorationsegmentamplitude}}
    \pgfpathlineto{\pgfqpoint{\pgfdecoratedremainingdistance}{-\pgfdecorationsegmentamplitude}}
    \pgfpathmoveto{\pgfpointdecoratedpathlast}
  }
}
    \begin{tikzpicture}
        \draw[demo decoration=cross from left to middle](A)to[bend left](B);
        \draw[brown,decorate,decoration=cross from middle to right](A)to[bend left](B);
    \end{tikzpicture}

Repeating it with different color, we are done.

\tikzset{
    bichromatic ribbon/.style={
        preaction={draw,decorate,decoration={cross from left to right,amplitude=-6*.5\pgflinewidth}},
        repeat decoration CFLTM/.list={-5,-4,-3,-2,-1,0,1,2,3,4,5},
        repeat decoration CFMTR/.list={-5,-4,-3,-2,-1,0,1,2,3,4,5},
        preaction={draw,decorate,decoration={cross from left to right,amplitude=6*.5\pgflinewidth}},
    },
    repeat decoration CFLTM/.style={
        preaction={draw=blue!50,decorate,decoration={cross from left to middle,amplitude=#1*.5\pgflinewidth}}
    },
    repeat decoration CFMTR/.style={
        preaction={draw=teal!50,decorate,decoration={cross from middle to right,amplitude=#1*.5\pgflinewidth}}
    }
}
    \begin{tikzpicture}
        \path[bichromatic ribbon](A)to[bend left](B);
    \end{tikzpicture}



\end{document}