/* built with Studio Sketchpad:
* https://sketchpad.cc
*
* observe the evolution of this sketch:
* https://studio.sketchpad.cc/sp/pad/view/ro.LyCDXev-AaG/rev.944
*
* authors:
* Stephen Boyd
* license (unless otherwise specified):
* creative commons attribution-share alike 3.0 license.
* https://creativecommons.org/licenses/by-sa/3.0/
*/
/*
Stephen Boyd, @sspboyd
Built while reading @shiffman's The Nature of Code book
https://www.kickstarter.com/projects/shiffman/the-nature-of-code-book-project
Instructions:
Click and drag to move the white attractor around the screen
Press "c" to clear the screen and re-initialize the sketch
Press "s" to render out an image of the screen.
Try playing with variables to get different results, specifically:
Attractor mass,
gravity constant (g)
topSpeed
distance max mins (5, and 30 now)
the number of particles (NUM_PARTICLES)
MIN_PARTICLE_SIZE and MAX_PARTICLE_SIZE
*/
int NUM_PARTICLES = 100;
float MIN_PARTICLE_SIZE = .9;
float MAX_PARTICLE_SIZE = 12;
Mover[] movers = new Mover[NUM_PARTICLES];
Attractor a;
void setup() {
size(900,800);
smooth();
colorMode(RGB, 100);
frameRate(30);
background(100, 100, 100);
init();
}
void init(){
background(100, 100, 100);
for(int i=0; i<movers.length; i++) {
movers[i] = new Mover(random(MIN_PARTICLE_SIZE, MAX_PARTICLE_SIZE), random(width), random(height));
}
a = new Attractor();
}
void draw() {
for(int i=0; i<movers.length; i++) {
Mover mi = movers[i];
for(int j=0; j<movers.length; j++) {
Mover mj = movers[j];
if(i != j) {
PVector force = mj.attract(mi);
mi.applyForce(force);
}
}
PVector attractorForce = a.attract(mi);
mi.applyForce(attractorForce);
PVector wind = new PVector(.01, 0);
mi.applyForce(wind);
mi.update();
mi.display();
}
a.display();
}
class Attractor {
float mass;
PVector location;
float g;
Attractor() {
location = new PVector(width/2, height/2.5);
mass = 400;
g = 0.8;
}
PVector attract(Mover m) {
PVector force = PVector.sub(location, m.location);
float distance = force.mag();
distance = constrain(distance, 5, 30);
force.normalize();
float strength = (G * mass * m.mass) / (distance * distance);
force.mult(strength);
return force;
}
void display() {
noStroke();
fill(100,100,100,15);
if(mousePressed){
location.x=mouseX;
location.y=mouseY;
}
ellipse(location.x, location.y, mass*.5, mass*.5);
}
}
class Mover {
PVector location;
PVector velocity;
PVector acceleration;
float topSpeed;
float mass;
color c;
Mover(float m, float x, float y) {
location = new PVector(x, y);
velocity = new PVector(0,0);
acceleration = new PVector(0,0);
topSpeed = 50;
mass = m;
}
PVector attract(Mover m) {
PVector force = PVector.sub(location, m.location);
float distance = force.mag();
distance = constrain(distance, 5, 30);
force.normalize();
float strength = (G * mass * m.mass) / (distance * distance);
force.mult(strength);
return force;
}
void update() {
velocity.add(acceleration);
velocity.limit(topSpeed);
location.add(velocity);
acceleration.mult(0);
}
void applyForce(PVector force) {
PVector f = force.get();
f.div(mass);
acceleration.add(f);
}
void display() {
noStroke();
color c;
if(velocity.mag() > topSpeed/2) {
c = color(95,83,0,30);
}
else {
c = color(24,72,87,30);
}
fill(c,100);
ellipse(location.x, location.y, mass*1, mass*1);
}
}
// Keyboard UI
void keyPressed() {
if(key == 'C' || key == 'c'){ // C = clear the screen and re-initialize the sketch
init();
}
if (key == 'S' || key == 's') { // S = save the sketch as an image
String filePrefix = "Attractor_Duotone";
String fileType = ".tif";
String dateStamp = year() + "" + nf(month(),2) + "" + nf(day(),2) + "-" + nf(hour(),2) + "" + nf(minute(),2) + "" + nf(second(),2);
String fileName = filePrefix + dateStamp + fileType;
save(fileName);
println("saved file: " + fileName);
}
}