Loading...
Logo
Processing Forum
Hey guys!

I have coded a simple autonomous agents 3D environment and I trigger some events based on a "counter" a variable that takes integer values from 0 to 180. There is a public class for the basics and some subclasses that define some additional parameters for each group of agents. Every agent is being represented as a 3D shere. 

What I want to do is to create something like a trail of spheres (for each agent) that depicts its path while flying. Something like this but in 3D: 

import controlP5.*;
ControlP5 controlP5;
int time;

boolean playPause = true; // true = play, false = pause
void play() { playPause = !playPause; } // toggle

void setup() {
  size(500,500);
  frameRate(30);
  smooth();
  controlP5 = new ControlP5(this);
  controlP5.addSlider("time",0,width,10,height-30,width-75,20);
  controlP5.addButton("play",0,width-40,height-30,30,20);
}

void draw() {
  background(125);

  if (playPause) { time++; controlP5.controller("play").setLabel("Pause"); }
  else { controlP5.controller("play").setLabel("Play"); }

  if (time >= width) { time = 0; } // reset when right side is reached (only useful for this particular sketch)
  controlP5.controller("time").setValue(time); // set the slider to time value

  randomSeed(1); // controls the randomness, to get the same output every time
  for (int i=0; i<time; i++) { // the time variable controls the output
    float c = map(i,0,time,0,255);
    fill(c,c);
    ellipse(i%width,random(height),40,40);
  }
}

So I tried to implement the map() function but the results are wrong. Any ideas how I can use it? Here is one of the subclasses and the public class:

class FW1 extends Capsule { // subclass: female_worker_single

  FW1(float x_fw1, float y_fw1, float z_fw1, color c_fw1, int nam_fw1) {
    super(x_fw1,y_fw1,z_fw1,c_fw1,nam_fw1);
  }
  
  void displayINFO() {
    advance();
    shapes2D();
  }
  
  void advance(){
    angle += angleVel; 
  }  
  
  void display() {
    fill(0,255,0);
    noStroke();
    pushMatrix();
    translate(location.x, location.y, location.z);
    sphere(r/2);
    
    float c = map(counter,0,180,0,255);
    translate(counter%location.x, counter%location.y, counter%location.z);
    fill(c,c);
    sphere(r/2);
    
    popMatrix();
  }
  
  void shapes2D(){  
    
    fill(0,255,0);
    noStroke();
    pushMatrix();
    translate(location.x, location.y, location.z);
    sphere(r/2);
    float[] rota = cam.getRotations();
    rotateX(rota[0]);
    rotateY(rota[1]);
    rotateZ(rota[2]);
    
    fill(160);
    String myText = "ID: female-worker-single" + "\nx: " + location.x + "\ny: " + location.y + "\nz: " + location.z;
    textSize(3);
    text(myText,location.x/4,location.y/6, location.z/10);
    
    stroke(160); fill(0,0,0,0);strokeWeight(1);
    ellipse(0,0,8,8);
    
    stroke(0,255,0); fill(0,255,0);strokeWeight(0.3);
    line(-4,-6,-10,-12);
    line(-10,-12,-16,-12);
    ellipse(-4,-6,1,1);
    text("buffer zone", -25, -12);
    
    rotate(angle);
    stroke(0,255,0); fill(0,0,0,15);strokeWeight(0.3);
    ellipse(0,0,18,18);
    line(0,10,0,12);
    line(0,-8,0,-10);
    line(-8,0,-10,0);
    line(8,0,10,0);
    popMatrix();
  }
}







abstract class Capsule {  //the super-class that defines the chararacteristics of the agents

  PVector location;
  PVector velocity;
  PVector acceleration;
  float r;  // the radius of the circles-spheres/ agents
  float wandertheta, wanderphi, wanderpsi;
  float maxforce;    // Maximum steering force
  float maxspeed;    // Maximum speed
  color fillColor;  // differentiation of agent types are based on complementary coloring
  int name;  // gives an ID - number to each different agent
  float angle, angleVel; 
  
  Capsule(float x, float y, float z, color c, int nam) {  // the class constructor
    acceleration = new PVector(0,0,0);
    velocity = new PVector(0,0,0);
    location = new PVector(x,y,z);
    r = 6;
    wandertheta = 0;
    wanderphi = 0;
    wanderpsi = 0;
    maxspeed = 0.5;
    maxforce = 0.05;
    fillColor = c;
    name = nam;
    angle=random(0, 6.28);
    angleVel = random(-0.1,0.1);
  }
 
  void update() {  // Method to update location
    velocity.add(acceleration);  // Update velocity
    velocity.limit(maxspeed);  // Limit speed
    location.add(velocity);  // Reset accelertion to 0 each cycle
    acceleration.mult(0);
  }

  void wander() {  // Method that makes the agent float into 2D space
    float wanderR = 25;  // Radius for our "wander circle"
    float wanderD = 200;  // Distance for our "wander circle"
    float change = 0.05;
    wandertheta += random(-change,change);  // Randomly change wander theta

    // calculating the new location to steer towards on the wander circle
    PVector circleloc = velocity.get();    // Start with velocity
    circleloc.normalize();   // Normalize to get heading
    circleloc.mult(wanderD);  // Multiply by distance
    circleloc.add(location);  // Make it relative to boid's location
    
    float h = velocity.heading2D();  // We need to know the heading to offset wandertheta

    PVector circleOffSet = new PVector(wanderR*cos(wandertheta+h),wanderR*sin(wandertheta+h));
    PVector target = PVector.add(circleloc,circleOffSet);
    seek(target);
  }  
  
  PVector wander3D() {
     float wanderR = 30.0;         // Radius for our "wander circle"
     float wanderD = 30.0;         // Distance for our "wander circle"
     float change = 0.5;
   
     wandertheta += random(-change, change);     // Randomly change wander theta
     wanderphi += random(-change, change);       // Randomly change wander phi
     wanderpsi += random(-change, change);       // Randomly change wander psi
     
     PVector circleloc = velocity.get();  // Start with velocity
     circleloc.normalize();            // Normalize to get heading
     circleloc.mult(wanderD);          // Multiply by distance
     circleloc.add(location);               // Make it relative to boid's location

     PVector circleOffSet = new PVector(wanderR*cos(wandertheta),wanderR*sin(wanderphi), wanderR*cos(wanderpsi));
     PVector target = PVector.add(circleloc,circleOffSet);
     return target; 
  }

  void applyForce(PVector force) {  // Newton's second law; We could add mass here if we want A = F / M
    acceleration.add(force);
  }


  // A method that calculates and applies a steering force towards a target
  // STEER = DESIRED MINUS VELOCITY
  void seek(PVector target) {
    PVector desired = PVector.sub(target,location);  // A vector pointing from the location to the target
    desired.normalize();  // Normalize desired and scale to maximum speed
    desired.mult(maxspeed);
    PVector steer = PVector.sub(desired,velocity);  // Steering = Desired minus Velocity
    steer.limit(maxforce);  // Limit to maximum steering force
    applyForce(steer);
  }
  
  // Method that lowers the speed of the agent by the time he reaches the target - see: "buffer zone"
  void arrive(PVector target) {
    PVector desired = PVector.sub(target,location);  // A vector pointing from the location to the target
    float d = desired.mag();
    desired.normalize();  // Normalize desired and scale with arbitrary damping within 100 pixels
    if (d < r) {
      float m = map(d,0,15,0,maxspeed);
      desired.mult(m);
    } else {
      desired.mult(maxspeed);
    }

    PVector steer = PVector.sub(desired,velocity);  // Steering = Desired minus Velocity
    steer.limit(5*maxforce);  // Limit to maximum steering force
    applyForce(steer);
  }
  
  void displayINFO() {
    advance();
    shapes2D();
  }
  
  void advance(){  // animates rotation for the buffer zone ellipse
    angle += angleVel; 
  }  
  
  void display() {  // Draws an ellipse that represents an agent-capsule
    fill(fillColor);
    noStroke();
    pushMatrix();
    translate(location.x, location.y, location.z);
    sphere(r/2);
    popMatrix();
  }
  
  void shapes2D() {
    /////////////////////////billboarding/////////////////////////
    fill(0,0,0);
    noStroke();
    pushMatrix();
    translate(location.x, location.y, location.z);
    sphere(r/2);
    float[] rota = cam.getRotations();
    rotateX(rota[0]);
    rotateY(rota[1]);
    rotateZ(rota[2]);
    fill(160);
    String myText = "ID: - give agent characteristics -" + "\nx: " + location.x + "\ny: " + location.y + "\nz: " + location.z;
    textSize(3);
    text(myText,location.x/4,location.y/6, location.z);
    
    stroke(160); fill(0,0,0,0);strokeWeight(1);
    ellipse(0,0,8,8);
    
    line(-4,-6,-10,-12);  // drawing the Info
    line(-10,-12,-16,-12);
    stroke(0,255,0); fill(0,255,0);strokeWeight(0.3);
    ellipse(-4,-6,1,1);
    text("buffer zone", -25, -12);
    
    rotate(angle);
    stroke(0); fill(0);strokeWeight(0);  // 2D shape representing the buffer zone
    ellipse(0,0,0,0);
    line(0,0,0,0);
    line(0,0,0,0);
    line(0,0,0,0);
    line(0,0,0,0);
    
    popMatrix();
    //////////////////////////////////////////////////////////////
  }
  
  // Method that checkes the maximum floating range of the agents - bouncing on the edges of the cube
  void boundaries() {

    if (location.x < 0 || location.x > worldSize) {
      velocity.x *=-1;
    }  

    if (location.y < 0 || location.y > worldSize) {
      velocity.y *=-1;
    }  
    
    if (location.z < 0 || location.z > worldSize) {
      velocity.z *=-1;
    }  
  }
  
  // Method that separates the agents from one-another -- see: "buffer zone"
  void separate (ArrayList<Capsule> agent) {
    float desiredseparation = r*4;  // 4*r = separation ONLY for wandering
    PVector sum = new PVector();
    int count = 0;
    
    // For every agent in the system, check if it's too close
    for (Capsule v : agent) {
      float d = PVector.dist(location, v.location);
      
      // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
      if ((d > 0) && (d < desiredseparation)) {
        
        // Calculate vector pointing away from neighbor
        PVector diff = PVector.sub(location, v.location);
        diff.normalize();
        diff.div(d);        // Weight by distance
        sum.add(diff);
        count++;            // Keep track of how many
      }
    }
    // Average -- divide by how many
    if (count > 0) {
      sum.div(count);
      sum.normalize();  // Our desired vector is the average scaled to maximum speed
      sum.mult(maxspeed);
      
      // Implement Reynolds: Steering = Desired - Velocity
      PVector steer = PVector.sub(sum, velocity);
      steer.limit(maxforce);
      applyForce(steer);
    }
  }  
  
  // Method that separates the agents from one-another -- see: "buffer zone"
  void separateSpecial (ArrayList<Capsule> agent) {
    float desiredseparation = r;  //
    PVector sum = new PVector();
    int count = 0;
    
    // For every agent in the system, check if it's too close
    for (Capsule v : agent) {
      float d = PVector.dist(location, v.location);
      
      // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
      if ((d > 0) && (d < desiredseparation)) {
        
        // Calculate vector pointing away from neighbor
        PVector diff = PVector.sub(location, v.location);
        diff.normalize();
        diff.div(d);        // Weight by distance
        sum.add(diff);
        count++;            // Keep track of how many
      }
    }
    // Average -- divide by how many
    if (count > 0) {
      sum.div(count);
      sum.normalize();  // Our desired vector is the average scaled to maximum speed
      sum.mult(maxspeed);
      
      // Implement Reynolds: Steering = Desired - Velocity
      PVector steer = PVector.sub(sum, velocity);
      steer.limit(maxforce);
      applyForce(steer);
    }
  }
  
  // Alignment
  // For every nearby agent in the system, calculate the average velocity
  PVector align (ArrayList<Capsule> agent) {
    float neighbordist = 50;
    PVector sum = new PVector(0,0);
    int count = 0;
    for (Capsule v : agent) {
      float d = PVector.dist(location,v.location);
      if ((d > 0) && (d < neighbordist)) {
        sum.add(v.velocity);
        count++;
      }
    }
    if (count > 0) {
      sum.div((float)count);
      sum.normalize();
      sum.mult(maxspeed);
      PVector steer = PVector.sub(sum,velocity);
      steer.limit(maxforce);
      return steer;
    } else {
      return new PVector(0,0);
    }
  }
}





Replies(2)

Re: 3D sphere mapping

9 months ago
anyone? If you need more stuff from the script just inform me but I think the problem is in the yellow lines...:(

Re: 3D sphere mapping

9 months ago
Note, I didn't look at your code as it is long and this should be a reasonable solution from your explanation: Create three arrays for each agent that store the x, y, and z positions of that agent up to some number of frames in the past. Every time an agent moves, put its previous position into the arrays. The next time it moves shift the previous values in the arrays and add the new previous position. If each array has a length of 10, then you'll be able to visualize 10 previous locations an agent was.