What's the equivalent of using cos() and sin() to find points on the edge of a circle to find points on a sphere?

When putting points on the edge of a 2D circle you can do the following.

let r = 50;
for(i = 0; i < 360; i ++){
ellipse(cos(i) * r, sin(i) * r,3);
}

There has to be an equivalent for spheres without cheating it right?

1 Like


// https://discourse.processing.org/t/i-want-the-pvector-after-rotate/30744
// https://discourse.processing.org/t/i-want-the-pvector-after-rotate/30744/7

import peasy.*;

PeasyCam pCamera;

ArrayList<PVector> list = new ArrayList(); 

PVector v = new PVector();

PVector center, 
  myPV, myPV2;

void setup() {
  size(800, 800, P3D);

  center = new PVector(0, 0, 0);

  pCamera = new PeasyCam(this, 300);

  v = new PVector(40, 0, 0);

  for (int i=0; i<=360; i+=10) {
    makeCircle(v); 
    rotateY(radians(10));
  }
}

void draw() {
  background(255);
  lights();

  fill( 0, 0, 255); 
  spherePV(center, 4);

  fill( 255, 0, 0);
  for (PVector pv : list) {
    spherePV(pv, 2);
  }
}

//--------------------------------------------------------------------------------------

void makeCircle(PVector pv) {
  for (int i = 3; i<360; i+=17) { 
    pushMatrix();  

    rotateZ(radians(i));

    translateAndAddToList(pv); 

    popMatrix();
  }//for
}
//--------------------------------------------------------------------------------------

void translateAndAddToList(PVector pv) {
  pushMatrix();  
  translatePV(pv); 
  PVector myPV = new PVector(modelX(0, 0, 0), modelY(0, 0, 0), modelZ(0, 0, 0) );
  list.add(myPV); 
  popMatrix();
}

void translatePV(PVector pv) {
  // one nice wrapper for build in translate-command
  translate(pv.x, pv.y, pv.z);
}

void spherePV(PVector pv, 
  float size1) {
  // one nice wrapper for build in sphere-command
  pushMatrix();
  noStroke(); 
  // fill(255, 0, 0); 
  translate(pv.x, pv.y, pv.z);
  sphere(size1);
  popMatrix();
}

p5js, Isn’t there a format with two for loops maybe?

1 Like

There are 2 for loops

@olaf000olaf You can think of a sphere as a whole bunch of circles stacked on top of each other, the circles near the middle have a radius from the axis of the sphere that is nearly equal to the radius of the sphere, the circles near the poles have a radius from the axis that approaches zero. The curve that the relationship of the radius of each circle to the height in the sphere follows is the same as the curve for a circle. In order to map angles to points on a sphere you need two angles: elevation (from 90° straight up to -90° straight down), and the azimuth (from 0° to 360°). To find the Cartesian coordinates for the point on a sphere given those angles you can first find the radius of the circle at that elevation (circle_radius = sphere_radius * cos(elevation)) and the y component of the vector (y = sphere_radius * sin(elevation)). Then you can use the circle_radius and the azimuth angle to find the x and z components (x = circle_radius * cos(azimuth) and z = circle_radius * zin(azimuth).

Here’s a quick example:

const radius = 100;

function setup() {
	createCanvas(windowWidth, windowHeight, WEBGL);
	angleMode(DEGREES);
}

function draw() {
	orbitControl(2, 1, 0.05);
	background(0);
	strokeWeight(2);
	stroke('lime');
	for (let e = -80; e <= 80; e += 10) {
		let cr = radius * cos(e);
		let y = radius * sin(e);
		for (let a = 0; a < 360; a += 20) {
			let x = cr * cos(a);
			let z = cr * sin(a);
			point(x, y, z);
		}
	}
}
2 Likes

here is a kind of firework in 3D

(sorry, KumuPaul, I saw your post too late)





// 3D sketch : Fireworks 

// You can see the sketch as a stage where you can place different Main Items on. 
// In this case the Main Item is Fireworks.

// To change the  Main Item, see initMainItem() and showMainItem() in their two tabs.
// Other elements are pure decoration, like clouds, trees, CSG etc. 

//You can also switch Video Export on and off. See the variable videoExportIsOn. 

// https://youtu.be/xNfS3uOd6P8
// https://youtu.be/46qwczx1jx8

import com.hamoid.*;


final int GENERAL_Y_HEIGHT = 500; 

ArrayList<Spark> sparks = new ArrayList();
int divider=2;

// the PShape reference which will contain the converted 
PShape csgResult;

// ***************************************************************************
boolean videoExportIsOn = true;// ******************************************** 
// ***************************************************************************

VideoExport videoExport;

PVector   camPos    = new PVector(width/2.0, height/2.0, 990);
PVector  camLookAt = new PVector(width/2.0, height/2.0, -600);
PVector  camUp     = new PVector( 0, 1, 0 );

// -----------------------------------------------------

void setup() {
  size (1400, 800, P3D);
  sphereDetail(18);  

  initExplosion() ; 

  // hamoid video export
  if (videoExportIsOn) {
    videoExport = new VideoExport(this);
    println( "Start with s // Use e for  videoExport.endMovie");
    videoExport.startMovie();
  } else {
    println( "videoExport IS OFF  +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
  }
} // func 

void draw() {
  // clear & prepare canvas 
  background(220);
  lights();

  // apply vectors to actual camera
  camera (camPos.x, camPos.y, camPos.z, 
    camLookAt.x, camLookAt.y, camLookAt.z, 
    camUp.x, camUp.y, camUp.z);

  // ***************************************************************************
  // show Main Item - the core of draw() 
  showMainItem(); 
  // ***************************************************************************

  // show center
  //  scene.showCenters(); // two spheres  

  // text upper left corner
  fill(0, 255, 0); 

  //// hamoid video export
  //if (videoExportIsOn) {
  //  videoExport.saveFrame();
  //}
  //
} // func 

void initExplosion() {

  float extensionHalf=500;

  int colType=int(random (7));

  float x= random(-extensionHalf, extensionHalf); 
  float y= random(-44, GENERAL_Y_HEIGHT-222);  //     random(33, 220);
  float z= random(-extensionHalf, extensionHalf);  
  for (int i=0; i<990; i++) {
    Spark spark = new Spark(width/2, height-4, 4, 0);
    sparks.add(spark); 
    spark.startSpark( x, y, z, 
      colType );
  }
}

void showMainItem() {

  // main item of the sketch 

  // manage sparks / shrapnels 
  if (keyPressed) {
    noStroke();
  }

  // The core: 
  for (Spark spark : sparks) {
    spark.move();
    spark.display();
    spark.script();
  }

  // remove dead ones 
  for (int i=sparks.size()-1; i>=0; i--) {
    if (sparks.get(i).isDead) 
      sparks.remove(i);
  }

  // spawn new 
  if (frameCount%divider == 0) {
    initExplosion();
    divider=int(random(53, 133));
  }
}
//


void endSketch() {
  // hamoid video export 
  if (videoExportIsOn) {
    videoExport.endMovie();
    exit();
  }
}

// ---------------------------------------------------------------------------------------

void myBox(float x, float y, float z, // pos
  float w, float h, float d) {        // size 

  // nice wrapper for build in box-command
  //w   float: dimension of the box in the x-dimension
  //h   float: dimension of the box in the y-dimension
  //d   float: dimension of the box in the z-dimension

  pushMatrix();
  translate(x, y, z);
  box(w, h, d);
  popMatrix();
}

void mySphere(float x, float y, float z, 
  float size1) {
  // nice wrapper for build in sphere-command
  pushMatrix();
  translate(x, y, z);
  sphere(size1);
  popMatrix();
}

String videoExportIsOnToString() {
  if (videoExportIsOn) 
    return " with Video Export.";
  else return " no Video Export.";
}
//

//=====================================================




class Spark {

  float x, y, z; // current position 
  float x1, y1, z1; // initial position 
  float diameter; // diameter  

  color col;

  //states 
  final int stateWait=0; // unique numbers  
  final int stateTriggered=1;
  final int stateJump=2;
  int stateSpark = stateWait;

  int timer;
  int duration; 

  boolean isDead=false; 

  float xadd, yadd, zadd;

  // new approach 
  PVector gear0=new PVector(0, 0, 0); 

  float Theta;
  float u; 

  // new approach 
  float Tau100 =  (TAU/100);  
  float i2=random(100); 
  float i3=random(100);

  // Constructor
  Spark(float x_in, float y_in, float z_in, 
    float diameter_in) {

    x = x_in;
    y = y_in;
    z = z_in;

    xadd=0;
    yadd=0;
    zadd=0;

    diameter = diameter_in;
  }// Constructor 

  void move() {                                              

    // Spark move 

    Theta = Tau100 * i2;
    u = map (i3, 0, 100, -1, 1);

    x = x1+ gear0.x*cos(Theta)*sqrt(1-(u*u));
    z = y1+ gear0.y*sin(Theta)*sqrt(1-(u*u)); 
    y = z1+ gear0.z*u;

    gear0.add(xadd, yadd, zadd);

    // it dies 
    if (diameter<=0.25)
      isDead=true;

    diameter -= 0.041;  // how fast it shrinks / and then dies
  } // function 

  // start spark
  void startSpark( float x_, float y_, float z_, 
    int colType ) {
    // duration for this spark to start after ignition 
    duration=int(random(4, 379)); // int(random(40, 190));
    stateSpark=stateTriggered;

    x1=x_;
    y1=y_;
    z1=z_;

    diameter=random(1.5, 3.7);

    switch (colType) {
    case 0:
      col = color(random(256), random(256), random(256));
      break; 

    case 1:
      col =  color(random(256), 0, 0);
      break; 

    case 2:
      col =  color(random(256));
      break;

    case 3:
      col =  color(random(254, 256));
      break;

    case 4:
      // blue or white 
      if (random(100)>50) 
        col =  color(0, 0, random(254, 256));//blue
      else col =  color(random(254, 256)); // white
      break;

    case 5:
      // A or B 
      if (random(100)>50) 
        col = color(random(50, 256), random(50, 256), random(50, 256));
      else col = color(random(256), random(256), random(256));
      break;

    default:
      col = color(random(33), random(22), random(256));
      break;
    }//switch 

    timer=millis();
  }

  void script() {

    switch(stateSpark) {

    case stateWait:
      // wait
      break;

    case stateTriggered:
      if (millis()-timer>duration)
        stateSpark=stateJump;
      break;

    case stateJump:
      startExplosion();
      stateSpark=0;
      break;

    default:
      println("Error 114");
      exit();
      break;
    }//switch
  }//method

  void startExplosion() {
    //starts explosion 
    //  radius_Add=random(2.1, 9.4);
    xadd=random(2.4, 3.3) ; 
    yadd=random(2.4, 3.3) ; 
    zadd=random(2.4, 3.3) ; 

    if (random(100)>50)
      xadd*=-1;
    if (random(100)>50)
      yadd*=-1;
    if (random(100)>50)
      zadd*=-1;
  }// function 

  void display() {
    // Spark display 
    pushMatrix();
    translate(x, y, z);
    fill(col);
    //if (!keyPressed)
    // ellipse(x, y, diameter, diameter);
    // else
    sphere(diameter);
    popMatrix();
    //
  }// function 
  //
}//class
//

Here is an example that adds a bit more visualization for where the trig components fit in: Trig Sphere - OpenProcessing

It’s all just triangles all the way down.

2 Likes

No problem, but I’m confused. The OP is asking about math for positions on a sphere and p5.js and you’re sharing complicated sketches written in Processing that happen to include some spherical coordinate math. As cool as that sketch may be I’m not sure it is particularly helpful to the OP.

2 Likes

That’s a good point! I thought he could just copy the relevant lines and translate them?

Hello,

I found this to be a good reference:

A Processing Java version implementing above:

Code
 float theta;  // θ
 float phi;    // φ
 float x, y, z;
 int r = 100; 

// TAU = TWO_PI in radians which is 360 degrees
 
 void sphere01()
   {
   for(int j = 0; j<=8; j++)  //theta loop
    {
    theta = j*(TAU/2)/8;
    
    for(int i = 0; i<32; i++) //phi loop
      {
      phi = i*TAU/32;
      
      // From Wikipedia
      //x = r*cos(phi)*sin(theta);
      //y = r*sin(phi)*sin(theta);
      //z = r*cos(theta);

      // Swapped z and y axes
      x = r*cos(phi)*sin(theta);
      z = r*sin(phi)*sin(theta);
      y = r*cos(theta);      
      
      strokeWeight(4);
      stroke(255, 0, 0);
      point(x, y, z);     
      }
    }
   } 

You can optimize by putting the theta calculations in the inner loop to the outer loop.

I tend to work directly with radians; that is just a personal choice.

@KumuPaul has the optimized version at first glance.

image

:)

2 Likes

For 2D there is a fast algorithm, 3D I don’t know:
https://www.vicanek.de/articles/QuadOsc.pdf