[Tex/LaTex] How to draw a waving flag in TikZ

tikz-pgf

Let's take the flag of Germany as an example, because the flag is way quite simple and its waving state can be easily drawn "manually". But I am asking about general flag (the flag of any country/group, even the flag of my team).


Normal flag (1)

\documentclass[tikz]{standalone}
\usepackage{xcolor}
\definecolor{gerbla}{RGB}{0,0,0}
\definecolor{gerred}{RGB}{255,0,0}
\definecolor{geryel}{RGB}{255,204,0}
\begin{document}
\begin{tikzpicture}
\fill[gerbla] (0,2) rectangle (5,3);
\fill[gerred] (0,1) rectangle (5,2);
\fill[geryel] (0,0) rectangle (5,1);
\end{tikzpicture}
\end{document}

enter image description here

Waving flag (2) (the ratio may not be true)

\documentclass[tikz]{standalone}
\usepackage{xcolor}
\definecolor{gerbla}{RGB}{0,0,0}
\definecolor{gerred}{RGB}{255,0,0}
\definecolor{geryel}{RGB}{255,204,0}
\begin{document}
\begin{tikzpicture}
\fill[gerbla] (0,3) to[out=0,in=180] (4,2) -- (4,1) to[out=180,in=0] (0,2) -- cycle;
\fill[gerred] (0,2) to[out=0,in=180] (4,1) -- (4,0) to[out=180,in=0] (0,1) -- cycle;
\fill[geryel] (0,1) to[out=0,in=180] (4,0) -- (4,-1) to[out=180,in=0] (0,0) -- cycle;
\end{tikzpicture}
\end{document}

enter image description here

Advanced waving flag (3)

enter image description here

(unTikZified – image taken from Emojipedia)


Question

How to draw a waving flag? In other word, say I already have a rectangle-shape flag (1), with many patterns and items on it (even with some \includegraphics), how can I "wave" (1) to get (2), where

  1. The ratio length/width is still correct.
  2. The images, patterns, etc. on the flag are waved too, and these items still fit correctly with the overall flag.

Bonus question

If I already have (1) or (2), how can I get shadow effects and light effects like (3)?


The code for the flag of the United States, as requested by @marmot:

\documentclass[tikz]{standalone}
\usetikzlibrary{shapes}
\usepackage{xcolor}
\begin{document}
\begin{tikzpicture}
\definecolor{usblue}{rgb}{.234,.233,.430}
\definecolor{usared}{rgb}{.698,.132,.203}
\fill[usared] (0,0) rectangle (1.9,1);
\foreach \i in {1,3,...,11}
    \fill[white] (0,\i/13) rectangle (1.9,{(\i+1)/13});
\fill [usblue] (0,6/13) rectangle ({1.9*2/5},1);
\foreach \i in {1,2,3,4,5,6} {
    \foreach \j in {1,2,3,4,5} {
        \node[star,star points=5,star point ratio=2.25,fill=white,minimum size=0.0616cm,inner sep=0pt] at ({(1.9/15)*\i-(1.9/30)},{6/13+(7/130)+(7*(\j-1)/65)}) {};
    }
}
\foreach \i in {1,2,3,4,5} {
    \foreach \j in {1,2,3,4} {
        \node[star,star points=5,star point ratio=2.25,fill=white,minimum size=0.0616cm,inner sep=0pt] at ({(1.9/15)*\i},{6/13+(7*\j/65)}) {};
    }
}
\end{tikzpicture}
\end{document}

enter image description here

Best Answer

You can use nonlinear transformations to achieve this effect.

\documentclass[tikz,border=3.14mm]{standalone}
\usepgfmodule{nonlineartransformations}
\definecolor{gerbla}{RGB}{0,0,0}
\definecolor{gerred}{RGB}{255,0,0}
\definecolor{geryel}{RGB}{255,204,0}
\makeatletter
\def\flagtransformation{%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
%\typeout{old\space x=\pgf@xa\space old \space y=\pgf@ya}%
\pgfmathsetmacro{\myy}{\pgf@ya+7*sin(\pgf@xa*3.6)-0.1*\pgf@xa}%
%\typeout{at\space x=\the\pgf@xa:\space new\space y=\myy}%
\pgf@y=\myy pt}
\makeatother
\newcommand{\GermanFlag}{%
\fill[gerbla] (0,2) rectangle (4,3);
\fill[gerred] (0,1) rectangle (4,2);
\fill[geryel] (0,0) rectangle (4,1);}
\begin{document}
\begin{tikzpicture}[font=\sffamily,scale=1]
\begin{scope}[local bounding box=linear]
 \GermanFlag
\end{scope}
\node[anchor=south] (lin) at (linear.north){linear};
\begin{scope}[xshift=5cm,local bounding box=nonlinear,transform shape
nonlinear=true]
 \pgftransformnonlinear{\flagtransformation}
 \GermanFlag
 \shade[left color=black,right color=black,middle color=white,opacity=0.15]
 (0,0) rectangle (2,3);
 \shade[left color=black,right color=black,middle color=white,opacity=0.15]
 (2,0) rectangle (4,3);
\end{scope}
\node[anchor=south] at (lin.south-|nonlinear.north){nonlinear};
\end{tikzpicture}
\end{document}

enter image description here

Of course, this works with any flag you have the TikZ code for, including of course your nice US flag.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{shapes}
\usepgfmodule{nonlineartransformations}
\definecolor{usblue}{rgb}{.234,.233,.430}
\definecolor{usared}{rgb}{.698,.132,.203}
\makeatletter
\def\flagtransformation{%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
%\typeout{old\space x=\pgf@xa\space old \space y=\pgf@ya}%
\pgfmathsetmacro{\myy}{\pgf@ya+7*sin(\pgf@xa*3.6)-0.1*\pgf@xa}%
%\typeout{at\space x=\the\pgf@xa:\space new\space y=\myy}%
\pgf@y=\myy pt}
\makeatother
\newcommand{\USFlag}{%
\fill[usared] (0,0) rectangle (1.9,1);
\foreach \i in {1,3,...,11}
    \fill[white] (0,\i/13) rectangle (1.9,{(\i+1)/13});
\fill [usblue] (0,6/13) rectangle ({1.9*2/5},1);
\foreach \i in {1,2,3,4,5,6} {
    \foreach \j in {1,2,3,4,5} {
        \node[star,star points=5,star point ratio=2.25,fill=white,
        minimum size=0.0616cm,inner sep=0pt] at ({(1.9/15)*\i-(1.9/30)},{6/13+(7/130)+(7*(\j-1)/65)}) {};
    }
}
\foreach \i in {1,2,3,4,5} {
    \foreach \j in {1,2,3,4} {
        \node[star,star points=5,star point ratio=2.25,fill=white,minimum size=0.0616cm,inner sep=0pt] at ({(1.9/15)*\i},{6/13+(7*\j/65)}) {};
    }
}}
\begin{document}
\begin{tikzpicture}[font=\sffamily,scale=2,transform shape]
\begin{scope}[local bounding box=linear]
 \USFlag
\end{scope}
\node[anchor=south] (lin) at (linear.north){linear};
\begin{scope}[xshift=2.5cm,local bounding box=nonlinear,transform shape
nonlinear=true]
 \pgftransformnonlinear{\flagtransformation}
 \USFlag
 \shade[left color=black,right color=black,middle color=white,opacity=0.15]
 (0,0) rectangle (0.95,1);
 \shade[left color=black,right color=black,middle color=white,opacity=0.15]
 (0.95,0) rectangle (1.9,1);
\end{scope}
\node[anchor=south] at (lin.south-|nonlinear.north){nonlinear};
\end{tikzpicture}
\end{document}

enter image description here

And here is something for Black Mild.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{shapes}
\usepgfmodule{nonlineartransformations}
\definecolor{usblue}{rgb}{.234,.233,.430}
\definecolor{usared}{rgb}{.698,.132,.203}
\makeatletter
\def\flagtransformation{%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
%\typeout{old\space x=\pgf@xa\space old \space y=\pgf@ya}%
\pgfmathsetmacro{\myy}{\pgf@ya+7*sin(\pgf@xa*3.6)-0.1*\pgf@xa}%
%\typeout{at\space x=\the\pgf@xa:\space new\space y=\myy}%
\pgf@y=\myy pt}
\tikzdeclarecoordinatesystem{flag}{% https://tex.stackexchange.com/a/434247/121799
    \tikz@scan@one@point\relax(#1)
    \flagtransformation
}
\makeatother
\newcommand{\USFlag}{%
\fill[usared] (0,0) rectangle (1.9,1);
\foreach \i in {1,3,...,11}
    \fill[white] (0,\i/13) rectangle (1.9,{(\i+1)/13});
\fill [usblue] (0,6/13) rectangle ({1.9*2/5},1);
\foreach \i in {1,2,3,4,5,6} {
    \foreach \j in {1,2,3,4,5} {
        \node[star,star points=5,star point ratio=2.25,fill=white,
        minimum size=0.0616cm,inner sep=0pt] at ({(1.9/15)*\i-(1.9/30)},{6/13+(7/130)+(7*(\j-1)/65)}) {};
    }
}
\foreach \i in {1,2,3,4,5} {
    \foreach \j in {1,2,3,4} {
        \node[star,star points=5,star point ratio=2.25,fill=white,minimum size=0.0616cm,inner sep=0pt] at ({(1.9/15)*\i},{6/13+(7*\j/65)}) {};
    }
}}
\begin{document}
\foreach \X in {0,5,...,95}
{\begin{tikzpicture}[font=\sffamily,scale=2,transform shape]
\begin{scope}[xshift=\X pt,
local bounding box=nonlinear,transform shape
nonlinear=true]
 \pgftransformnonlinear{\flagtransformation}
 \USFlag
 \shade[left color=black,right color=black,middle color=white,opacity=0.15]
 (0,0) rectangle (0.95,1);
 \shade[left color=black,right color=black,middle color=white,opacity=0.15]
 (0.95,0) rectangle (1.9,1); 
 \shade[left color=gray,right color=gray!70,middle color=gray!20] 
(0,1.05) rectangle ++ (-0.1,-3);
\end{scope}
\end{tikzpicture}}
\end{document}

enter image description here

This answer has the codes for many flags, out of which I picked Iceland. (Note that if the flag contains shapes, you need to say transform shape nonlinear=true.) The shading can be added on top.

\documentclass[tikz,border=3.14mm]{standalone}
\usepgfmodule{nonlineartransformations}
\makeatletter
\def\flagtransformation{%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
%\typeout{old\space x=\pgf@xa\space old \space y=\pgf@ya}%
\pgfmathsetmacro{\myy}{\pgf@ya+7*sin(\pgf@xa*3.6)-0.1*\pgf@xa}%
%\typeout{at\space x=\the\pgf@xa:\space new\space y=\myy}%
\pgf@y=\myy pt}
\makeatother
\definecolor{SkyBlue}{rgb}{0.00784314,0.32156864,0.61176473}
\definecolor{FireRed}{rgb}{0.86274511,0.11764706,0.20784314}
\newcommand\IcelandFlag{
\fill[SkyBlue] (0,0) rectangle (25,18);
\fill[white] (7,0) rectangle (11,18);
\fill[white] (0,7) rectangle (25,11);
\fill[FireRed] (8,0) rectangle (10,18);
\fill[FireRed] (0,8) rectangle (25,10);}
\begin{document}
\begin{tikzpicture}[font=\sffamily,scale=0.15]
\begin{scope}[local bounding box=linear]
 \IcelandFlag
\end{scope}
\node[anchor=south] (lin) at (linear.north){linear};
\begin{scope}[xshift=35cm,local bounding box=nonlinear]
 \pgftransformnonlinear{\flagtransformation}
 \IcelandFlag
 \shade[left color=black,right color=black,middle color=white,opacity=0.15]
 (0,0) rectangle (12.5,18);
 \shade[left color=black,right color=black,middle color=white,opacity=0.15]
 (12.5,0) rectangle (25,18);
\end{scope}
\node[anchor=south] at (lin.south-|nonlinear.north){nonlinear};
\end{tikzpicture}
\end{document}

enter image description here

In order to draw a 3d flag, you may use tikz-3dplot. With \shade you can get something like this.

\documentclass[tikz]{standalone}
\usepackage{tikz-3dplot} 
\definecolor{gerbla}{RGB}{0,0,0}
\definecolor{gerred}{RGB}{255,0,0}
\definecolor{geryel}{RGB}{255,204,0}
\begin{document}
\tdplotsetmaincoords{70}{20} 
\begin{tikzpicture}[tdplot_main_coords]
% \draw[-stealth] (0,0,0) -- (2,0,0) node[pos=1.1]{$x$};
% \draw[-stealth] (0,0,0) -- (0,2,0) node[pos=1.1]{$y$};
\foreach \X [count=\Y] in {gerbla,gerred,geryel}
{\shade[left color=\X,right color=\X!70!black,middle color=\X!70!white] 
plot[variable=\x,domain=0:2] (\x,{0.2*sin(\x*90)},4-\Y)
-- 
plot[variable=\x,domain=2:0] (\x,{0.2*sin(\x*90)},3-\Y);
\shade[left color=\X!70!black,right color=\X,middle color=\X!70!white] 
plot[variable=\x,domain=2:4] (\x,{0.2*sin(\x*90)},4-\Y)
-- 
plot[variable=\x,domain=4:2] (\x,{0.2*sin(\x*90)},3-\Y);}
\end{tikzpicture}
\end{document}

enter image description here