plotting points on a sphere

edited February 2017 in Questions about Code

So I want to plot a number of points (i.e. small spheres) on the surface of a large sphere. The formulae seems straightforward enough, though weirdly there're lot of different variations. Anyway, no matter what varient I use every time the spheres end up plotted along a 3d line, rather than forming a sphere and I can't figure out why that is. Hopefully some one can help me along. Here's the relevant section of code

for (float i = 0; i <= 100; i++) {
      Theta = (TAU/100)*i;
      Phi = (PI/100)*i;
      XYZ.x =  gear0.RX*cos(Theta)*sin(Phi);
      XYZ.y =  gear0.RY*sin(Theta)*sin(Phi); 
      XYZ.z =  gear0.RZ*sin(Phi); 
      pushMatrix();
      translate(XYZ.x, XYZ.y, XYZ.z);
      sphere(10);
      popMatrix();}
Tagged:

Answers

  • Answer ✓

    You are using I for both Theta and phi. You need a second loop around the current one and use that for phi.

  • Entire code?

    I guess tau is 0 or gear?

    It's all radians.

  • Here is a demo sketch that demonstrates two different point spheres. The fibonacci spiral approach is a bit more involved, gives relatively even coverage of the surface -- unlike z-projection of a plane of points, which is simpler to implement, but gives dense points at the pole (center) and creates unusual curved artifacts at the equator (edges).

  • Answer ✓
    float a1; 
    
    void setup() {
      size(1200, 1000, P3D);
      background(111);
      println (TAU + "=" + TWO_PI);
    }
    
    void draw() {
      PVector XYZ=new PVector();
      PVector gear0=new PVector(100, 100, 100); 
      float Theta;
    
      background(111);
      lights(); 
      translate(width/2, height/2);
    
      rotateX(a1-.2); 
      rotateY(a1); 
    
      for (float i2 = 0; i2 <= 100; i2+=4) {
    
        Theta = (TAU/100)*i2;
    
        for (float i = 0; i <= 100; i+=4) {
    
          float u = map (i, 0, 100, -1, 1);  
    
          XYZ.x =  gear0.x*cos(Theta)*sqrt(1-(u*u));
          XYZ.y =  gear0.y*sin(Theta)*sqrt(1-(u*u)); 
          XYZ.z =  gear0.z*u;
    
          pushMatrix();
          translate(XYZ.x, XYZ.y, XYZ.z);
          fill(255, 2, 2);
          noStroke(); 
          sphere(10);
          popMatrix();
        }
      }
      a1+=.01;
    }
    //
    
  • Woo thanks so much Chrisir, totally works now!! :-bd And yeah thanks for the suggestion Koog, indeed had to include a second loop.

  • i2 <= 100

    that's one too many...

    i2 < 100

    the other loop is ok though, you want it to be inclusive

  • edited February 2017

    Interesting example! Here is an interactive version to inspect that style of point sphere at different resolutions.

    Click and drag with the mouse to change the x / y sphere counts:

    float a1; 
    
    void setup() {
      size(300, 300, P3D);
      background(111);
    }
    
    void draw() { 
      background(111);
      lights();
      translate(width/2, height/2); 
      rotateX(a1-.2); 
      rotateY(a1);  
      // interactive or fixed version
      if(!mousePressed){
        longitudeSphere(100, 25, 25);
      } else {
        longitudeSphere(100, (int)(30 * mouseX/(float)width) + 1, (int)(30 * mouseY/(float)height) + 1);
      }
      a1+=.01;
    }
    //
    
    void longitudeSphere( int r, int vring, int hring ){
      // based on longitude sphere by vrtxt and Chrisir
      // this method tends to leave open top and bottom end-caps with a single point on one end
      // https:// forum.processing.org/two/discussion/20688/plotting-points-on-a-sphere
      int vstep = r/max(vring,1); // prevent divide-by-zeros if dragging out of window
      int hstep = (r*2)/max(hring,1);
      PVector point = new PVector();
      PVector gear0 = new PVector(r, r, r); 
      float Theta;
      float u;
      for (float j = 0; j < r; j+=vstep) {
        Theta = (TAU/100)*j; 
        for (float i = 0; i <= r; i+=hstep) {
          u = map (i, 0, r, -1, 1);   
          point.x =  gear0.x*cos(Theta)*sqrt(1-(u*u));
          point.y =  gear0.y*sin(Theta)*sqrt(1-(u*u)); 
          point.z =  gear0.z*u;
          pushMatrix();
          translate(point.x, point.y, point.z);
          fill(255, 0, 0);
          noStroke(); 
          sphere(5);
          popMatrix();
        }
      }
    }
    
  • edited February 2017

    Thanks everybody! I adjusted the code a little bit, back to using PI for the second loop. . made more sense to me.

    float Spheres = 150;   
        for (float T = 0; T <= Spheres; T++) {
          Theta = (TAU/Spheres)*T;
          for (float P = 0; P<=Spheres; P++) {
            Phi = (PI/Spheres)*P;
            XYZ.x =  gear0.RX*cos(Theta)*sin(Phi) + gear1.RX*cos(Theta/gear1.Ratio())*sin(Phi/gear1.Ratio());
            XYZ.y =  gear0.RY*sin(Theta)*sin(Phi) + gear1.RY*sin(Theta/gear1.Ratio())*sin(Phi/gear1.Ratio()); 
            XYZ.z =  gear0.RZ*cos(Phi) + gear1.RZ*cos(Phi/gear1.Ratio());
            pushMatrix();
            translate(XYZ.x, XYZ.y, XYZ.z);
            sphere(1);
            sphereDetail(5);
            popMatrix();
          }
        }
    

    And added another gear, making spirograph in 3d.

  • That's amazing, absolutely beautiful!

    Mind to share your code?

    Chrisir

  • edited February 2017

    Thanks! As for code, it's part a larger project, basically a rudimentary animator for 2d spirograph which is already working and now I'm trying to squeeze the 3d part into existing structure and UI. . All this to say it's a bit messy at the moment. :D

  • Answer ✓

    for (float T = 0; T <= Spheres; T++) {

    this is still doing one too many loops. use T < Spheres.

    at the start it'll be 0, it'll end at Spheres (i'd look at java naming conventions btw)

    Theta = (TAU / Spheres) * T;

    when T = 0 => Theta = 0

    when T = Spheres => Theta = TAU

    cos(0) is the same as cos(TAU), ditto sin().

    the last spheres are the same as the first so you don't see them...

  • edited February 2017

    Oh right, thanks! Still getting used to 0 = 1, if that makes sense. Banged my head many a time getting 'out of bounds' errors because of that. . Anyhow, adjusted it and will look into naming conventions as well.

  • that looks neat as a screen saver :

    float a1, a2; 
    PVector gear0=new PVector(100, 100, 100); 
    PVector gear1=new PVector(100, 100, 100); 
    
    float gear1Ratio = 100; 
    
    float m1x, m1xAdd=1;
    float m1y, m1yAdd=1;
    float m1z, m1zAdd=1;
    
    void setup() {
      fullScreen(P3D); // size(1300, 1000, P3D);
      background(111);
      //println (TAU + "=" + TWO_PI);
      sphereDetail(8);
    }
    
    void draw() { 
      background(111);
    
      lights();
    
      translate(width/2, height/2); 
      rotateX(a1-.2); 
      rotateY(a2);  
      // interactive or fixed version
    
      //
      // scale(1.5);
    
      float Spheres = 150;  
      float Theta;
      float Phi; 
      PVector XYZ=new PVector();  
      gear1Ratio=map(m1x, 0, width, 0, 1);
      gear1.x=m1x;
      gear1.y=m1y;
      gear1.y=m1z;
      // gear1.y= map(mouseY, 0, height, 0, 1);
      for (float T = 0; T <= Spheres; T++) {
        Theta = (TAU/Spheres)*T;
        fill(255, 2, T);
        for (float P = 0; P<=Spheres; P++) {
          Phi = (PI/Spheres)*P;
          XYZ.x =  gear0.x*cos(Theta)*sin(Phi) + gear1.x*cos(Theta/gear1Ratio)*sin(Phi/gear1Ratio);
          XYZ.y =  gear0.y*sin(Theta)*sin(Phi) + gear1.y*sin(Theta/gear1Ratio)*sin(Phi/gear1Ratio); 
          XYZ.z =  gear0.z*cos(Phi) + gear1.z*cos(Phi/gear1Ratio);
          pushMatrix();
          translate(XYZ.x, XYZ.y, XYZ.z);
          noStroke(); 
          sphere(6);
          popMatrix();
        }
      }
    
      m1x+=m1xAdd;
      m1y+=m1yAdd;
      m1z+=m1zAdd;
    
      if (m1x>width) m1xAdd=-abs(m1xAdd+random(-.12, 12)); 
      if (m1y>height) m1yAdd=-abs(m1yAdd+random(-.12, 12));
      if (m1z>height) m1zAdd=-abs(m1zAdd+random(-.12, 12));
    
      if (m1x<0) m1xAdd=abs(m1xAdd+random(-.12, 12)); 
      if (m1y<0) m1yAdd=abs(m1yAdd+random(-.12, 12));
      if (m1z<0) m1zAdd=abs(m1zAdd+random(-.12, 12));
    
      a1+=.02; 
      a2+=.002;
    }
    
  • edited February 2017

    Would be totally neat! And from the inside can get pretty interesting shapes as well, just add more gears to it. ;)

  • did you try my sketch? Looks awesome...

    I just dive into this...

    @vrtxt : you wrote

    back to using PI for the second loop. . made more sense to me.

    so without adding another gear, does this look correct to you?

    float a1; 
    
    void setup() {
      size(1200, 1000, P3D);
      background(111);
      println (TAU + "=" + TWO_PI);
    }
    
    void draw() {
      PVector XYZ=new PVector();
      PVector gear0=new PVector(100, 100, 100); 
      float Theta;
    
      background(111);
      lights(); 
      translate(width/2, height/2);
    
      rotateX(a1-.2); 
      rotateY(a1); 
    
      for (float i2 = 0; i2 <= 100; i2+=4) {
    
        Theta = (TAU/100)*i2;
    
        for (float i = 0; i <= 100; i+=4) {
    
          float PHI = map (i, 0, 100, 0, TWO_PI);  
    
          XYZ.x =  gear0.x*cos(Theta)*sin(PHI);
          XYZ.y =  gear0.y*sin(Theta)*sin(PHI); 
          XYZ.z =  gear0.z*cos(PHI);
    
          pushMatrix();
          translate(XYZ.x, XYZ.y, XYZ.z);
          fill(255, 2, 2);
          noStroke(); 
          sphere(3);
          popMatrix();
        }
      }
      a1+=.01;
    }
    //
    
  • for (float i2 = 0; i2 <= 100; i2+=4) {

    !!!

  • @vrtxt: in the small 3D video though which parameters did you change there in which way....?

  • edited February 2017

    yes I ran your sketch @Chrisir, hence my remarks to explore the inside as well. :) In the video I changed the x,y,z dimensions respectively of gear0 which I made available in the GUI, albeit very quick n dirty.

    Also, I used PI rather than TWO_PI (which is just TAU) because according to this here it only needs PI, which kinda makes sense. I'm guessing by using TWO_PI every dot is plotted twice, and also not sure why you're mapping it.

Sign In or Register to comment.