How to maker a LaTex drawing of dominoes falling as the following figure?
Asked
Active
Viewed 6,338 times
28
2 Answers
129
As I can't find the original code this doesn't produce quite the same image that was linked in the comments above but this is much the same idea and uses the same principles.
The "wavy" arrangement of the standing dominoes is quite straightforward. The four falling dominoes at the end (or start - depending on how you look at it) form one big unsatisfactory kludge.
\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\tikzset{3D/.cd,
x/.store in=\xx, x=0,
y/.store in=\yy, y=0,
z/.store in=\zz, z=0
}
\tikzdeclarecoordinatesystem{3D}{%
\tikzset{3D/.cd,#1}%
\pgfpoint{sin(\yy)*(\xx)}{-((\xx)/75)^2+(\zz)/100*(\xx)}%
}
\begin{document}
\begin{tikzpicture}[line join=round, very thin]
\def\e{1260}
\foreach \x [evaluate={\i=mod(\x+90,360); \j=int((\i<180)*2-1); \t=3; \sc=\x/\e; \n=int((\e-\x)/15+5); \X=\x/\e;}] in {10,25,...,\e}{
\path [shift={(3D cs:x=\x-\t,y={3*sin(\x-\t)})}, yslant=cos(\x)/5]
(-\X/2, 0) coordinate (A') ( \X/2, 0) coordinate (B')
( \X/2,2*\X) coordinate (C') (-\X/2,2*\X) coordinate (D');
\path [shift={(3D cs:x=\x,y=3*sin \x)}, yslant=cos(\x)/5]
(-\X/2, 0) coordinate (A) ( \X/2, 0) coordinate (B)
( \X/2,2*\X) coordinate (C) (-\X/2,2*\X) coordinate (D);
\filldraw [black!90] (B) -- (B') -- (C') -- (C) -- cycle;
\filldraw [black!80] (A) -- (A') -- (D') -- (D) -- cycle;
\filldraw [black!70] (C) -- (D) -- (D') -- (C') -- cycle;
\filldraw [black] (A) -- (B) -- (C) -- (D) -- cycle;
\node [text=white, shift={($(C)!0.5!(D)$)}, anchor=north, yslant=cos(\x)/5, font=\sf, scale=\sc*1.5]
at (0,-.33*\X) {\n};
}
%
\foreach \i [evaluate={\x=\i*30-10; \X=1; \n=int(5-\i);\xsl=\x/180}]in {1,...,4}{
\path [shift={(3D cs:x=\x+\e,y=-3*\x/90)}, yslant=cos \e/5, xslant=\xsl]
(-\X/2, 0) coordinate (A) ( \X/2, 0) coordinate (B)
( \X/2, \X*2-\x/360) coordinate (C) (-\X/2, \X*2-\x/360) coordinate (D);
\path [shift={(3D cs:x=\x+\e,y=-3*\x/90)}, shift={(5/50,5/50-\i*2/50)}, yslant=cos \e/5, xslant=\xsl]
(-\X/2, 0) coordinate (A') ( \X/2, 0) coordinate (B')
( \X/2, \X*2-\x/330) coordinate (C') (-\X/2, \X*2-\x/330) coordinate (D');
\filldraw [black!70] (C) -- (D) -- (D') -- (C') -- cycle;
\filldraw [black!70] (A) -- (B) -- (B') -- (A') -- cycle;
\filldraw [black!90] (B) -- (B') -- (C') -- (C) -- cycle;
\filldraw [black] (A) -- (B) -- (C) -- (D) -- cycle;
\node [text=white, shift={($(C)!0.5!(D)$)}, anchor=north, xslant=\xsl,yslant=cos \e/5, font=\sf, scale=1.5]
at (0,-.33*\X) {\n};
}
\end{tikzpicture}
\end{document}

Mark Wibrow
- 70,437
-
7
-
1Great example! I added it to the TikZ gallery, thank you for showing this impressive code! – Stefan Kottwitz May 11 '14 at 14:34
-
1
-
2Now we need an animated version too.
:)P.S. Really impressive piece of code. – Svend Tveskæg May 19 '14 at 13:08 -
-
1
87
Here's an Asymptote version that uses a semi-realistic model to compute the falling dominoes, giving vector output:

And, the animated version (halfway--the gif with a full 200 frames was too big to upload):

Both versions take a while to compile.
Code for the still picture (save in foo.asy and run asy foo):
settings.outformat="pdf";
settings.render=0;
settings.prc=false;
import three;
unitsize(1cm);
currentprojection=perspective(
camera=(-10,0,5),
target=(48,2,-1),
angle=5,
autoadjust=false);
real height = 1;
real width = 0.5;
real depth = 0.08;
real separation = 0.5; //This is the interval from start to start.
surface domino = scale(depth, width, height) * shift(-1,-1/2,0) * unitcube;
triple labelposition = (-depth, 0, 0.7*height);
surface labelfor(string s) {
static transform3 T = shift(labelposition)*rotate(90,Y)*rotate(90,Z)*scale3(0.016)*scale(-1,1,1);
return T*surface(Label(s, p=fontsize(32)));
}
path receeding = scale(separation) * yscale(-1) * ( (0,-7) .. (7,0) .. (25,-6) .. (60,2) .. (95,-3) :: (140, -1) :: (200,0));
struct pointAndAngle {
triple point;
real angle;
}
pointAndAngle dominoPosition(int n) {
pointAndAngle toreturn;
real t = arctime(receeding, n*separation);
toreturn.point = XYplane(point(receeding,t));
pair tangent = dir(receeding, t);
toreturn.angle = degrees(atan2(tangent.y, tangent.x));
return toreturn;
}
transform3 dominoUpright(int n) {
pointAndAngle info = dominoPosition(n);
return shift(info.point) * rotate(info.angle, Z);
}
transform3 lyingDown(int n) {
return dominoUpright(n) * rotate(90, Y);
}
int nDominoes = 200;
draw(dominoUpright(0) * domino, invisible);
draw(dominoUpright(nDominoes-1) * domino, invisible);
draw(lyingDown(nDominoes-1) * domino, invisible);
int nToppled = 8;
write("Computing image with " + (string)nToppled + " dominoes toppled.");
surface currentdomino;
for (int n = nDominoes-1; n >= 0; --n) {
pointAndAngle position = dominoPosition(n);
transform3 T = shift(position.point) * rotate(position.angle, Z);
if (n <= nToppled-1) {
if (currentdomino.s.length == 0) T = T * rotate(85,Y);
else {
path3 toisectleft = T * circle(c=(0, interp(-width/2, width/2, 1/3), 0),normal=Y,r=height);
path3 toisectright = T* circle(c=(0, interp(-width/2, width/2, 2/3), 0),normal=Y,r=height);
triple[] isectionpointsleft = intersectionpoints(toisectleft, currentdomino);
triple[] isectionpointsright = intersectionpoints(toisectright, currentdomino);;
real zleft=0, zright=0;
for (triple pt : isectionpointsleft) {
if (pt.z >= zleft) zleft = pt.z;
}
for (triple pt : isectionpointsright) {
if (pt.z >= zright) zright = pt.z;
}
real angle1 = aSin(zleft / height);
real angle2 = aSin(zright / height);
if (angle1 > angle2) {
real tmp = angle2;
angle2 = angle1;
angle1 = tmp;
}
real angle = interp(angle1, angle2, 2);
T = T * rotate(90-angle, Y);
}
}
currentdomino = T * domino;
draw(currentdomino, gray(0.5));
if (n < 80)
draw( T*labelfor((string)(n+1)), emissive(white), meshpen=white );
}
Code for the animated version:
settings.outformat="gif";
settings.render=0;
import three;
import animation;
unitsize(1cm);
currentprojection=perspective(
camera=(-10,0,5),
target=(48,2,-1),
angle=5,
autoadjust=false);
real height = 1;
real width = 0.5;
real depth = 0.08;
real separation = 0.5; //This is the interval from start to start.
surface domino = scale(depth, width, height) * shift(-1,-1/2,0) * unitcube;
path3[] dominoOutline = scale(depth,width,height) * shift(-1,-1/2,0) * unitbox;
path receeding = scale(separation) * yscale(-1) * ( (0,-7) .. (7,0) .. (25,-6) .. (60,2) .. (95,-3) :: (140, -1) :: (200,0));
struct pointAndAngle {
triple point;
real angle;
}
pointAndAngle dominoPosition(int n) {
pointAndAngle toreturn;
real t = arctime(receeding, n*separation);
toreturn.point = XYplane(point(receeding,t));
pair tangent = dir(receeding, t);
toreturn.angle = degrees(atan2(tangent.y, tangent.x));
return toreturn;
}
transform3 dominoUpright(int n) {
pointAndAngle info = dominoPosition(n);
return shift(info.point) * rotate(info.angle, Z);
}
transform3 lyingDown(int n) {
return dominoUpright(n) * rotate(90, Y);
}
int nDominoes = 200;
animation a;
draw(dominoUpright(0) * domino, invisible);
draw(dominoUpright(nDominoes-1) * domino, invisible);
draw(lyingDown(nDominoes-1) * domino, invisible);
for (int nToppled = 0; nToppled < 100; ++nToppled) {
save();
write("Computing image with " + (string)nToppled + " dominoes toppled.");
surface currentdomino;
for (int n = nDominoes-1; n >= 0; --n) {
pointAndAngle position = dominoPosition(n);
transform3 T = shift(position.point) * rotate(position.angle, Z);
if (n <= nToppled) {
if (currentdomino.s.length == 0) T = T * rotate(85,Y);
else {
path3 toisectleft = T * circle(c=(0, interp(-width/2, width/2, 1/3), 0),normal=Y,r=height);
path3 toisectright = T* circle(c=(0, interp(-width/2, width/2, 2/3), 0),normal=Y,r=height);
triple[] isectionpointsleft = intersectionpoints(toisectleft, currentdomino);
triple[] isectionpointsright = intersectionpoints(toisectright, currentdomino);;
real zleft=0, zright=0;
for (triple pt : isectionpointsleft) {
if (pt.z >= zleft) zleft = pt.z;
}
for (triple pt : isectionpointsright) {
if (pt.z >= zright) zright = pt.z;
}
real angle1 = aSin(zleft / height);
real angle2 = aSin(zright / height);
if (angle1 > angle2) {
real tmp = angle2;
angle2 = angle1;
angle1 = tmp;
}
real angle = interp(angle1, angle2, 2);
T = T * rotate(90-angle, Y);
}
}
currentdomino = T * domino;
draw(currentdomino, emissive(white), meshpen=black + linewidth(1pt));
}
a.add();
restore();
}
a.movie(delay=50);
Charles Staats
- 19,514
-
1Impressive! As soon as I can I'll open a bounty for your answer. – Gonzalo Medina May 22 '14 at 16:43
-
pst-solides3d(perspective view) andanimate. Who is willing to take the challenge? – AlexG Dec 13 '13 at 11:23\includegraphics{picture-of-dominoes}– David Carlisle Dec 13 '13 at 11:54tikzcode. – Mark Wibrow Dec 14 '13 at 09:40