37

In the spirit of How can we draw a Christmas tree with decorations, using TikZ?

Can you draw a 3D Christmas gift box with as many details as possible(bow tie, elevated lid/bow, patterned wrapping paper)?

Bonus points (500 rep bounty) for animating the box as it opens.

MWE (Addition by HK)

\documentclass[varwidth,tikz]{standalone}
\begin{document}

\begin{tikzpicture}
\draw[fill=red] (0,0) rectangle (1,1);
\draw[fill=red] (1,0) -- ++(50:0.75cm) -- ++(90:1cm) -- (1,1) -- cycle;
\draw[fill=magenta] (0,1) -- ++(50:0.75cm) -- ++(0:1cm) -- (1,1) --  cycle;
\draw (0,0.8) -- (1,0.8) -- +(50:0.75cm);
\end{tikzpicture}
\end{document}

enter image description here

2 Answers2

97

Just to get things going...

\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}


\tikzset{gift box/.cd,
  x/.store in=\gbx,
  y/.store in=\gby,
  z/.store in=\gbz,
  x=0,y=0,z=0
}
\tikzdeclarecoordinatesystem{gift box}{%
  \tikzset{x=(-20:1cm),y=(200:1cm),z=(90:1cm)}%
  \tikzset{gift box/.cd, #1}%
  \pgfpointxyz{\gbx*(.9+\gbz/20)}{\gby*(.9+\gbz/20)}{\gbz*(1+\gbx/10+\gby/10)}%
}

\tikzset{wrapping paper/.style={
    bottom color=red!75!black, top color=red!50!black,
  },
  ribbon/.style={
    bottom color=yellow!75!orange, top color=yellow!50!orange,
  },
  sheen/.style={
    left color=black, right color=white, opacity=0.25
  },
  highlight/.style={
    draw=white,
    opacity=1/100,
    line width=#1,
    line cap=round
  },
  lowlight/.style={
    draw=black,
    opacity=1/80,
    line width=#1,
    line cap=round,
    line join=round
  }
}



\begin{document}

\begin{tikzpicture}

\foreach \l in {1,...,20}
  \path [lowlight=\l/3] 
       (gift box cs:x=-1, y= 1, z=-1) 
    -- (gift box cs:x= 1, y= 1, z=-1) 
    -- (gift box cs:x= 1, y=-1, z=-1) 
    -- (gift box cs:x= 1, y=-1, z=-1)
    -- cycle;



\path [wrapping paper]
     (gift box cs:x=-1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z=1)
  -- (gift box cs:x= 1, y= 1, z=1)
  -- (gift box cs:x= 1, y=-1, z=1)
  -- (gift box cs:x= 1, y=-1, z=-1)
  -- (gift box cs:x= 1, y= 1, z=-1)
  -- cycle;


\foreach \n/\o in {.25/1, .33/.5}
  \path [ribbon, opacity=\o] 
       (gift box cs:x=1, y=-\n, z=-1)
    -- (gift box cs:x=1, y=-\n, z=1)
    -- (gift box cs:x=1, y= \n, z=1)
    -- (gift box cs:x=1, y= \n, z=-1)
    -- cycle
       (gift box cs:x=-\n, y=1, z=-1)
    -- (gift box cs:x=-\n, y=1, z=1)
    -- (gift box cs:x= \n, y=1, z=1)
    -- (gift box cs:x= \n, y=1, z=-1)
    -- cycle;

\path [sheen]
     (gift box cs:x=1,y=-1,z=-1)
  -- (gift box cs:x=1,y=-1,z=1)
  -- (gift box cs:x=1, y=1,z=1)
  -- (gift box cs:x=1, y=1,z=-1)
  -- cycle;

\path  [sheen]
     (gift box cs:x=-1,y=1,z=-1)
  -- (gift box cs:x=-1,y=1,z=1)
  -- (gift box cs:x=1, y=1,z=1)
  -- (gift box cs:x=1, y=1,z=-1)
  -- cycle;

\begin{scope}

\path [clip]
     (gift box cs:x=-1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z= 1)
  -- (gift box cs:x= 1, y= 1, z= 1)
  -- (gift box cs:x= 1, y=-1, z= 1)
  -- (gift box cs:x= 1, y=-1, z=-1)
  -- (gift box cs:x= 1, y= 1, z=-1)
  -- cycle;

\foreach \l in {1,...,10}{
  \path [lowlight=\l/3] 
       (gift box cs:x=-1, y= 1, z=1) 
    -- (gift box cs:x=-1, y= 1, z=-1) 
    -- (gift box cs:x= 1, y= 1, z=-1) 
    -- (gift box cs:x= 1, y=-1, z=-1) 
    -- (gift box cs:x= 1, y=-1, z=1)
       (gift box cs:x=1, y=1,z=-1) 
    -- (gift box cs:x=1, y=1,z= 1);

  \path [lowlight=\l/2]
       (gift box cs:x=-1.05, y= 1.05, z=1) 
    -- (gift box cs:x=-1.05, y= 1.05, z=.5)
    -- (gift box cs:x= 1.05, y= 1.05, z=.5)
    -- (gift box cs:x= 1.05, y=-1.05, z=.5)
    -- (gift box cs:x= 1.05, y=-1.05, z=1);
}
\end{scope}


\path [wrapping paper]
     (gift box cs:x=-1.05, y=-1.05, z=1)
  -- (gift box cs:x=-1.05, y= 1.05, z=1)
  -- (gift box cs:x=-1.05, y= 1.05, z=.5)
  -- (gift box cs:x= 1.05, y= 1.05, z=.5)
  -- (gift box cs:x= 1.05, y=-1.05, z=.5)
  -- (gift box cs:x= 1.05, y=-1.05, z=1)
  -- cycle;



\foreach \n/\o in {.33/.5, .25/1}{
  \path [ribbon, opacity=\o]
       (gift box cs:x= 1.05, y=-\n, z=0.5)
    -- (gift box cs:x= 1.05, y=-\n, z=1)
    -- (gift box cs:x=-1.05, y=-\n, z=1)
    -- (gift box cs:x=-1.05, y= \n, z=1)
    -- (gift box cs:x= 1.05, y= \n, z=1)
    -- (gift box cs:x= 1.05, y= \n, z=.5)
    -- cycle;

  \path [ribbon, opacity=\o]
       (gift box cs:y= 1.05, x=-\n, z=0.5)
    -- (gift box cs:y= 1.05, x=-\n, z=1)
    -- (gift box cs:y=-1.05, x=-\n, z=1)
    -- (gift box cs:y=-1.05, x= \n,  z=1)
    -- (gift box cs:y= 1.05, x= \n, z=1)
    -- (gift box cs:y= 1.05, x= \n, z=.5)
    -- cycle;
}


\path [sheen]
     (gift box cs:x=-1.05, y=1.05, z=1)
  -- (gift box cs:x=-1.05, y=1.05, z=0.5)
  -- (gift box cs:x= 1.05, y=1.05, z=0.5)
  -- (gift box cs:x= 1.05, y=1.05, z=1)
  -- cycle;

\path [sheen]
     (gift box cs:x=1.05, y=-1.05, z=1)
  -- (gift box cs:x=1.05, y=-1.05, z=0.5)
  -- (gift box cs:x=1.05, y= 1.05, z=0.5)
  -- (gift box cs:x=1.05, y= 1.05, z=1)
  -- cycle;

\path [sheen]
     (gift box cs:x=-1.05, y=-1.05, z=1)
  -- (gift box cs:x=-1.05, y= 1.05, z=1)
  -- (gift box cs:x= 1.05, y= 1.05, z=1)
  -- (gift box cs:x= 1.05, y=-1.05, z=1)
  -- cycle;

\begin{scope}
\path [clip]
     (gift box cs:x=-1.05, y=-1.05, z=1)
  -- (gift box cs:x=-1.05, y= 1.05, z=1)
  -- (gift box cs:x=-1.05, y= 1.05, z=.5)
  -- (gift box cs:x= 1.05, y= 1.05, z=.5)
  -- (gift box cs:x= 1.05, y=-1.05, z=.5)
  -- (gift box cs:x= 1.05, y=-1.05, z=1)
  -- cycle;

\foreach \l in {1,...,10}{
  \path [highlight=\l/3] 
       (gift box cs:x= 1.05, y= 1.05, z=1) 
    -- (gift box cs:x= 1.05, y=-1.05, z=1) 
    -- (gift box cs:x=-1.05, y=-1.05, z=1) 
    -- (gift box cs:x=-1.05, y= 1.05, z=1)
    -- cycle 
       (gift box cs:x=1.05, y=1.05, z=1) 
    -- (gift box cs:x=1.05, y=1.05, z=0.5);

  \path [lowlight=\l/3]
       (gift box cs:x=-1.05, y= 1.05, z=1) 
    -- (gift box cs:x=-1.05, y= 1.05, z=.5)
    -- (gift box cs:x= 1.05, y= 1.05, z=.5)
    -- (gift box cs:x= 1.05, y=-1.05, z=.5)
    -- (gift box cs:x= 1.05, y=-1.05, z=1);
}
\end{scope}
\end{tikzpicture}

\end{document}

enter image description here

And, with surprisingly little extra work:

enter image description here

Unfortunately, this gift box is empty. Sad faces all round.

(Unless it contains something small that cannot yet be seen).

The (rather pleasing) "texture" that the wrapping seems to have is a result of reducing the size and optimizing the color space of the gif for uploading.

The code (which takes some time to compile) is shown below. The resulting pdf is then processed with gimp to produce the image above:

\documentclass[tikz,border=0.125cm]{standalone}

\tikzset{gift box/.cd,
  x/.store in=\gbx,
  y/.store in=\gby,
  z/.store in=\gbz,
  rotate around z axis/.store in=\gbzaxisrotate,
  x=0,y=0,z=0,  
  rotate around z axis=0
}
\tikzdeclarecoordinatesystem{gift box}{%
  \tikzset{x=(-20:1cm),y=(200:1cm),z=(90:1cm)}%
  \tikzset{gift box/.cd, #1}%
  \pgfmathsin{\gbzaxisrotate}\let\gbsin=\pgfmathresult%
  \pgfmathcos{\gbzaxisrotate}\let\gbcos=\pgfmathresult%
  \pgfmathparse{cos(\gbzaxisrotate)*(\gbx)-sin(\gbzaxisrotate)*(\gby)}\let\gbxa=\pgfmathresult%
  \pgfmathparse{sin(\gbzaxisrotate)*(\gbx)+cos(\gbzaxisrotate)*(\gby)}\let\gbya=\pgfmathresult%
  \pgfpointxyz{\gbxa*(.9+(\gbz)/20)}{\gbya*(.9+(\gbz)/20)}{(\gbz)*(1+\gbxa/10+\gbya/10)}%
}

\tikzset{wrapping paper/.style={
    bottom color=red!75!black, top color=red!50!black,
  },
  ribbon/.style={
    bottom color=yellow!75!orange, top color=yellow!50!orange,
  },
  sheen/.style={
    left color=black, right color=white, opacity=0.25
  },
  sheen inside/.style={
    left color=black, right color=black!50, opacity=0.25
  },
  highlight/.style={
    draw=white,
    opacity=1/100,
    line width=#1,
    line cap=round
  },
  lowlight/.style={
    draw=black,
    opacity=1/80,
    line width=#1,
    line cap=round,
    line join=round
  }
}



\begin{document}


\foreach \i [evaluate={\zangle=-45+mod(\i,18)*5; \lidz=1.25+min(\i,18)*.1;}] in {0,...,35}{
\begin{tikzpicture}[gift box/rotate around z axis=\zangle]

\useasboundingbox [gift box/rotate around z axis=0] 
     (gift box cs:x= 1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z=3)
  -- (gift box cs:x=-1, y=-1, z=3)
  -- (gift box cs:x= 1, y=-1, z=3)
  -- (gift box cs:x= 1, y=-1, z=-1)
  -- cycle;

\foreach \l in {1,...,20}
  \path [lowlight=\l/3] 
       (gift box cs:x=-1, y= 1, z=-1) 
    -- (gift box cs:x= 1, y= 1, z=-1) 
    -- (gift box cs:x= 1, y=-1, z=-1) 
    -- (gift box cs:x= 1, y=-1, z=-1)
    -- cycle;

\path [wrapping paper]
     (gift box cs:x= 1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z=1)
  -- (gift box cs:x=-1, y=-1, z=1)
  -- (gift box cs:x= 1, y=-1, z=1)
  -- (gift box cs:x= 1, y=-1, z=-1)
  -- cycle;


\path [sheen inside]
     (gift box cs:x= 1, y=-1, z=-1)
  -- (gift box cs:x= 1, y=-1, z=1)
  -- (gift box cs:x=-1, y=-1, z=1)
  -- (gift box cs:x=-1, y=-1, z=-1)
  -- cycle;

\path  [sheen inside]
     (gift box cs:x=-1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z=1)
  -- (gift box cs:x=-1, y=-1, z=1)
  -- (gift box cs:x=-1, y=-1, z=-1)
  -- cycle;

\begin{scope}

\path [clip]
     (gift box cs:x= 1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z=1)
  -- (gift box cs:x=-1, y=-1, z=1)
  -- (gift box cs:x= 1, y=-1, z=1)
  -- (gift box cs:x= 1, y=-1, z=-1)
  -- cycle;

\foreach \l in {1,...,20}
  \path [lowlight=\l/3] 
       (gift box cs:x=-1, y= 1, z=1) 
    -- (gift box cs:x=-1, y=-1, z=1) 
    -- (gift box cs:x= 1, y=-1, z=1);
\end{scope}

\path [wrapping paper]
     (gift box cs:x=-1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z=1)
  -- (gift box cs:x= 1, y= 1, z=1)
  -- (gift box cs:x= 1, y=-1, z=1)
  -- (gift box cs:x= 1, y=-1, z=-1)
  -- (gift box cs:x= 1, y= 1, z=-1)
  -- cycle;


\foreach \n/\o in {.25/1, .33/.5}
  \path [ribbon, opacity=\o] 
       (gift box cs:x=1, y=-\n, z=-1)
    -- (gift box cs:x=1, y=-\n, z=1)
    -- (gift box cs:x=1, y= \n, z=1)
    -- (gift box cs:x=1, y= \n, z=-1)
    -- cycle
       (gift box cs:x=-\n, y=1, z=-1)
    -- (gift box cs:x=-\n, y=1, z=1)
    -- (gift box cs:x= \n, y=1, z=1)
    -- (gift box cs:x= \n, y=1, z=-1)
    -- cycle;

\path [sheen]
     (gift box cs:x=1, y=-1, z=-1)
  -- (gift box cs:x=1, y=-1, z=1)
  -- (gift box cs:x=1, y= 1, z=1)
  -- (gift box cs:x=1, y= 1, z=-1)
  -- cycle;

\path  [sheen]
     (gift box cs:x=-1,y=1,z=-1)
  -- (gift box cs:x=-1,y=1,z=1)
  -- (gift box cs:x=1, y=1,z=1)
  -- (gift box cs:x=1, y=1,z=-1)
  -- cycle;

\begin{scope}

\path [clip]
     (gift box cs:x=-1, y= 1, z=-1)
  -- (gift box cs:x=-1, y= 1, z= 1)
  -- (gift box cs:x= 1, y= 1, z= 1)
  -- (gift box cs:x= 1, y=-1, z= 1)
  -- (gift box cs:x= 1, y=-1, z=-1)
  -- (gift box cs:x= 1, y= 1, z=-1)
  -- cycle;

\foreach \l in {1,...,10}{
  \path [lowlight=\l/3] 
       (gift box cs:x=-1, y= 1, z=1) 
    -- (gift box cs:x=-1, y= 1, z=-1) 
    -- (gift box cs:x= 1, y= 1, z=-1) 
    -- (gift box cs:x= 1, y=-1, z=-1) 
    -- (gift box cs:x= 1, y=-1, z=1)
       (gift box cs:x=1, y=1,z=-1) 
    -- (gift box cs:x=1, y=1,z= 1);

   \path [highlight=\l/3]
        (gift box cs:x=-1, y= 1, z=1) 
     -- (gift box cs:x= 1, y= 1, z=1)
     -- (gift box cs:x= 1, y=-1, z=1);

    \path [lowlight=\l/2]
        (gift box cs:x=-1.05, y= 1.05, z=\lidz-.5) 
     -- (gift box cs:x= 1.05, y= 1.05, z=\lidz-.5)
     -- (gift box cs:x= 1.05, y=-1.05, z=\lidz-.5);
}
\end{scope}

\path [wrapping paper]
     (gift box cs:x=-1.05, y=-1.05, z=\lidz)
  -- (gift box cs:x=-1.05, y= 1.05, z=\lidz)
  -- (gift box cs:x=-1.05, y= 1.05, z=\lidz-.5)
  -- (gift box cs:x= 1.05, y= 1.05, z=\lidz-.5)
  -- (gift box cs:x= 1.05, y=-1.05, z=\lidz-.5)
  -- (gift box cs:x= 1.05, y=-1.05, z=\lidz)
  -- cycle;



\foreach \n/\o in {.33/.5, .25/1}{
  \path [ribbon, opacity=\o]
       (gift box cs:x= 1.05, y=-\n, z=\lidz-.5)
    -- (gift box cs:x= 1.05, y=-\n, z=\lidz)
    -- (gift box cs:x=-1.05, y=-\n, z=\lidz)
    -- (gift box cs:x=-1.05, y= \n, z=\lidz)
    -- (gift box cs:x= 1.05, y= \n, z=\lidz)
    -- (gift box cs:x= 1.05, y= \n, z=\lidz-.5)
    -- cycle;

  \path [ribbon, opacity=\o]
       (gift box cs:y= 1.05, x=-\n, z=\lidz-.5)
    -- (gift box cs:y= 1.05, x=-\n, z=\lidz)
    -- (gift box cs:y=-1.05, x=-\n, z=\lidz)
    -- (gift box cs:y=-1.05, x= \n,  z=\lidz)
    -- (gift box cs:y= 1.05, x= \n, z=\lidz)
    -- (gift box cs:y= 1.05, x= \n, z=\lidz-.5)
    -- cycle;
}


\path [sheen]
     (gift box cs:x=-1.05, y=1.05, z=\lidz)
  -- (gift box cs:x=-1.05, y=1.05, z=\lidz-.5)
  -- (gift box cs:x= 1.05, y=1.05, z=\lidz-.5)
  -- (gift box cs:x= 1.05, y=1.05, z=\lidz)
  -- cycle;

\path [sheen]
     (gift box cs:x=1.05, y=-1.05, z=\lidz)
  -- (gift box cs:x=1.05, y=-1.05, z=\lidz-.5)
  -- (gift box cs:x=1.05, y= 1.05, z=\lidz-.5)
  -- (gift box cs:x=1.05, y= 1.05, z=\lidz)
  -- cycle;

\path [sheen]
     (gift box cs:x=-1.05, y=-1.05, z=\lidz)
  -- (gift box cs:x=-1.05, y= 1.05, z=\lidz)
  -- (gift box cs:x= 1.05, y= 1.05, z=\lidz)
  -- (gift box cs:x= 1.05, y=-1.05, z=\lidz)
  -- cycle;

\begin{scope}
\path [clip]
     (gift box cs:x=-1.05, y=-1.05, z=\lidz)
  -- (gift box cs:x=-1.05, y= 1.05, z=\lidz)
  -- (gift box cs:x=-1.05, y= 1.05, z=\lidz-.5)
  -- (gift box cs:x= 1.05, y= 1.05, z=\lidz-.5)
  -- (gift box cs:x= 1.05, y=-1.05, z=\lidz-.5)
  -- (gift box cs:x= 1.05, y=-1.05, z=\lidz)
  -- cycle;

\foreach \l in {1,...,10}{
  \path [highlight=\l/3] 
       (gift box cs:x= 1.05, y= 1.05, z=\lidz) 
    -- (gift box cs:x= 1.05, y=-1.05, z=\lidz) 
    -- (gift box cs:x=-1.05, y=-1.05, z=\lidz) 
    -- (gift box cs:x=-1.05, y= 1.05, z=\lidz)
    -- cycle 
       (gift box cs:x=1.05, y=1.05, z=\lidz) 
    -- (gift box cs:x=1.05, y=1.05, z=\lidz-.5);

  \path [lowlight=\l/3]
       (gift box cs:x=-1.05, y= 1.05, z=\lidz) 
    -- (gift box cs:x=-1.05, y= 1.05, z=\lidz-.5)
    -- (gift box cs:x= 1.05, y= 1.05, z=\lidz-.5)
    -- (gift box cs:x= 1.05, y=-1.05, z=\lidz-.5)
    -- (gift box cs:x= 1.05, y=-1.05, z=\lidz);
}
\end{scope}
\ifnum\i>17
\path [fill=white,opacity=(\i-17)/7]
     (gift box cs:x=-1.1, y=-1.1, z=\lidz)
  -- (gift box cs:x=-1.1, y= 1.1, z=\lidz)
  -- (gift box cs:x=-1.1, y= 1.1, z=\lidz-.5)
  -- (gift box cs:x= 1.1, y= 1.1, z=\lidz-.5)
  -- (gift box cs:x= 1.1, y=-1.1, z=\lidz-.5)
  -- (gift box cs:x= 1.1, y=-1.1, z=\lidz)
  -- cycle;
\fi
\end{tikzpicture}
}
\end{document}
Mark Wibrow
  • 70,437
5

This does not come anywhere close to the 3D efforts in the other answer, but here is a mathematical effort in Metapost.

enter image description here

It's a semi-regular tiling drawn in Christmassy colours (from metapost-colorbrewer), with its dual tiling superimposed, so that it looks a bit like gold ribbon wrapped around square packages.

Merry Christmas!

\documentclass{standalone}
\usepackage{luamplib}
\begin{document}
\begin{mplibcode}
input colorbrewer-cmyk
% a polygon of n sides on a given path
vardef poly expr n of p = 
    save x, y;
    z0 = point 0 of p;
    z1 = point 1 of p;
    for i=2 upto n-1:
        z[i] = z[i-2] rotatedabout(z[i-1], 180(2/n-1));
    endfor
    for i=0 upto n-1: z[i] -- endfor cycle
enddef;
vardef median(expr P) = 
    (origin for i=1 upto length P: + point i of P endfor) / length P
enddef;
beginfig(1);
    path t[], s[];
    s1 = poly 4 of (origin -- 42 dir 15);
    t1 = poly 3 of subpath (2, 1) of s1;
    t2 = poly 3 of subpath (3, 2) of s1;
    t3 = poly 3 of subpath (2, 1) of t1;
    t4 = poly 3 of subpath (2, 1) of t2;
    s2 = poly 4 of subpath (2, 1) of t4;
picture unit;
unit = image(
    fill s1 withcolor Oranges 8 5;
    fill t1 withcolor Blues 8 4;
    fill t4 withcolor Blues 8 3;
    fill t3 withcolor Greens 8 4;
    fill t2 withcolor Greens 8 3;
    fill s2 withcolor Reds 8 5;
    forsuffixes $=s1, t1, s2, t2, t3, t4: 
        pair m; m = median($);
        for i=1 upto length $:
            draw point i - 1/2 of $ -- m 
                withpen pencircle scaled 3/2 withcolor Spectral 3 2;
        endfor
    endfor
);

pair u, v;
u = point 2 of t2 - point 0 of s1;
v = point 2 of t3 - point 0 of s1;

for i=0 upto 6:
    for j=0 upto 9:
        draw unit shifted (i*u + j*v);
    endfor
endfor

endfig; \end{mplibcode} \end{document}

You will need to compile this with lualatex.

Thruston
  • 42,268