/* built with Studio Sketchpad:
* https://sketchpad.cc
*
* observe the evolution of this sketch:
* https://studio.sketchpad.cc/sp/pad/view/ro.XyQl6NaXq5c/rev.981
*
* authors:
* Julien Blanchet
* license (unless otherwise specified):
* creative commons attribution-share alike 3.0 license.
* https://creativecommons.org/licenses/by-sa/3.0/
*/
color backgroundColor = 0;
int maxRunners = 10;
int frame_rate = 60;
int fade_rate = 1;
boolean paused = false;
boolean started = false;
ArrayList<RunnerController> runners;
ArrayList<RunnerController> runnersToRemove;
void setup() {
size(600, 600);
colorMode(RGB, 255, 255, 255, 100);
background(backgroundColor);
noStroke();
frameRate(frame_rate);
runners = new ArrayList<RunnerController>();
runnersToRemove = new ArrayList<RunnerController>();
}
void draw() {
if (paused)
return;
if (fade_rate != 0){
// Fade the background
noStroke();
color black = color(0, fade_rate);
fill(black);
rect(0,0,width,height);
}
for (RunnerController r: runners) {
r.step();
r.draw();
if (r.finished())
runnersToRemove.add(r);
}
for (RunnerController r: runnersToRemove)
runners.remove(r);
int addedThisFrame = 0;
while (runners.size () < maxRunners && addedThisFrame < 1) {
runners.add(new RunnerController());
addedThisFrame += 1;
}
if (!started){
stroke(128);
strokeWeight(10);
fill(255);
rect(130,130,width-260, height-260);
fill(0);
textAlign(CENTER, CENTER);
textSize(20);
float t_y = height*.33;
text("Welcome to...", width/2, t_y);
t_y += textAscent() + textDescent();
textSize(48);
t_y += textAscent();
text("RUNNERS", width/2, t_y);
t_y += textAscent() + textDescent();
textSize(15);
text("A creative sketch by Julien Blanchet", width/2, t_y);
t_y += 6 * (textAscent() + textDescent());
text("Click to start\nSpacebar pauses/plays the simulation\nNumber Keys control fade rate", width/2, t_y);
}
strokeWeight(2);
}
void mouseClicked(){
if (started == false)
background(0);
started = true;
paused = false;
maxRunners += 1;
}
void keyTyped(){
if (key == ' '){
if (started == false)
background(0);
paused = !paused;
started = true;
}
else if (key == '0')
fade_rate = 0;
else if (key == '1')
fade_rate = 1;
else if (key == '2')
fade_rate = 2;
else if (key == '3')
fade_rate = 3;
else if (key == '4')
fade_rate = 4;
else if (key == '5')
fade_rate = 5;
else if (key == '6')
fade_rate = 6;
else if (key == '7')
fade_rate = 7;
else if (key == '8')
fade_rate = 8;
else if (key == '9')
fade_rate = 9;
}
interface Runner{
void initXYA(V2D xy, float ang);
void draw();
void step();
boolean finished();
V2D getXY();
float getAng();
}
class RunnerCircle implements Runner{
V2D xy;
float ang, rad;
color col;
boolean drawn;
RunnerCircle(){
xy = new V2D(mouseX, mouseY);
ang = random(0, TWO_PI);
rad = random(6, 15);
col = posColor();
drawn = false;
}
void initXYA(V2D xy, float ang){
this.xy = xy;
this.ang = ang;
}
void draw(){
fill(col);
noStroke();
ellipse(xy.x, xy.y, rad, rad);
drawn = true;
}
boolean finished(){return drawn;}
void step(){}
float getAng(){return ang;}
V2D getXY(){return xy;}
}
static float RUNNER_LINE_THICKNESS = 1;
class RunnerLines implements Runner {
float x, y, w, ang, spd, d_ang, d_spd, d_w, draw_angle;
int curType;
int stagesLeft;
float framesLeft;
float framesInLife;
color col;
RunnerLines() {
this.x = mouseX;
this.y = mouseY;
this.w = random(2, 10);
this.ang = random(0, TWO_PI);
this.spd = random(2, 5);
this.col = posColor();
this.draw_angle = random(0, TWO_PI);
float prob = random(0, 100);
if (prob < 80)
this.framesLeft = random(7, 25);
else
this.framesLeft = random(25, 105);
this.framesInLife = framesLeft;
prob = random(0, 100);
float angleChange = random(QUARTER_PI / 3, QUARTER_PI);
this. d_ang = angleChange / framesInLife;
if (prob < 50)
this.d_ang = - d_ang;
this.d_w = random(- (w / framesInLife), (w / framesInLife));
prob = random(0, 100);
if (prob < 80)
this.d_spd = random(- spd / framesInLife, spd / framesInLife);
else
this.d_spd = random(spd / framesInLife, 2* spd / framesInLife);
}
void initXYA(V2D xy, float ang){
this.x = xy.x;
this.y = xy.y;
this.ang = ang;
}
void draw() {
if (finished())
return;
float dx = x - w * sin(draw_angle) / 2;
float dy = y + w * cos(draw_angle);
float dx2 = x + w * sin(draw_angle) / 2;
float dy2 = y - w * cos(draw_angle);
stroke(this.col);
line(dx, dy, dx2, dy2);
}
V2D getXY(){
return new V2D(x,y);
}
float getAng(){
return ang;
}
void step() {
this.x += cos(ang) * spd;
this.y += sin(ang) * spd;
this.ang += d_ang;
this.w += d_w;
this.spd += d_spd;
framesLeft -= 1.0;
}
boolean finished() {
return framesLeft < 0;
}
}
static final int STRAIGHT_THICKNESS = 2;
class RunnerStraight implements Runner {
V2D xy;
float ang, len;
color col;
boolean drawn;
RunnerStraight() {
xy = new V2D(mouseX, mouseY);
ang = random(0, TWO_PI);
len = random(10, 30);
col = posColor();
drawn = false;
}
void initXYA(V2D xy, float ang) {
this.xy = xy;
this.ang = ang;
float modAngle = random(HALF_PI / 4, HALF_PI / 2);
if (random(0, 100) < 50)
ang -= modAngle;
else
ang += modAngle;
}
void draw() {
strokeWeight(STRAIGHT_THICKNESS);
stroke(col);
line(xy.x, xy.y, xy.x + cos(ang) * len, xy.y + sin(ang) * len);
drawn = true;
}
boolean finished() {
return drawn;
}
void step() {
}
float getAng() {
return ang;
}
V2D getXY() {
return xy;
}
}
class V2D {
float x, y;
V2D(float x, float y) {
this.x = x;
this.y = y;
}
V2D() {
this.x = 0;
this.y = 0;
}
}
static final int RUNNER_TYPE_LINES = 1;
static final int RUNNER_TYPE_CIRCLE = 2;
static final int RUNNER_TYPE_STRAIGHT = 3;
static int min_run_type = 1;
static int max_run_type = 2;
class RunnerController {
Runner myRunner;
int stagesLeft;
V2D xy;
float ang;
RunnerController() {
stagesLeft = int(random(1, 5));
xy = new V2D(mouseX, mouseY);
ang = random(0, TWO_PI);
initMyRunner();
}
void initMyRunner() {
int runType = int(random(min_run_type, max_run_type + 1));
switch(runType) {
case RUNNER_TYPE_LINES:
myRunner = new RunnerLines();
break;
case RUNNER_TYPE_CIRCLE:
myRunner = new RunnerCircle();
break;
case RUNNER_TYPE_STRAIGHT:
myRunner = new RunnerStraight();
break;
default:
myRunner = new InvalidRunner();
println("invalid runner type: reverting to runner liens");
break;
}
myRunner.initXYA(xy, ang);
// stagesLeft -= 1;
}
void draw() {
myRunner.draw();
}
void step() {
myRunner.step();
if (myRunner.finished()) {
xy = myRunner.getXY();
ang = myRunner.getAng();
// if runner has strayed, point them back towards the screen
if (xy.x < 0 || xy.x > width ||
xy.y < 0 || xy.y > height) {
V2D tarPoint = new V2D(random(10, width-10), random(10, height-10));
ang = atan2(tarPoint.y - xy.y, tarPoint.x - xy.x) + random(-QUARTER_PI / 4, QUARTER_PI / 4);
}
initMyRunner();
}
}
boolean finished() {
return myRunner.finished() && stagesLeft <= 0;
}
}
color[] possibleColors = {
#F93149,
#FFA033,
#3097C9,
#55E82E
};
color posColor() {
return possibleColors[int(random(0, possibleColors.length))];
}
class InvalidRunner implements Runner {
float getAng() {
return 0;
}
V2D getXY() {
return new V2D(0, 0);
}
void step() {
}
void initXYA(V2D xy, float ang) {
}
boolean finished() {
return true;
}
void draw() {
}
}