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

  • 21
    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. – Marco Daniel Dec 13 '13 at 11:22
  • 3
    This calls for pst-solides3d (perspective view) and animate. Who is willing to take the challenge? – AlexG Dec 13 '13 at 11:23
  • 35
    \includegraphics{picture-of-dominoes} – David Carlisle Dec 13 '13 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. – Mark Wibrow Dec 14 '13 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. – jubobs Jan 17 '14 at 22:01
up vote 64 down vote accepted
+400

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);
  • Impressive! As soon as I can I'll open a bounty for your answer. – Gonzalo Medina May 22 '14 at 16:43
  • Amazing! I need to learn Asymptote... – jubobs Aug 23 '14 at 10:22
up vote 107 down vote
+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
    WOW!! It is incredible!! – Azoun Dec 27 '13 at 18:17
  • 1
    Great example! I added it to the TikZ gallery, thank you for showing this impressive code! – Stefan Kottwitz May 11 '14 at 14:34
  • This is so awesome, I have to open a bounty! – Henri Menke May 19 '14 at 13:01
  • 2
    Now we need an animated version too. :) P.S. Really impressive piece of code. – Svend Tveskæg May 19 '14 at 13:08
  • This is ridiculously gorgeous. Tip of the hat to you, Mark. – jubobs Aug 23 '14 at 10:21

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.