P3D Vector Spherical Geometry Ball

I want to create a Ball that could go bigger and smaller according to the Music Frequency. (I don’t want to use the sphere(), because it doesn’t create the dot points)
I made it work. But, it is a little bit laggy. At the moment, I could reduce the rows and cols or decrease the player analysis rate to relieve the situation. But, it is still laggy.
I think the main program is the three loops that I had. I tried to minimize them, but I have no idea what I can do.

Just a little bit more, instead of making the whole ball bigger and smaller. If there is anyway that I could make them like a “wave”? Something like perlin noise.

/***** IMPORT LIBRARIES *****/
import peasy.*;               // Import Peasy Cam Library. Peasy Cam is a camera library. 
import ddf.minim.analysis.*;  // Import Minim Music Library
import ddf.minim.*;           // Import Minim Music Library

/***** Define Some variables ****/
PeasyCam cam;
Minim       minim;
AudioPlayer player;
FFT         fft;

PVector[][]  globe;           // Create a Vector array to save all
int total = 50;               // Rows and Cols of the ball
float angle;                  // Auto Rotate Ball
float c;                      // HSB Dynamic Color

void setup()
{
  size(displayWidth, displayHeight, P3D); // Windows Size Not Correct
  cam = new PeasyCam(this, 600);          // Cam Size 600. Initial
  minim = new Minim(this);                // Initial Minim

  globe = new PVector[total+1][total+1]; 
  player = minim.loadFile("test.mp3", 128);
  player.loop();
  fft = new FFT( player.bufferSize(), player.sampleRate() );
}

void draw()
{
  /***** Basic Setups *****/
  background (0);
  fft.forward( player.mix ); // Play the music forward

  /***** START DRAWING THE BALL *****/
  angle += PI/18;             // + 10 Degree 
  rotateX(PI/2);              // Rotate the Ball along the X 
  rotateY(angle);             // Rotate the Ball along the Y with 10 degree 
  colorMode(HSB);             // Set Color Mode to HSB
  if (c >= 255)               // Increase the c 
  {
    c = 0;
  } else
  {
    c++;
  }
  stroke(c, 255, 255);         // Hue/Saturation/Brightness. Change the HUE and keep Saturation and Brighness 255
  float r = 200;               // Radius of the ball
  for (int a = 0; a < fft.specSize(); a++)
  {
    /*****
    
     Complicated Spherical Geometry Calculation
     From YouTube The Coding Train: Spherical Geometry.
     
     *****/
    for (int i = 0; i < total+1; i++)
    {
      float lon = map(i, 0, total, -PI, PI);
      for (int j = 0; j < total+1; j++)
      {

        float lat = map(j, 0, total, -HALF_PI, HALF_PI);
        float x = r * sin(lon) * cos(lat);
        float y = r * sin(lon) * sin(lat);
        float z = r * cos(lon);
        globe [i][j] = new PVector(x, y, z);

        /*****
         Returns the Perlin noise value at specified coordinates. 
         Perlin noise is a random sequence generator producing a more natural, 
         harmonic succession of numbers than that of the standard random() function. 
         *****/
        point(x*noise(fft.getFreq(a)), y*noise(fft.getFreq(a)), z*noise(fft.getFreq(a)));
      }
    }
  }
}

65 * 51 * 51 loop might give you that LAG

try more easy first:

import peasy.*;               // Import Peasy Cam Library camera library. 
import ddf.minim.*;           // Import Minim Music Library
import ddf.minim.analysis.*;

PeasyCam cam;
Minim       minim;
AudioPlayer player;
FFT         fft;

void setup_sound() {
  minim = new Minim(this);
  player = minim.loadFile("groove.mp3", 1024);
  player.loop();
  fft = new FFT( player.bufferSize(), player.sampleRate() );
  println("fft.specSize: "+fft.specSize());
}

int total = 25;
float r0 =30, zoom = 0.5;

void setup() {
  size(500, 500, P3D);
  stroke(200, 0, 0);  //noStroke();
  strokeWeight(5);
  noFill();
  setup_sound();
  cam = new PeasyCam(this, 100);
}

void draw() {
  background(200, 200, 0);
  fft.forward( player.mix );
  surface.setTitle(nf(frameRate, 0, 1)+"FPS");
  ball();
}

void ball() {
  float r=0;
  for (int i = 0; i < total; i++) {
    float lon = map(i, 0, total, -PI, PI);
    for (int j = 0; j < total; j++) {
      r = r0 + zoom * fft.getBand(j);//(i+i*j);
      float lat = map(j, 0, total, -HALF_PI, HALF_PI);
      float x = r * sin(lon) * cos(lat);
      float y = r * sin(lon) * sin(lat);
      float z = r * cos(lon);
      point(x, y, z);
    }
  }
}


works here ( Win10 / Processing 3.5.3 / medium hardware ) with 50 FPS
i try stay as close as possible to your example, but actually not get why the
sound analysis FFT must again mixed with noise, so i skipped that

1 Like

A lot of stuff is constant and could be pre-calculated in setup ().

Eg lon
lat
and sin * cos

Hence you can store them in a class (Arraylist of it) and derive from there. Especially map and sin and cos are slow. It’s called look up table. Can be faster.

Only r is really variable

1 Like

I made one version of this idea with a class and an ArrayList of it (not showing here).

Then I shortened this to an Array of PVector:


// https://discourse.processing.org/t/p3d-vector-spherical-geometry-ball/14083/3

import peasy.*;               // Import Peasy Cam Library camera library. 
import ddf.minim.*;           // Import Minim Music Library
import ddf.minim.analysis.*;  // Import 

PeasyCam    cam;
Minim       minim;
AudioPlayer player;
FFT         fft;

int total = 25;
float r0 =30, 
  zoom = 0.5;

// ArrayList<Data> data = new ArrayList();
PVector[] data = new PVector [total*total];

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

void setup() {
  size(500, 500, P3D);
  stroke(200, 0, 0);  //noStroke();
  strokeWeight(5);
  noFill();
  setup_sound();
  cam = new PeasyCam(this, 100);
}

void draw() {
  background(200, 200, 0);
  fft.forward( player.mix );
  surface.setTitle(nf(frameRate, 0, 1)+" FPS");
  // text (int(frameRate)+" FPS", 33, 33);
  ball();
}

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

void ball() {
  // runs on and on 
  // uses data 
  int j=0;
  float r, x, y, z; 
  for (PVector myData : data) {
    r = r0 + zoom * fft.getBand(j);//(i+i*j);
    x = r * myData.x;
    y = r * myData.y;
    z = r * myData.z;
    //
    point(x, y, z);
    j++;
  }//for
}//func 

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

void setup_sound() {
  // runs only ONCE 
  minim = new Minim(this);
  player = minim.loadFile("groove.mp3", 1024);
  if (player!=null) { 
    // success
    player.loop();
    fft = new FFT( player.bufferSize(), player.sampleRate() );
    println("fft.specSize: "+fft.specSize());
    ball_Init();
  } else {
    // error 
    println("Abort");
    exit();
  }//else
}//func

void ball_Init() {
  // runs only ONCE 
  // stripped down version of the old ball to fill the Array data 
  // float r=0;
  int k=0; 
  for (int i = 0; i < total; i++) {
    float lon = map(i, 0, total, -PI, PI);
    for (int j = 0; j < total; j++) {
      //r = r0 + zoom * fft.getBand(j);//(i+i*j);
      float lat = map(j, 0, total, -HALF_PI, HALF_PI);
      //float x = r * sin(lon) * cos(lat);
      //float y = r * sin(lon) * sin(lat);
      //float z = r * cos(lon);
      PVector preparation = new PVector(0, 0, 0);
      preparation.x = sin(lon) * cos(lat);
      preparation.y = sin(lon) * sin(lat);
      preparation.z = cos(lon);
      data[k] = preparation.copy();  
      // point(x, y, z);
      k++;
    }
  }
}
//
1 Like

I have to admit my version is crab.

It’s not faster but it also looks bad, much much worse than yours.

Which is strange, it must be because I store the results in PVector which maybe has not the precision Java works with internally (double?).

E.g. in this line: float x = r * sin(lon) * cos(lat);

I could try to store as double… see below.

still looks crab. The amplitude is much smaller than in your sketch

I am sorry I couldn’t be of help

Chrisir

(version where I unsuccessfully store as double (in a class))


// https://discourse.processing.org/t/p3d-vector-spherical-geometry-ball/14083/3

import peasy.*;               // Import Peasy Cam Library camera library. 
import ddf.minim.*;           // Import Minim Music Library
import ddf.minim.analysis.*;  // Import 

PeasyCam    cam;
Minim       minim;
AudioPlayer player;
FFT         fft;

int total = 25;
float r0 = 30, 
  zoom = 0.5;

ArrayList<Data> data = new ArrayList();

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

void setup() {
  size(500, 500, P3D);
  stroke(200, 0, 0);  //noStroke();
  strokeWeight(5);
  noFill();
  setup_sound();
  cam = new PeasyCam(this, 100);
}

void draw() {
  background(200, 200, 0);
  fft.forward( player.mix );
  surface.setTitle(nf(frameRate, 0, 1)+" FPS");
  // text (int(frameRate)+" FPS", 33, 33);
  ball();
}

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

void ball() {
  // runs on and on 
  // uses ArrayList<Data> data 
  int j=0;
  double r, x, y, z; 
  for (Data myData : data) {
    r = r0 + zoom * fft.getBand(j);//(i+i*j);
    x = r * myData.preparationX;
    y = r * myData.preparationY;
    z = r * myData.preparationZ;
    //
    point((float)x, (float) y, (float) z);
    j++;
  }//for
}//func 

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

void setup_sound() {
  // runs only ONCE 
  minim = new Minim(this);
  player = minim.loadFile("groove.mp3", 1024);
  if (player!=null) { 
    // success
    player.loop();
    fft = new FFT( player.bufferSize(), player.sampleRate() );
    println("fft.specSize: "+fft.specSize());
    ball_Init();
  } else {
    // error 
    println("Abort");
    exit();
  }//else
}//func

void ball_Init() {
  // runs only ONCE 
  // stripped down version of the old ball to fill the ArrayList<Data> data 
  // float r=0;
  for (int i = 0; i < total; i++) {
    float lon = map(i, 0, total, -PI, PI);
    for (int j = 0; j < total; j++) {
      //r = r0 + zoom * fft.getBand(j);//(i+i*j);
      float lat = map(j, 0, total, -HALF_PI, HALF_PI);
      //float x = r * sin(lon) * cos(lat);
      //float y = r * sin(lon) * sin(lat);
      //float z = r * cos(lon);
      Data newData = new Data(); 
      newData.preparationX = sin(lon) * cos(lat);
      newData.preparationY = sin(lon) * sin(lat);
      newData.preparationZ = cos(lon);
      data.add(newData); 
      // point(x, y, z);
    }
  }
}

// ================================================================
// classes 

class Data {
  double preparationX = 0;
  double preparationY = 0;
  double preparationZ = 0;
}//class 
//
1 Like

Have you thought about creating a single sphere of fixed size and then use scale when rendering it.

no, my idea was to map the FFT on the sphere:

just to change its size / scale, BUT
fft.specSize() * frameRate /sec
is too heavy and ?invisible? and anyhow much like the BEAT example

@Chrisir test switchable
import peasy.*;               // Import Peasy Cam Library camera library. 
import ddf.minim.*;           // Import Minim Music Library
import ddf.minim.analysis.*;

PeasyCam cam;
Minim       minim;
AudioPlayer player;
FFT         fft;

void setup_sound() {
  minim = new Minim(this);
  player = minim.loadFile("groove.mp3", 1024);
  player.loop();
  fft = new FFT( player.bufferSize(), player.sampleRate() );
  println("fft.specSize: "+fft.specSize());
}

int total = 50;
float r0 =30, zoom = 0.2;
float[][][] ksphere = new float[total][total][3];
boolean atChrisir = false; //true;

void setup_ksphere() {                                    // @Chrisir
  for (int i = 0; i < total; i++) {
    float lon = map(i, 0, total, -PI, PI);
    for (int j = 0; j < total; j++) {
      float lat = map(j, 0, total, -HALF_PI, HALF_PI);
      ksphere[i][j][0] = sin(lon) * cos(lat);
      ksphere[i][j][1] = sin(lon) * sin(lat);
      ksphere[i][j][2] = cos(lon);
    }
  }
}

void setup() {
  size(500, 500, P3D);
  stroke(200, 0, 0);  //noStroke();
  strokeWeight(5);
  noFill();
  setup_sound();
  if ( atChrisir ) setup_ksphere();
  cam = new PeasyCam(this, 100);
}

void draw() {
  background(200, 200, 0);
  fft.forward( player.mix );
  surface.setTitle(nf(frameRate, 0, 1)+"FPS");
  ball();
}

void ball() {
  float x=0, y=0, z=0, lon=0, lat=0, r=0;
  for (int i = 0; i < total; i++) {
    if ( !atChrisir ) lon = map(i, 0, total, -PI, PI);
    for (int j = 0; j < total; j++) {
      r = r0 + zoom * fft.getBand(j);//(i+i*j);
      if ( !atChrisir ) { 
        lat = map(j, 0, total, -HALF_PI, HALF_PI);
        x = r * sin(lon) * cos(lat);
        y = r * sin(lon) * sin(lat);
        z = r * cos(lon);
      } else {
        x = r * ksphere[i][j][0];
        y = r * ksphere[i][j][1];
        z = r * ksphere[i][j][2];
      }
      point(x, y, z);
    }
  }
}

You are super awesome!!! That’s really useful! It works on my laptop as well. Also, if I want to create the ball surface like wave/mountain shape. Do you have any ideas about it? Is it even possible? A surface like this: https://www.youtube.com/watch?v=IKB1hWWedMk. Maybe. (Don’t worry about the Triangle strips)

The complicated part is how to match the FFT with the vertex. So, instead of defining the r in your example. I might need to define the point directly. What do you think? >_<

I think it is going to be a little bit complicated. You don’t really need to give me the whole code. Just some thought should be enough.

1 Like

no, but he has:
Coding Challenge #25: Spherical Geometry - YouTube ,
watch spec. 13 … 24 minutes