6

I began creating some shapes in TikZ and it worked quite well. Filling the shape using the even odd rule left out some space in the middle since TikZ does not recognize it as part of the shape since the lines are inverted at the crossing points. How could I possible fill these spaces?

See this MWE:

\usepackage{tikz}

\begin{document}
\begin{tikzpicture}

\draw[fill=cyan,domain=-pi:pi,smooth,samples=200,even odd rule]
plot ({sin(deg(\x))+0.6*sin(deg(-3*\x))},  {cos(deg(\x))+0.6*cos(deg(-3*\x))})
plot ({1.2*(sin(deg(\x))+0.9*sin(deg(-3*\x)))}, {1.2*(cos(deg(\x))+0.9*cos(deg(-3*\x)))});

\end{tikzpicture}
\end{document}

In the middle you can see several white spaces. The one in the center is fine, but I hope there is a way to fill the other four since they are part of the shape.

Shape with white gaps

Is it also possible to create a 3D look by folding the stripes under each other? Similar to a Celtic knot or the Penrose triangle.

Thanks for your help!

0
4

another solution ...

\documentclass{article}
\usepackage{pgfplots}
\usepgfplotslibrary{fillbetween}
\begin{document}
\begin{tikzpicture}
\foreach \y in {1,...,8} {
\draw[name path=A, thick, domain=-1+45*\y:45*(1+\y),smooth,samples=200]
        plot ({sin(\x)+0.6*sin(-3*\x)},  {cos(\x)+0.6*cos(-3*\x)});
\draw[name path=B, thick, domain=-1+45*\y:45*(1+\y),smooth,samples=200]
        plot ({1.2*(sin(\x)+0.9*sin(-3*\x))}, {1.2*(cos(\x)+0.9*cos(-3*\x))});
\tikzfillbetween[of=A and B] {cyan}
}
\end{tikzpicture}
\end{document}

enter image description here

1
  • I need to learn about the fillbetween library. This is such simple code! – Andrew Stacey Nov 12 '20 at 19:19
8

While you are waiting for the TikZ-team, here is a small effort in Metapost, purely for amusement, comparison, or instruction...

enter image description here

\documentclass[border=5mm]{standalone}
\usepackage{luamplib}
\begin{document}
\mplibtextextlabel{enable}
\begin{mplibcode}
input colorbrewer-rgb
beginfig(1);
    vardef f(expr t, p, q) = 
        (sind(t) + p * sind(q * t), cosd(t) + p * cosd(q * t))
    enddef;

    picture P[];
    for n=4, 5, 6:

        path ff, gg, xx;
        ff = (f(0, 1/2, 1-n) for t=1 upto 360/n: .. f(t, 1/2, 1-n) endfor) scaled 42;
        gg = (f(0, 3/4, 1-n) for t=1 upto 360/n: .. f(t, 3/4, 1-n) endfor) scaled 42;
        xx = ff -- reverse gg -- cycle;

        interim linecap := butt;
        P[n] = image(
            for k=true, false:
                for i=0 upto n-1:
                    if odd i = k:
                        fill xx rotated (360 / n * i) withcolor Spectral[8][n];
                        draw ff rotated (360 / n * i);
                        draw gg rotated (360 / n * i);
                    fi
                endfor
            endfor
        );
        draw P[n] shifted (150n, 0);
    endfor
endfig;
\end{mplibcode}
\end{document}

This is wrapped up in luamplib so you will need to compile it with lualatex. You will also need a reasonably recent TeX distribution that includes the metapost-colorbrewer package.

Notes

  • in case it's not obvious, I have cheated a bit by drawing the shape in pieces to get the effect of overlapping.
7

This also decomposes the path into smaller stretches.

\documentclass[tikz,border=3mm]{standalone}

\begin{document}
\begin{tikzpicture}%[trig format=rad]
\foreach \X in {0,1,2,3}
{\draw[cyan,fill=cyan,smooth,samples=51] 
plot[domain=-pi+\X*pi/2:-pi/2+\X*pi/2] ({sin(deg(\x))+0.6*sin(deg(-3*\x))},  {cos(deg(\x))+0.6*cos(deg(-3*\x))})
--
plot[domain=-pi/2+\X*pi/2:-pi+\X*pi/2] ({1.2*(sin(deg(\x))+0.9*sin(deg(-3*\x)))}, {1.2*(cos(deg(\x))+0.9*cos(deg(-3*\x)))})
;
\draw[smooth,samples=51] 
plot[domain=-pi+\X*pi/2:-pi/2+\X*pi/2] ({sin(deg(\x))+0.6*sin(deg(-3*\x))},  {cos(deg(\x))+0.6*cos(deg(-3*\x))})
plot[domain=-pi/2+\X*pi/2:-pi+\X*pi/2] ({1.2*(sin(deg(\x))+0.9*sin(deg(-3*\x)))}, {1.2*(cos(deg(\x))+0.9*cos(deg(-3*\x)))})
;}

\end{tikzpicture}
\end{document}

enter image description here

An even simpler option is to draw a double line.

\documentclass[tikz,border=3mm]{standalone}
\begin{document}
\begin{tikzpicture}
\draw[double=cyan,double distance=4mm,domain=-pi:pi,smooth cycle,samples=201]
plot ({1.1*sin(deg(\x))+0.8*sin(deg(-3*\x))},  {1.1*cos(deg(\x))+0.8*cos(deg(-3*\x))});
\end{tikzpicture}
\end{document}

enter image description here

1
  • +1 Welcom your comeback. :-) – user213378 Nov 11 '20 at 17:53
6
\documentclass[tikz, border=1cm]{standalone}
\begin{document}
\begin{tikzpicture}
\newcommand{\bgd}{(-2.5,-2.5) rectangle (2.5,2.5)}
\newcommand{\largeKnot}{{1.2*(sin(deg(\x))+0.9*sin(deg(-3*\x)))}, {1.2*(cos(deg(\x))+0.9*cos(deg(-3*\x)))}}
\newcommand{\outsetLargeKnot}{{1.2*(sin(deg(\x))+1.05*0.9*sin(deg(-3*\x)))}, {1.2*(cos(deg(\x))+1.05*0.9*cos(deg(-3*\x)))}}
\newcommand{\smallKnot}{{sin(deg(\x))+0.6*sin(deg(-3*\x))},  {cos(deg(\x))+0.6*cos(deg(-3*\x))}}
\newcommand{\insetSmallKnot}{{sin(deg(\x))+0.9*0.6*sin(deg(-3*\x))},  {cos(deg(\x))+0.9*0.6*cos(deg(-3*\x))}}

\shade \bgd;
\draw[fill=cyan, smooth, samples=200]
plot[domain=-4/4*pi:-3/4*pi](\largeKnot) -- plot[domain=-3/4*pi:-4/4*pi](\smallKnot)
plot[domain=-2/4*pi:-1/4*pi](\largeKnot) -- plot[domain=-1/4*pi:-2/4*pi](\smallKnot)
plot[domain=-0/4*pi: 1/4*pi](\largeKnot) -- plot[domain= 1/4*pi: 0/4*pi](\smallKnot)
plot[domain= 2/4*pi: 3/4*pi](\largeKnot) -- plot[domain= 3/4*pi: 2/4*pi](\smallKnot);
\begin{scope}
\clip[smooth, samples=200]
plot[domain=-4/4*pi+0.2:-3/4*pi-0.02](\largeKnot) -- plot[domain=-3/4*pi-0.02:-4/4*pi+0.2](\smallKnot) -- cycle
plot[domain=-2/4*pi+0.2:-1/4*pi-0.02](\largeKnot) -- plot[domain=-1/4*pi-0.02:-2/4*pi+0.2](\smallKnot) -- cycle
plot[domain=-0/4*pi+0.2: 1/4*pi-0.02](\largeKnot) -- plot[domain= 1/4*pi-0.02: 0/4*pi+0.2](\smallKnot) -- cycle
plot[domain= 2/4*pi+0.2: 3/4*pi-0.02](\largeKnot) -- plot[domain= 3/4*pi-0.02: 2/4*pi+0.2](\smallKnot) -- cycle
\bgd;
\draw[fill=cyan, smooth, samples=200, domain=-pi:pi, even odd rule] plot(\largeKnot) plot(\smallKnot);
\end{scope}

\begin{scope}[xshift=6cm]
\shade \bgd;
\draw[fill=cyan, smooth, samples=200]
plot[domain=-4/4*pi:-3/4*pi](\largeKnot) -- plot[domain=-3/4*pi:-4/4*pi](\smallKnot)
plot[domain=-2/4*pi:-1/4*pi](\largeKnot) -- plot[domain=-1/4*pi:-2/4*pi](\smallKnot)
plot[domain=-0/4*pi: 1/4*pi](\largeKnot) -- plot[domain= 1/4*pi: 0/4*pi](\smallKnot)
plot[domain= 2/4*pi: 3/4*pi](\largeKnot) -- plot[domain= 3/4*pi: 2/4*pi](\smallKnot);
\begin{scope}
\clip[smooth, samples=200]
plot[domain=-4/4*pi+0.2:-3/4*pi-0.02](\outsetLargeKnot) -- plot[domain=-3/4*pi-0.02:-4/4*pi+0.2](\insetSmallKnot) -- cycle
plot[domain=-2/4*pi+0.2:-1/4*pi-0.02](\outsetLargeKnot) -- plot[domain=-1/4*pi-0.02:-2/4*pi+0.2](\insetSmallKnot) -- cycle
plot[domain=-0/4*pi+0.2: 1/4*pi-0.02](\outsetLargeKnot) -- plot[domain= 1/4*pi-0.02: 0/4*pi+0.2](\insetSmallKnot) -- cycle
plot[domain= 2/4*pi+0.2: 3/4*pi-0.02](\outsetLargeKnot) -- plot[domain= 3/4*pi-0.02: 2/4*pi+0.2](\insetSmallKnot) -- cycle
\bgd;
\draw[fill=cyan, smooth, samples=200, domain=-pi:pi, even odd rule] plot(\largeKnot) plot(\smallKnot);
\end{scope}
\end{scope}

\end{tikzpicture}
\end{document}

enter image description here

2

This is very similar to what the knots, and celtic, TikZ libraries wer designed to do, except they wer designed to work on paths rather than filled regions. Nevertheless, we can take the ideas of those packages and adapt them to this. It's not pretty code because it uses a few low-level commands, but that could be hidden in a package.

We split the drawing into four pieces and draw them one after the other, clipping each piece against the previous one to create the cut-out effect. The clipping only needs to be done when drawing the paths, not filling.

\documentclass{article}
%\url{https://tex.stackexchange.com/q/570414/86}

\usepackage{tikz}

% spath3 package from https://tex.stackexchange.com/q/32125/86
\usepackage{spath3}

% The intention is that the commands of the spath3 package are used by
% other packages which is why much of the structure doesn't have a
% public interface.  This means that we have to use \ExplSyntaxOn
% ... \ExplSyntaxOff.


% reverse clip from https://tex.stackexchange.com/q/12010/86
\tikzset{
  reverseclip/.style={
    clip even odd rule,
    insert path={(current page.north east) --
      (current page.south east) --
      (current page.south west) --
      (current page.north west) --
      (current page.north east)}
  },
  clip even odd rule/.code={%
    \pgfseteorule
  }
}

\begin{document}
\begin{tikzpicture}[remember picture] % needed for the reverse clip

% define and store the various paths
% "outer" part
\path[save spath=outer,domain=0:pi/2,smooth,samples=200]
plot ({sin(deg(\x))+0.6*sin(deg(-3*\x))},  {cos(deg(\x))+0.6*cos(deg(-3*\x))});
% "inner" part
\path[save spath=inner,domain=0:pi/2,smooth,samples=200]
plot ({1.2*(sin(deg(\x))+0.9*sin(deg(-3*\x)))}, {1.2*(cos(deg(\x))+0.9*cos(deg(-3*\x)))});
% joining line between the parts, for filling
\path[save spath=linejoin] (0,2.28) -- (0,1.6);
% joining the parts with a move, for drawing
\path[save spath=movejoin] (0,2.28) (0,1.6);


\ExplSyntaxOn
% We now construct the paths from their constituent parts.

% Reverse the inner path
\spath_reverse:n {inner}
% Clone it into two new paths, one will be for drawing the other for filling
\spath_gclone:nn {inner} {outline}
\spath_gclone:nn {inner} {fillable}

% The filled path consists of the inner, a line out, then the outer, and it is closed
\spath_append_no_move:nn {fillable} {linejoin}
\spath_append_no_move:nn {fillable} {outer}
\spath_close_path:n {fillable}

% The drawn path is similar except with a move in place of the joining line
\spath_append_no_move:nn {outline} {movejoin}
\spath_append_no_move:nn {outline} {outer}

% Clone the fillable path
\spath_gclone:nn {fillable} {fillableA}
\spath_gclone:nn {fillable} {fillableB}
\spath_gclone:nn {fillable} {fillableC}
\spath_gclone:nn {fillable} {fillableD}

% Rotate the clones
\spath_transform:nnnnnnn {fillableB} {0}{1}{-1}{0}{0}{0}
\spath_transform:nnnnnnn {fillableC} {-1}{0}{0}{-1}{0}{0}
\spath_transform:nnnnnnn {fillableD} {0}{-1}{1}{0}{0}{0}

% Clone the drawable path
\spath_gclone:nn {outline} {outlineA}
\spath_gclone:nn {outline} {outlineB}
\spath_gclone:nn {outline} {outlineC}
\spath_gclone:nn {outline} {outlineD}

% Rotate the clones
\spath_transform:nnnnnnn {outlineB} {0}{1}{-1}{0}{0}{0}
\spath_transform:nnnnnnn {outlineC} {-1}{0}{0}{-1}{0}{0}
\spath_transform:nnnnnnn {outlineD} {0}{-1}{1}{0}{0}{0}

\ExplSyntaxOff

% Fill the fillable paths, drawing them as well to ensure that there aren't gaps
\filldraw[cyan] (2.28,0) [insert spath=fillableA];
\filldraw[cyan] (0,-2.28) [insert spath=fillableB];
\filldraw[cyan] (-2.28,0) [insert spath=fillableC];
\filldraw[cyan] (0,2.28) [insert spath=fillableD];

% Draw the drawing paths, each one clipped against the previous one to create the overlay illusion
\begin{scope}
\clip[overlay, reverseclip] (2.28,0) [insert spath=fillableA];
\draw[ultra thick, line cap=rect] (0,2.28) [insert spath=outlineD];
\end{scope}

\begin{scope}
\clip[overlay, reverseclip] (0,2.28) [insert spath=fillableD];
\draw[ultra thick, line cap=rect] (-2.28,0) [insert spath=outlineC];
\end{scope}

\begin{scope}
\clip[overlay, reverseclip] (-2.28,0) [insert spath=fillableC];
\draw[ultra thick, line cap=rect] (0,-2.28) [insert spath=outlineB];
\end{scope}

\begin{scope}
\clip[overlay, reverseclip] (0,-2.28) [insert spath=fillableB];
\draw[ultra thick, line cap=rect] (2.28,0) [insert spath=outlineA];
\end{scope}

\end{tikzpicture}
\end{document}

Flourished knot

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.