Rashid
Rashid

Reputation: 1384

applying setTimeout to recursive tree in p5.js

I am using p5.js to create growing tree in html5 convas.
I want to generate following tree smoothly instead of at once.

function setup(){ 
  createCanvas(600,600); 
  noLoop(); 
} 

function draw(){ 
  background(255);    
  strokeWeight(10); 
  translate(width/2,height-20); 
  branch(0); 
} 

function branch(depth){ 
  if (depth < 10) { 
    line(0,0,0,-height/10); // draw a line going up
    { 
      translate(0,-height/10); // move the space upwards
      rotate(random(-0.05,0.05));  // random wiggle

      if (random(1.0) < 0.6){ // branching   
        rotate(0.3); // rotate to the right
        scale(0.8); // scale down
        
        push(); // now save the transform state
        branch(depth + 1); // start a new branch!
        pop(); // go back to saved state
        
        rotate(-0.6); // rotate back to the left 
        
        push(); // save state
        branch(depth + 1);   // start a second new branch 
        pop(); // back to saved state        
     } 
      else { // no branch - continue at the same depth  
        branch(depth);
      } 
    } 
  }
} 


function mouseReleased(){ 
  redraw();  
}
html, body {
  margin: 0;
  padding: 0;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script>
<!DOCTYPE html><html><head>
  </head>
  <body>
    <script src="sketch.js"></script>
  

</body></html>

I am using setTimeout function to delay each recursive branch to grow the tree smoothly.
But getting following unexpected shape

function setup(){ 
  createCanvas(600,600); 
  noLoop(); 
} 

function draw(){ 
  background(255);    
  strokeWeight(10); 
  translate(width/2,height-20); 
  branch(0); 
} 

function branch(depth){ 
setTimeout(function() {
  if (depth < 10) { 
    line(0,0,0,-height/10); // draw a line going up
    { 
      translate(0,-height/10); // move the space upwards
      rotate(random(-0.05,0.05));  // random wiggle

      if (random(1.0) < 0.6){ // branching   
        rotate(0.3); // rotate to the right
        scale(0.8); // scale down
        
        push(); // now save the transform state
        branch(depth + 1); // start a new branch!
        pop(); // go back to saved state
        
        rotate(-0.6); // rotate back to the left 
        
        push(); // save state
        branch(depth + 1);   // start a second new branch 
        pop(); // back to saved state        
     } 
      else { // no branch - continue at the same depth  
        branch(depth);
      } 
    } 
  }
}, 500);
} 


function mouseReleased(){ 
  redraw();  
}
html, body {
  margin: 0;
  padding: 0;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script>
<!DOCTYPE html><html><head>
  </head>
  <body>
    <script src="sketch.js"></script>
  

</body></html>

Please give any solution to grow the tree smoothly (instead of at once).

Upvotes: 1

Views: 303

Answers (1)

trincot
trincot

Reputation: 350272

You can use async and await for this.

  1. Define a utility function the returns a promise that resolves after a given delay:

    const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
    
  2. Make your branch function async (just prepend it with that keyword).

  3. Add await before each of the three recursive calls you make. For example:

    await branch(depth+1);
    
  4. Add a new line to introduce the delay:

    if (depth < 10) {
        await delay(10); // "sleep" for 10 milliseconds.
        // ...
    

The result:

function setup(){ 
  createCanvas(600,600); 
  noLoop(); 
} 

function draw(){ 
  background(255);    
  strokeWeight(10); 
  translate(width/2,height-20); 
  branch(0); 
} 

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

async function branch(depth){ 
  if (depth < 10) { 
    await delay(10);
    line(0,0,0,-height/10); // draw a line going up
    { 
      translate(0,-height/10); // move the space upwards
      rotate(random(-0.05,0.05));  // random wiggle

      if (random(1.0) < 0.6){ // branching   
        rotate(0.3); // rotate to the right
        scale(0.8); // scale down
        
        push(); // now save the transform state
        await branch(depth + 1); // start a new branch!
        pop(); // go back to saved state
        
        rotate(-0.6); // rotate back to the left 
        
        push(); // save state
        await branch(depth + 1);   // start a second new branch 
        pop(); // back to saved state        
     } 
      else { // no branch - continue at the same depth  
        await branch(depth);
      } 
    } 
  }
} 


function mouseReleased(){ 
  redraw();  
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script>

Note that the mouseReleased function may get called while there is still an asychronous chain of drawing ongoing. This will lead to an unexpected mix of drawing taking place.

You can avoid this, by temporarily "disabling" that function with a guard, as follows:

  1. Define a global variable busy = false;

  2. Manage that variable at the start/end of the drawing, by changing the function draw to:

    function draw(){ 
      if (busy) return; // guard against concurrent drawing activity
      busy = true; // set guard
      background(255);    
      strokeWeight(10); 
      translate(width/2,height-20); 
      branch(0).then(() => busy = false); // clear guard asynchronously
    } 
    

function setup(){ 
  createCanvas(600,600); 
  noLoop(); 
} 

let busy = false;
function draw(){ 
  if (busy) return; // guard against concurrent drawing activity
  busy = true; // set guard
  background(255);    
  strokeWeight(10); 
  translate(width/2,height-20); 
  branch(0).then(() => busy = false); // clear guard asynchronously
} 

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

async function branch(depth){ 
  if (depth < 10) { 
    await delay(10);
    line(0,0,0,-height/10); // draw a line going up
    { 
      translate(0,-height/10); // move the space upwards
      rotate(random(-0.05,0.05));  // random wiggle

      if (random(1.0) < 0.6){ // branching   
        rotate(0.3); // rotate to the right
        scale(0.8); // scale down
        
        push(); // now save the transform state
        await branch(depth + 1); // start a new branch!
        pop(); // go back to saved state
        
        rotate(-0.6); // rotate back to the left 
        
        push(); // save state
        await branch(depth + 1);   // start a second new branch 
        pop(); // back to saved state        
     } 
      else { // no branch - continue at the same depth  
        await branch(depth);
      } 
    } 
  }
} 


function mouseReleased(){ 
  redraw();  
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script>

Upvotes: 3

Related Questions