28

How to maker a LaTex drawing of dominoes falling as the following figure?enter image description here

11
  • 24
    On this site, a question should typically revolve around an abstract issue (e.g. "How do I get a double horizontal line in a table?") rather than a concrete application (e.g. "How do I make this table?"). Questions that look like "Please do this complicated thing for me" tend to get closed because they are either "off topic", "too broad", or "unclear". Please try to make your question clear and simple by giving a minimal working example (MWE): you'll stand a greater chance of getting help. Commented Dec 13, 2013 at 11:22
  • 4
    This calls for pst-solides3d (perspective view) and animate. Who is willing to take the challenge?
    – AlexG
    Commented Dec 13, 2013 at 11:23
  • 40
    \includegraphics{picture-of-dominoes} Commented Dec 13, 2013 at 11:54
  • 4
    I don't know how long the link is valid for but this, although not perfect, takes about 70 lines of tikz code. Commented Dec 14, 2013 at 9:40
  • 3
    Since you have some responses below that seem to answer your question, please consider marking one of them as ‘Accepted’ by clicking on the tickmark below their vote count (see How do you accept an answer?). This shows which answer helped you most, and it assigns reputation points to the author of the answer (and to you!). It's part of this site's idea to identify good questions and answers through upvotes and acceptance of answers.
    – jub0bs
    Commented Jan 17, 2014 at 22:01

2 Answers 2

87
+1400

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):

enter image description here

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);
2
  • 1
    Impressive! As soon as I can I'll open a bounty for your answer. Commented May 22, 2014 at 16:43
  • Amazing! I need to learn Asymptote...
    – jub0bs
    Commented Aug 23, 2014 at 10:22
129
+450

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}

enter image description here

6
  • 7
    WOW!! It is incredible!!
    – Azoun
    Commented Dec 27, 2013 at 18:17
  • 1
    Great 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
    This is so awesome, I have to open a bounty! Commented May 19, 2014 at 13:01
  • 2
    Now we need an animated version too. :) P.S. Really impressive piece of code. Commented May 19, 2014 at 13:08
  • 1
    Beautifully concise code!
    – Geremia
    Commented Sep 3, 2014 at 18:44

You must log in to answer this question.

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