orbit

orbital particle systems

// made by alex
// iamalexbaker@gmail.com
// dailygenerative.art.blog

ArrayList<particle> particles;
ArrayList<zone> zones;
ArrayList<planet> planets;
ArrayList<PVector> traces;

float drag = 0.999; // set to 1 for no drag
float gravity = 0;
float powerRange = 0.02; // for zones
float[] sizeRange = {100, 400}; // for zones/planets
float circleRadius = 100;
float circleAngle = PI/143;
int initialiseMode = 1; // 0: random, 1: circle
float[] initialImpulse = {0.01, 0.035, 0};
boolean randomImpulses = true; // if true, each particle has a different starting velocity
float startDirection = 1; // +1: fly away from circle, -1: fly into it
float[] planetMass = {1.0, 2.0};
float systemPull = 0.2;
int systemPullMode = 2; // 0 to drag into centre or 2 to pull into orbit
float systemCentre = 3.0;
boolean boundaries = false; // stop things going off the edge of the screen
float particleSize = 1;
int numParticles = 3000;
int numZones = 200;
int numPlanets = 8;
int numTicks = 1000;
int currentTick = 0;

boolean showZones = false;
boolean do_draw = true;
boolean realTime = false;

float[] hueRange = {0, TWO_PI};
float[] satRange = {1.0, 1.0};
float[] briRange = {1.0, 1.0};
float alpha = 25;
int nc = 2;
color[] c = new color[nc];
color bgc;
float hue, sat, bri;

void setup(){
  size(800, 800);
  colorMode(HSB, TWO_PI, 1, 1);
  bgc = color(0, 0, 0.2);
  background(bgc);
  hue = random(hueRange[0], hueRange[1]);
  sat = random(satRange[0], satRange[1]) * TWO_PI;
  bri = random(briRange[0], briRange[1]) * TWO_PI;
  for(int i = 0; i < nc; i++){
    c[i] = color(random(hueRange[0], hueRange[1]), random(satRange[0], satRange[1]), random(briRange[0], briRange[1]));
  }
  
  currentTick = 0;
  initialImpulse[2] = random(initialImpulse[0], initialImpulse[1]);
  particles = new ArrayList<particle>();
  zones = new ArrayList<zone>();
  planets = new ArrayList<planet>();
  traces = new ArrayList<PVector>();
  PVector origin = new PVector(width/2, height/2);
  if(initialiseMode==0){
    for(int i = 0; i < numParticles; i++){
      if(randomImpulses) initialImpulse[2] = random(initialImpulse[0], initialImpulse[1]);
      PVector force = PVector.random2D().mult(initialImpulse[2]);
      particles.add(new particle(random(width), random(height), force.x*50, force.y*50));
    }
  }
  if(initialiseMode==1){
    for(int i = 0; i < numParticles; i++){
      float r = circleAngle * i;
      float xp = circleRadius * cos(r);
      float yp = circleRadius * sin(r);
      if(randomImpulses) initialImpulse[2] = random(initialImpulse[0], initialImpulse[1]);
      particles.add(new particle(origin.x+xp, origin.y+yp, xp * initialImpulse[2] * startDirection, yp * initialImpulse[2] * startDirection));
    }
  }
  if(systemCentre > 0) planets.add(new planet(origin.x, origin.y, systemCentre, 5, 1));
  if(systemPull > 0) planets.add(new planet(origin.x, origin.y, width, systemPull, systemPullMode));
  for(int j = 0; j < numPlanets; j++){
    planets.add(new planet(random(width),random(height),random(sizeRange[0], sizeRange[1]),random(planetMass[0], planetMass[1]),2));
  }
  do_draw = true;
  
}

void draw(){
  if(realTime){
    background(bgc);
    traces = new ArrayList<PVector>();
    numTicks = 1;
    do_draw = true;
  }
  if(!do_draw) return;
  for(int k = 0; k < numTicks; k++){
    for(planet z: planets){
      z.tick();
      if(showZones) z.show();
    }
    for(particle p: particles){
      p.tick();
      //p.show();
    }
    currentTick++;
  }
  noStroke();
  //ellipse(400,400,200,200);
  //println(traces.size());
  for(PVector t: traces){
    fill(lerpColor(c[0], c[1], t.z), alpha);
    ellipse(t.x, t.y, particleSize, particleSize);
  }
  do_draw = false;
}

class particle{
  PVector pos, vel;
  
  particle(float x, float y, float vx, float vy){
    pos = new PVector(x, y); vel = new PVector(vx, vy);
  }
  
  void tick(){
    pos.x += vel.x;
    pos.y += vel.y + gravity;
    if(boundaries){
      if(pos.x < 0 || pos.x > width) vel.x = -vel.x;
      if(pos.y < 0 || pos.y > height) vel.y = -vel.y;
    }
    vel.mult(drag);
    traces.add(new PVector(pos.x, pos.y, float(currentTick)/numTicks));
  }
  
  void show(){
    stroke(50, 150);
    noFill();
    ellipse(pos.x, pos.y, particleSize, particleSize);
  }
}

class planet{
  PVector pos;
  float radius, mass;
  int mode; // 0: black hole, 1: push away, 2: orbit
  planet(float x, float y, float r, float m, int mm){
    pos = new PVector(x, y); radius = r; mass = m; mode = mm;
  }
  
  void tick(){
    attractParticles();
  }
    
  void show(){
    noStroke();
    fill(3*PI/2, 0.7, 0.7, 100);
    ellipse(pos.x, pos.y, radius/2, radius/2);
  }
  
  void attractParticles(){
    for(particle p: particles){
      if(pos.dist(p.pos) <= radius/2){
        PVector facingPlanet = new PVector(pos.x - p.pos.x , pos.y - p.pos.y).normalize();
        float velMag = p.vel.mag();
        p.vel.normalize();
        switch(mode){
          case 0:
            
            //stroke(0,0,1,100);
            //line(p.pos.x, p.pos.y, p.pos.x + p.vel.x*100, p.pos.y + p.vel.y*100);
            //stroke(PI/4,0.5,1,100);
            //line(p.pos.x, p.pos.y, p.pos.x + facingPlanet.x*100, p.pos.y + facingPlanet.y*100);
            p.vel.lerp(facingPlanet, mass*0.1);
            break;
          case 1:
          facingPlanet.mult(-1);
            p.vel.lerp(facingPlanet, mass*0.1);
            break;
          case 2:
            PVector tangent = new PVector(facingPlanet.x, facingPlanet.y).rotate(PI/(2*mass));
            //stroke(0,0,1,100);
            //line(p.pos.x, p.pos.y, p.pos.x + p.vel.x*50, p.pos.y + p.vel.y*50);
            //stroke(PI/4,0.9,1,100);
            //line(p.pos.x, p.pos.y, p.pos.x + tangent.x*50, p.pos.y + tangent.y*50);
            p.vel.lerp(tangent,0.05);
            break;
        }
        p.vel.setMag(velMag);
      }
    }
  }
}

class zone{
  PVector pos, force;
  float radius;
  zone(float x, float y, float fx, float fy, float r){
    pos = new PVector(x, y); force = new PVector(fx, fy); radius = r;
  }
  
  void tick(){
    pushParticles();
  }
  
  void show(){
    stroke(50, 100);
    line(pos.x, pos.y, pos.x + force.x*500, pos.y + force.y * 500);
    fill(50,80,200, 50);
    noStroke();
    ellipse(pos.x, pos.y, radius/2, radius/2);
    
  }
  
  void pushParticles(){
    for(particle p: particles){
      if(pos.dist(p.pos) <= radius/2){
        p.vel.add(force);
      }
    }
  }
}

void keyPressed()
{
  if (keyCode==32) { // space
    saveFrame("po-"+hour()+"-"+minute()+"-"+second()+".png");
  }
  if (keyCode==10) { // enter
    setup();
  }
  if(keyCode==90) { // Z
    showZones = !showZones;
  }
}

Leave a comment