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;
}
}

















