/* built with Studio Sketchpad:
* https://sketchpad.cc
*
* observe the evolution of this sketch:
* https://studio.sketchpad.cc/sp/pad/view/ro.UlB8vsGzdgs/rev.1308
*
* authors:
*
* darksquid
* license (unless otherwise specified):
* creative commons attribution-share alike 3.0 license.
* https://creativecommons.org/licenses/by-sa/3.0/
*/
//Copyright 2012 Zak Hammerman. Free to distribute and edit noncommercially, with proper attribution.
//A simple sketch showing connections between moving bodies or nodes.
//The cursor location determines opacity; clicking toggles circle visibility.
//Keys: 'n' creates a new set of values
// '=' adds a ball, '-' removes one randomly <--Disabled for now.
// 'o' toggles current quantity of objects
// 'f' toggles average framerate
// 'v' toggles average velocities (x,y) *off by default*
//Each ball's size is in an inverse proportion to its speed. (1/radius)
//This code has not been optimised yet. It's more of a WIP.
//Version 0.2: converted the array structure into an ArrayList to freely manipulate the amount of objects.
//Version 0.3: added more detailed info, overhauled the formatting and added simple error checking for IndexOutOfBoundsExceptions for the ArrayList. Still working on desktop but not ProcessingJS.
// 0.3.1: circles are relatively opaque on mouseover; lines between circles are pink when two circles intersect.
//Version 0.4: returned to the more reliable array structure. Cleaned up some code. The previous changes should be visible now. Woohoo!
//Verdion 0.4.1: a little cleanup.
int index=(int)random(20, 30);
boolean filled, showobj=true, showfrate=true, showvel=false;
Ball[] balls;
void setup() {
size(600, 600);
frameRate(60);
smooth();
background(15);
fill(255);
filled=true;
balls = new Ball[index];
for(int i=0;i<index;i++) balls[i] = new Ball(random(25,60),filled);
}
void draw() {
background(15);
for (int i=0;i<balls.length;i++) balls[i].update();
for (int x=0;x<balls.length;x++)
for (int y=x; y<balls.length; y++) {
if (balls[x].l.dist(balls[y].l) <=200) {
strokeWeight(1.5);
if (balls[x].l.dist(balls[y].l)<=(balls[x].r/2)+(balls[y].r/2)) {
stroke(200, 50, 50, 127);
}
else stroke(100, 100, 100, 127);
line(balls[x].l.x, balls[x].l.y, balls[y].l.x, balls[y].l.y);
}
}
fill(255);
if (showobj) text("Objects: " + balls.length, 5, height-40);
if (showfrate) text("Framerate: " + round(frameRate), 5, height-25);
if (showvel) text("Average velocity: (" + avgVel().x + ", " + avgVel().y + ")", 5, height-10);
}
boolean intersect(Ball a, Ball b) {
if (a.l.dist(b.l)<=(a.r/2)+(b.r/2)) return true;
else return false;
}
void mousePressed() { //Toggles visibility of circles
if (filled) filled=false;
else filled=true;
for (int i=0;i<balls.length;i++) {
balls[i].col=filled;
}
}
void keyPressed() { //Creates a new set of circles and values
switch(key) {
case 'n':
for (int i=0;i<balls.length;i++) balls[i]=new Ball(random(25, 60), filled);
break;
/*case '=': //No longer needed. The ArrayList is being used on the desktop version but not in ProcessingJS. It's rather uncooperative...
balls.add(new Ball(random(20, 90), filled));
break;
case '-':
try {
balls.remove((int)random(0, balls.size()-1));
}
catch (IndexOutOfBoundsException e) {
} //Don't throw a fit when balls.size()=0
break;*/
case 'f':
if (showfrate) showfrate=false;
else showfrate=true;
break;
case 'o':
if (showobj) showobj=false;
else showobj=true;
break;
case 'v':
if (showvel) showvel=false;
else showvel=true;
break;
}
}
PVector avgVel() {
float tx=0, ty=0;
for (int i=0; i<balls.length-1; i++) {
tx+=abs(balls[i].v.x);
ty+=abs(balls[i].v.y);
}
tx/=balls.length;
ty/=balls.length;
return new PVector(tx, ty, 0.0);
}
class Ball {
private int c1, c2, c3;
private PVector v, l, m;
private float r;
private color f, s;
private boolean col, intersect;
Ball(float rad, boolean filled) {
r=rad;
v=new PVector(random(128*(1/r), 256*(1/r)), random(128*(1/r), 256*(1/r)), 0);
l=new PVector(int(random(r/2,width-r/2)),int(random(r/2,height-r/2)), 0);
m=new PVector(-width, -height);
c1=int(random(25, 255));
c2=int(random(25, 255));
c3=int(random(25, 255));
col=filled;
}
void update() {
l.x += v.x;
l.y += v.y;
v=collision();
this.display();
}
void display() {
m.set(mouseX, mouseY, 0);
strokeWeight(7.5);
if(!col && dist(mouseX,mouseY,l.x,l.y)<r){
stroke(255, 0, 0,225);
}
else stroke(100, 100, 100, 127);
if(col) {
if(dist(mouseX,mouseY,l.x,l.y)<r){
f=color(c1, c2, c3,160);
s=color(c1, c2, c3,225);
}
else f=color(c1, c2, c3, (4*width)/dist(l.x, l.y, m.x, m.y));
s=f;
fill(f);
stroke(s);
ellipse(l.x, l.y, r, r);
}
point(l.x, l.y);
}
PVector collision() {
if (l.x < r/2 || l.x > width-r/2) {
if (l.x < r/2) l.x=r/2;
if (l.x > width-r/2) l.x=width-r/2;
v.x *= -1;
if (frameCount%2==0) { //Psuedorandom changes in x-velocity
v.x*=(1/v.x);
v.x*=random(128*(1/r), 256*(1/r));
}
}
if (l.y < r/2 || l.y > height-r/2) {
if (l.y < r/2) l.y=r/2;
if (l.y > height-r/2) l.y=height-r/2;
v.y *= -1;
if (frameCount%2==0) { //Same type of changes in y-velocity
v.y*=(1/v.y);
v.y*=random(128*(1/r), 256*(1/r));
}
}
return v;
}
}