How to maker a LaTex drawing of dominoes falling as the following figure?
2 Answers
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);
-
1Impressive! As soon as I can I'll open a bounty for your answer. Commented May 22, 2014 at 16:43
-
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}
-
7
-
1Great example! I added it to the TikZ gallery, thank you for showing this impressive code!– Stefan Kottwitz ♦Commented May 11, 2014 at 14:34
-
1
-
2Now we need an animated version too.
:)
P.S. Really impressive piece of code. Commented May 19, 2014 at 13:08 -
1
pst-solides3d
(perspective view) andanimate
. Who is willing to take the challenge?\includegraphics{picture-of-dominoes}
tikz
code.