Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a nested for loop for cylinders around a sphere(coronavirus) in p5.js?

I'm pretty new and learning p5.js, and I am trying to make a 3D coronavirus in p5.js with a sphere and a bunch of cylinders..

You can see my sketch here: https://editor.p5js.org/zzzzzij/sketches/frE9-37R

    var sketch = function (p) {
      with(p) {

        let angle = 0;

        p.setup = function() {
          createCanvas(400, 400, WEBGL);
        };
    
        p.draw = function() {
          ambientLight(255);
          background(175);
          noStroke();
          rotateY(angle);
          rotateZ(angle*0.8);
          normalMaterial();

          push();
          rotateY(PI);
          sphere(100);
          pop();

          push();
          for (i = 0; i < 24; i ++) {
            rotateZ(PI/6);
            push();
            translate (0, -21*5, 0*5);
            rotateY(PI/18);
            rotateX(0);
            cylinder (6, 20);
            pop();
          } 

          for (i = 0; i < 24; i ++) {
            rotateZ(PI/5);
            push();
            translate (0, -19*5, 9*5);
            rotateY(PI/18);
            rotateX(-PI/8);
            cylinder (6, 20);
            pop();
          } 

          for (i = 0; i < 24; i ++) {
            rotateZ(PI/4);
            push();
            translate (0, -15*5, 15*5);
            rotateY(PI/18);
            rotateX(-PI/4);
            cylinder (6, 20);
            pop();
          } 

          for (i = 0; i < 24; i ++) {
            rotateZ(PI/3);
            push();
            translate (0, -9*5, 19*5);
            rotateY(PI/18);
            rotateX(-PI/2.5);
            cylinder (6, 20);
            pop();
          }  
            
          for (i = 0; i < 5; i ++) {
            rotateZ(0);
            push();
            translate (0, 0*5, 21*5);
            rotateY(0);
            rotateX(-PI/2);
            cylinder (6, 20);
            pop();
          } 

          for (i = 0; i < 5; i ++) {
            rotateZ(0);
            push();
            translate (0, 0*5, -21*5);
            rotateY(0);
            rotateX(-PI/2);
            cylinder (6, 20);
            pop();
          } 

          for (i = 0; i < 24; i ++) {
            rotateZ(PI/3);
            push();
            translate (0, 9*5, -19*5);
            rotateY(-PI/18);
            rotateX(-PI/2.5);
            cylinder (6, 20);
            pop();
          } 

          for (i = 0; i < 24; i ++) {
            rotateZ(PI/4);
            push();
            translate (0, 15*5, -15*5);
            rotateY(-PI/18);
            rotateX(-PI/4);
            cylinder (6, 20);
            pop();
          } 

          for (i = 0; i < 24; i ++) {
            rotateZ(PI/5);
            push();
            translate (0, 19*5, -9*5);
            rotateY(-PI/18);
            rotateX(-PI/8);
            cylinder (6, 20);
            pop();
          } 
          
          pop();
            
          angle+=0.01;
        };
     
      }
    };
    
    let node = document.createElement('div');
    window.document.getElementById('p5-container').appendChild(node);
    new p5(sketch, node);
body {
  background-color:#e9e9e9;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.10.2/p5.js"></script>
<div id="p5-container"></div>

Since I need every cylinder to point to a different direction, I've come up with using a bunch of for loops, doing one belt of cylinders after another spaced out along Z axis, every time the loop shrinks and rotates along X and Y a bit more.

As you can see, this has resulted for me in 7 for loops. And 3 of them just have some negative values of the other 3 to complete the ball.

I'm just wondering if there's a way to write these for loops in a nested loop? I've thought through it but just couldn't think of a solution...

Or if anyone has a better way to write these cylinder loops?

Thank you!

like image 485
zzzzziji Avatar asked Nov 07 '22 09:11

zzzzziji


1 Answers

Here is a simple solution. What I have done in the following code, is that I first calculated the position of each vertex according to the number of total vertices and store them in a 2D array called globe! This will allow you to store information that can be used later in the code to draw the sphere and cylinders in a singular for-loop (each)!

I then created a sphere using triangles (in other words vertices, edges and faces) from the information stored in globe. You will notice that instead of cylinders I draw spheres. If you try changing that to cylinders it won't look very attractive. I'm not very good at mathematics but you can fix that issue by calculating the normals of each vertex. You may find this A Unit Vector (Normalize) - The Nature of Code and normalize() helpful but keep in mind that you will need to convert that from 2D to 3D!

const globe = []; //Holds a 2D array of vectors (v1,v2)
const r = 70; //Radius of circle
const total = 15; //number of vertices
let angleX = 0; //Rotate X axis
let angleY = 0; //Rotate Y axis

function setup() {
  createCanvas(300, 200, WEBGL);

  //Calculate vectors according to number of vertices given
  for (let i = 0; i < total + 1; i++) {
    globe[i] = []; //Create 2D array to hold vertices
    const lat = map(i, 0, total, 0, PI);
    for (let j = 0; j < total + 1; j++) {
      const lon = map(j, 0, total, 0, TWO_PI);
      const x = r * sin(lat) * cos(lon);
      const y = r * sin(lat) * sin(lon);
      const z = r * cos(lat);
      globe[i][j] = createVector(x, y, z); //Store vertices in 2D array
    }
  }
}

function draw(){
  background(51);
  rotateX(angleX);
  rotateY(angleY);
  fill(255);
  stroke(1);
  //Draw Sphere using globe 2D array
  for (let i = 0; i < total; i++) {
    beginShape(TRIANGLE_STRIP);
    for (let j = 0; j < total + 1; j++) {
      const v1 = globe[i][j];
      vertex(v1.x, v1.y, v1.z);
      const v2 = globe[i + 1][j];
      vertex(v2.x, v2.y, v2.z);
      vertices = [v1,v2];
    }
    endShape();
  }
  
  //Draw Cylinders
  for (i = 0; i < total; i ++) {
    for (j = 0; j < total; j ++) {
      const v1 = globe[i][j];
      const v2 = globe[i + 1][j];
      
      push();
      noStroke();
      translate(v1.x,v1.y,v1.z)
      
      normalMaterial();
      sphere(3,5);
      pop();
    }
  } 
  
  //Rotate on X & Y axes 
  angleX += 0.005;
  angleY += 0.006;
  
}
html, body {
  margin: 0;
  padding: 0;
}
canvas {
  display: block;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <script src="sketch.js"></script>
  </body>
</html>
like image 146
programming_ritual Avatar answered Nov 14 '22 12:11

programming_ritual