18

A friend showed a quilt pattern on which they were working. Ah, I said, I could easily do that in TikZ with pics. Well, yes and no: Do it, certainly; easily, not so much. I have the pic shown below, as well as the finished pattern which was done completely by brute force. There must be a way to do this more efficiently, and in a way that could be generalized for other patters.

\documentclass{article}

\usepackage[papersize={5.5in,8.5in},margin=0.6in]{geometry}
\usepackage{tikz}

\usetikzlibrary{shapes.geometric}

\tikzset{%
    dartstyle/.style={kite,draw,kite vertex angles=60,inner sep=0.125in,outer sep=0pt,fill=#1},
    pics/quiltdart/.style={%
        code={%
          \node[dartstyle=red](base) {};
          \node[rotate=0,dartstyle=white,anchor=upper vertex] at (base.right vertex) {};
          \node[rotate=0,dartstyle=white,anchor=upper vertex] at (base.left vertex) {};
          \node[rotate=0,dartstyle=red,anchor=upper vertex] at (base.lower vertex) {};
        }%
    }%
}

\parindent0pt

\begin{document}

This was easy:

\tikz \pic {quiltdart};

\bigskip

This, not so much\dots

\tikz {\pic {quiltdart};
    \pic[yshift=-0.51in,xshift=0.89in,rotate=-60,transform shape]  {quiltdart};
    \pic[yshift=-1.54in,xshift=0.89in,rotate=-120,transform shape]  {quiltdart};
    \pic[yshift=-2.05in,xshift=-0in,rotate=-180,transform shape]  {quiltdart};
    \pic[yshift=-1.54in,xshift=-0.89in,rotate=-240,transform shape]  {quiltdart};
    \pic[yshift=-0.51in,xshift=-0.89in,rotate=-300,transform shape]  {quiltdart};
}

\end{document}

quilt image

Update

First, I want to thank those who have replied to my post: I have learned an enormous amount. For that reason, I am posting this as an Update rather than an Answer because it would not have been possible without the thought-provoking replies from which I do not in any way wish to detract.

Here is my approach. It works nicely though it is probably not the most efficient code possible. On the other hand it is straightforward and easily maintained/tinkered-with.

\documentclass[]{article}

\usepackage[rgb]{xcolor}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usepackage[margin=1.4in,top=0.5in,left=0.75in]{geometry}

\NewDocumentCommand{\makeshape}{m}{%
    \pgfmathsetmacro{\myhuei}{rnd}
    \pgfmathsetmacro{\myhueii}{rnd}
    \pgfmathsetmacro{\myhueiii}{rnd}
    \definecolor{mycolori}{hsb}{\myhuei,1,1}
    \definecolor{mycolorii}{hsb}{\myhueii,0.25,1}
    \definecolor{mycoloriii}{hsb}{\myhueiii,1,1}
    \pgfmathsetmacro{\lang}{180/#1}%
    \pgfmathsetmacro{\bang}{90-\lang}%
    \tikzset{%
        pics/dart/.style={code={%
            \draw[fill=mycolorii] (0,0) -- ++(90-\lang:2) --
                ++(180-\bang:2) -- ++(-90-\lang:2) -- cycle;
            \draw[fill=mycolori](0,0) -- ++(90-\lang:1) -- ++(180 - \bang:1)coordinate(T) --
                ++(-90-\lang:1) -- cycle;
            \draw[fill=mycoloriii](T) -- ++(90-\lang:1) -- ++(-180 - \bang:1) --
                ++(-90-\lang:1)-- cycle;
            }%
        },%
    }%
    \begin{tikzpicture}[rotate=\lang,scale=0.5]%% both rotate and scale can be altered to suit
        \foreach \i [count=\ii from 0] in {1,...,#1}
            \path pic[rotate around={360/#1*\ii:(0,0)},transform shape]{dart};
    \end{tikzpicture}
}

\parindent0pt

\begin{document}

\thispagestyle{empty}

\foreach \N in {4,5,...,18}{\makeshape{\N}}

\end{document}

samples new update

6
  • Note that there is an easier way of using pic without nodes, in this case
    – Black Mild
    Sep 2, 2021 at 22:05
  • 3
    A herd of elephants, a pack of wolves, a swarm of bees, a shoal of fish and a pic of nodes. Sep 2, 2021 at 22:22
  • Ah. An example of venereal TikZ!!
    – sgmoye
    Sep 2, 2021 at 22:58
  • 1
    There was a small piece of superfluous code in my Update. That has been fixed.
    – sgmoye
    Sep 5, 2021 at 17:04
  • @sgmoye What is the meaning of the third color (the between one)?
    – Black Mild
    Sep 6, 2021 at 8:10

6 Answers 6

12

Here is a somewhat improvement by using a \foreach statement and replacing the rotate option by rotate around.

\documentclass{article}

\usepackage[papersize={5.5in,8.5in},margin=0.6in]{geometry}
\usepackage{tikz}

\usetikzlibrary{shapes.geometric}

\tikzset{%
    dartstyle/.style={kite,draw,kite vertex angles=60,inner sep=0.125in,outer sep=0pt,fill=#1},
    pics/quiltdart/.style={%
        code={%
          \node[dartstyle=red](base) {};
          \node[rotate=0,dartstyle=white,anchor=upper vertex] at (base.right vertex) {};
          \node[rotate=0,dartstyle=white,anchor=upper vertex] at (base.left vertex) {};
          \node[rotate=0,dartstyle=red,anchor=upper vertex] 
                at (base.lower vertex)(bottom dart){}; % <-----
        }%
    }%
}

\parindent0pt

\begin{document}

This was easy:

\tikz \pic {quiltdart};

\bigskip

What about this ???


\begin{tikzpicture}
 \foreach \i in {0,60,...,300}{
  \pic[rotate around={\i:(bottom dart.lower vertex)},
      transform shape] {quiltdart};
  }

\end{tikzpicture}

\end{document}

An other solution by modifying the pic code so that the origin of the pic corresponds to the lowest vertex:

\documentclass{article}

\usepackage[papersize={5.5in,8.5in},margin=0.6in]{geometry}
\usepackage{tikz}

\usetikzlibrary{shapes.geometric}

\tikzset{%
    dartstyle/.style={kite,draw,kite vertex angles=60,inner sep=0.125in,outer sep=0pt,fill=#1},
    pics/quiltdart/.style={%
        code={%
          \node[dartstyle=red,anchor=lower vertex](base) {};
          \node[dartstyle=white,anchor=left vertex] at (base.upper vertex) {};
          \node[dartstyle=white,anchor=right vertex] at (base.upper vertex) {};
          \node[dartstyle=red,anchor=lower vertex] at (base.upper vertex){};
        }%
    }%
}

\parindent0pt

\begin{document}

%This was easy:

%\tikz \pic {quiltdart};

%\bigskip

%This, not so much\dots


\begin{tikzpicture}

% first copy
\foreach \i in {0,60,...,300}{
\pic[rotate =\i,transform shape] {quiltdart};
}
% second copy
\foreach \i in {0,60,...,300}{
\pic[rotate =\i,transform shape] at(0,8){quiltdart};
}
\end{tikzpicture}
\end{document}
3
  • Very interesting. I've not used rotate around before and it seems very handy. I take it that using rotate around in the options to the \pic allows reference to the named dart, also a revelation. I've learned something!
    – sgmoye
    Sep 3, 2021 at 8:08
  • Made an interesting and unexpected discovery. When I deleted the first instance of \tikz\pic{quiltdart} (following 'This was easy;') and ran your code, I received an error: No shape named bottom dart' is known.` Changing your code to \pic{quiltdart} \foreach \i in {60,120,...,300)... fixed the problem. I can only imagine that using the command\tikz as opposed to \begin{tikzpicture}...\end{tikzpicture}, thus avoiding grouping, allowed the second use of \pic{quiltdart} to succeed. Interesting. So I was wrong about my original thought about the named dart.
    – sgmoye
    Sep 3, 2021 at 9:51
  • @sgmoye answer updated! the command \tikz and the environment tikzpicture are essentially the same. The problem was that the coordinate "named bottom dart" is only created after the use of the pic in which it is defined. I update my answer with little changes to the pic code so that the origin corresponds to position of the lowest vertex. Sep 3, 2021 at 11:46
15

With just a path, without pics or nodes

\documentclass{article}

\usepackage[papersize={5.5in,8.5in},margin=0.6in]{geometry}
\usepackage{tikz}

\newcommand{\mydart}[1][]{
    \filldraw[black, fill=red, even odd rule,#1] (0,0)--++(60:1)--++(120:1)--++(240:1)--cycle (60:.5)--++(60:.5)--++(120:.5)--++(240:1)--++(120:.5)--++(60:.5)--cycle
}

\begin{document}

This was easy:

\tikz \mydart;

\bigskip

What about this ???

\begin{tikzpicture}
 \foreach \i in {0,60,...,300}{
  \mydart[rotate around={\i:(0,0)}];
  }

\end{tikzpicture}


or this ???

\begin{tikzpicture}
 \foreach \i in {0,60,...,300}{
  \mydart[shift={(\i:1)}, rotate=\i];
  }

\end{tikzpicture}

\end{document}

enter image description here

13

This is another approach, changing the pic. In my pic I draw four squares, but I changed the axes to 'deform' them.

Like this:

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

\definecolor{color0}{HTML}{FFFFFF} % white
\definecolor{color1}{HTML}{FF0000} % red

\tikzset
{%
  pics/quiltdart/.style={
    code={%
      \pgfmathsetmacro\y{sin(60)}
      \begin{scope}[x={(0.5 cm,\y cm)}, y={(-0.5 cm,\y cm)}]
        \foreach\i in {0,1} \foreach\j in {0,1}
        {
          \pgfmathtruncatemacro\c{Mod(\i+\j+1,2)}
          \draw[fill=color\c] (\i,\j) -- (\i,\j+1) -- (\i+1,\j+1) -- (\i+1,\j) -- cycle;
        } 
      \end{scope}
    }},
}

\begin{document}
\begin{tikzpicture}[line join=round]
\foreach\a in {0,60,...,300}
  \pic[rotate=\a] {quiltdart};
\end{tikzpicture}
\end{document}

enter image description here

1
  • I'll need to meditate on this for a while...
    – sgmoye
    Sep 3, 2021 at 8:08
9

Compile with Asymptote or at http://asymptote.ualberta.ca/

size(5cm);

picture pic;
unitsize(pic,1cm);

path g=(0,0)--0.5*dir(60)--0.5*dir(0)--0.5*dir(-60)--cycle;
draw(pic,g^^shift(0.5*dir(0))*g);
filldraw(pic,shift(0.5*dir(60))*g^^shift(0.5*dir(-60))*g,red,black);

for (int i=0;i<6;++i)
  add(rotate(i*60,dir(-60))*pic);

If you can any issues with my code, please comment.

enter image description here

9

New solution

enter image description here

\documentclass[border=5mm]{standalone}
\usepackage[rgb]{xcolor}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\tikzset{quiltdart/.pic={%
\def\n{#1} % the number of quiltdart
% random colors from OP
\pgfmathsetmacro{\myhuei}{rnd}
\pgfmathsetmacro{\myhueii}{rnd}
\pgfmathsetmacro{\myhueiii}{rnd}
\definecolor{mycolori}{hsb}{\myhuei,1,1}
\definecolor{mycolorii}{hsb}{\myhueii,0.25,1}
\definecolor{mycoloriii}{hsb}{\myhueiii,1,1}
        
\def\basicdart{(0,0)--(90-360/\n:1)--+(0,1)--(0,1)--cycle}          
\foreach \i in {0,...,\n}{
\draw[fill=mycoloriii,rotate=\i*360/\n,shift={(90-360/\n:1)},shift={(0,1)}] \basicdart;
\draw[fill=mycolori,rotate=\i*360/\n] \basicdart;
\draw[fill=mycolorii,rotate=\i*360/\n,shift={(0,1)}] \basicdart;
\draw[fill=mycolorii,rotate=\i*360/\n,shift={(0,1)},xscale=-1] \basicdart;
}
}}

\newcounter{myquilt}
\setcounter{myquilt}{2} 
\foreach \j in {1,...,5}
\foreach \i in {1,...,8}
{\stepcounter{myquilt}
\path (6*\i,-6*\j) pic[scale=.6]{quiltdart=\themyquilt} 
% +(-90:2.6) node{$n=$ \themyquilt}   % show the number of quiltdart
;
}
\end{tikzpicture}
\end{document} 

Old solutions I guess the essential issue is centering/not centering pic, neither pic of nodes nor rotate. This turns to quite interesting situations:

  • pic is/is not centered at the origin (name dart)
  • pic of pics (name quiltdart).

I add the default values for parameters of the pics. The option [rotate,shift] is used instead of [rotate around]. I hope the code and the pictures are self-exlpained.

enter image description here

\documentclass[border=5mm,tikz]{standalone}
% Multi-parameters pic with default
\tikzset{
pics/dart/.style args={#1 and #2}{code={%
\draw[fill=#1] (-120:1)--(60:1)--([turn]60:1)--(120:1)--(-60:1)--([turn]-60:1)--cycle;
\draw[fill=#2] (-120:1)--(60:1)--(0:1)--(-60:1)--(120:1)--(180:1)--cycle;
}},
pics/dart/.default={red and white}
}

% Multi-parameters pic of pics with default
\tikzset{pics/quiltdart/.style args={#1 and #2}{code={%
\foreach \i in {0,...,5}
\path (0,0) pic[rotate=60*\i,shift={(0,-sqrt(3))}]{dart={#1 and #2}};
}},
pics/quiltdart/.default={red and white}
}

\begin{document}
\begin{tikzpicture}
\path 
(0,0) pic{dart} node[above=2.5cm,scale=2]{\verb|\pic{dart}|}
(18,0) pic{dart={magenta and cyan}} node[above=2.5cm,scale=2]{\verb|\pic{dart={magenta and cyan}}|};

\foreach \i in {0,...,5}
\path (0,-10) pic[rotate=60*\i,shift={(0,-sqrt(3))}]{dart};
\path (0,-10) node[above=3.5cm,scale=2,align=center]{Good\\
    \verb|\foreach \i in {0,...,5}|\\
    \verb|\pic[rotate=60*\i,shift={(0,-sqrt(3))}]{dart}|};
\path 
(18,-10) pic{quiltdart={magenta and cyan}} node[above=3.5cm,scale=2,align=center]{Better\\
    \verb|\pic{quiltdart={magenta and cyan}}|};
\end{tikzpicture}
\end{document} 
 

Some examples of quilt decorations

  1. A tiling of the plane

enter image description here

\begin{tikzpicture}
\foreach \i in {0,...,5}
\foreach \j in {0,...,4}
\path (5*\i,-5*\j) pic{quiltdart};
\end{tikzpicture}
  1. A green quilt

enter image description here

\begin{tikzpicture}
\foreach \i in {0,...,5}
\foreach \j in {0,...,4}
\path (8*\i,-8*\j) pic{quiltdart={green and white}};
\end{tikzpicture}

Update Asymptote version pic of pics can be translated from TikZ to Asymptote.

enter image description here

unitsize(1cm);

// creating dart
picture dart;
unitsize(dart,1cm);
path p=dir(-120)--dir(60)--dir(0)--dir(-60)--dir(120)--dir(180)--cycle;
path q=dir(-120)--dir(60)--sqrt(3)*dir(90)--dir(120)--dir(-60)--sqrt(3)*dir(-90)--cycle;
filldraw(dart,p,pink,black);
filldraw(dart,q,purple,black);

// creating quiltdart from darts
picture quiltdart;
unitsize(quiltdart,1cm);
for(int i=0;i<6;++i)
add(quiltdart,rotate(i*60)*shift(0,-sqrt(3))*dart);

// Here is the quilt
for (int i=0;i<6;++i)
for (int j=0;j<3;++j)
add(shift(8*i,-8*j)*quiltdart);

shipout(bbox(1cm,invisible));

Update 2: General case A generalization of n-point-star quiltdarts. Sometimes, the general problem is easier to deal with a particular problem. It is how I feel for the situation of the new update answer of OP.

Idea is simple, straight forward of creating a function with parameter n. There 3 classes; each class is made from rotations of the basic dart; a small care for the middle class.

n=20 quiltdart(20,yellow,magenta,purple);

enter image description here

// http://asymptote.ualberta.ca/
unitsize(1cm);
// the basic dart
path dart(int n){
pair A=(0,0),D=(0,1),B=dir(90-360/n),C=B+D-A;
return A--B--C--D--cycle;
}

// the quiltdart of three classes
void quiltdart(int n, pen innerpen, pen middlepen, pen outerpen){
for(int i=0;i<n;++i){
transform rot=rotate(i*360/n);  
path outerclass=rot*shift((0,1)+dir(90-360/n))*dart(n);
path middleclass1=rot*shift(0,1)*dart(n);
path middleclass2=rot*shift(0,1)*xscale(-1)*dart(n);
path innerclass=rot*dart(n);
filldraw(outerclass,outerpen,black);
filldraw(middleclass1,middlepen,black);
filldraw(middleclass2,middlepen,black);
filldraw(innerclass,innerpen,black);
}} 

quiltdart(20,yellow,magenta,purple);
shipout(bbox(1cm,invisible));

n=4 quiltdart(4,yellow,orange,green);

enter image description here

n=50 quiltdart(50,yellow,green,red);

enter image description here

Update 3: A general solution with TikZ. n=45 is the maximal number that TikZ/TeX can compile without irritating and throwing up "Dimension too large" error.

enter image description here

\documentclass[border=5mm,tikz]{standalone}
\begin{document}
\begin{tikzpicture}
\def\n{45}
\def\basicdart{(0,0)--(90-360/\n:1)--+(0,1)--(0,1)--cycle}          
\foreach \i in {0,...,\n}{
\draw[fill=magenta,rotate=\i*360/\n,shift={(90-360/\n:1)},shift={(0,1)}] \basicdart;
\draw[fill=yellow,rotate=\i*360/\n] \basicdart;
\draw[fill=orange,rotate=\i*360/\n,shift={(0,1)}] \basicdart;
\draw[fill=orange,rotate=\i*360/\n,shift={(0,1)},xscale=-1] \basicdart;
}
\end{tikzpicture}
\end{document} 
10
  • 1
    Wonderful! Yes, it was frustrating that the 'pic of nodes' was not centered at the origin. Drawing the shapes as opposed to using nodes fixes that problem, as you nicely demonstrate.
    – sgmoye
    Sep 4, 2021 at 10:03
  • @sgmoye Yes, I guess the essential issue is centering/not centering pic, neither pic of nodes nor rotate.
    – Black Mild
    Sep 4, 2021 at 10:14
  • I added an Asymptote version for pic of pics
    – Black Mild
    Sep 4, 2021 at 11:00
  • 1
    I'm probably being very stupid. In my original post I suggested the possibility of generalizing this: not all darts are 60° (for a star of 6 points) -- some are 72° (5-point star) or indeed an arbitrary number of points, 360°/n. Clearly, in this case, 60 is not merely 360/6 nor is 120 simply 180-360/6. Could you generalize your TikZ to account for this possibility? So far, I have had no luck :<(
    – sgmoye
    Sep 4, 2021 at 18:53
  • @sgmoye I am not sure I understand exactly what you describe about 5-point star, 6-point star, ... . Are they made from the darts? Could you describe them more clearly? (as an update of your question)
    – Black Mild
    Sep 5, 2021 at 1:38
6

A trivial solution with PSTricks.

\documentclass[pstricks]{standalone}

\newcommand\x[1][red]{%
    \pspolygon[fillcolor=#1]
        (0,0)(1,0)(!1 60 PtoC exch 1 add exch)(1;60)}
        
\def\y{%
    \x\rput(1,0){\x[white]}
    \rput(1;60){\x[white]\rput(1,0){\x}}}
    
\begin{document}
\begin{pspicture}[fillstyle=solid](-4,-4)(4,4)
    \foreach \i in {0,60,...,300}{\rput{\i}(0,0){\y}}
\end{pspicture}
\end{document}

enter image description here

You must log in to answer this question.

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