import java.util.*;
PShape masterShape;
Grid grid;
ArrayList<Bubble> bubbles;
int yMarker = 0;
int xMarker = 0;
boolean rightMouseDown = false;
ArrayList<Point> send;
////////////////////////////////////////////////////////////////////
boolean showMouse = true;
int bubbleAlpha = 50;
int backGround = color(0);
int circleSegments = 40;
// set this to add some noise value to vector p, based on coords x, y
public void noiseFunction(float x, float y, PVector p) {
// perlin noise
p.x+= (noise(x*0.01, y*0.01) - 0.5) * 2.0;
p.y+= (noise(x*0.01 + 3000, y*0.01) - 0.5) * 2.0;
float ang = atan2(y - height*0.5, x - width*0.5);
float dist = dist(x, y, width*0.5, height*0.5);
// ziggy zag (like m3)
//p.x+= cos(ang+HALF_PI)*sin(dist * 0.02);
//p.y+= sin(ang+HALF_PI)*sin(dist * 0.02);
}
// called by the mouse bubble for colors
public int randCol() {
return color(random(127)+127, random(127)+127, random(127)+127);
}
////////////////////////////////////////////////////////////////////
public void setup() {
size(1000, 1000, P2D);
send = new ArrayList<Point>();
grid = new Grid(0, 0, width, height);
bubbles = new ArrayList<Bubble>();
//bubbles.add(new Bubble(width>>1, height>>1, 200, 10, 0));
}
public void draw() {
background(backGround);
masterShape = createShape(GROUP);
if (bubbles.size() > 0) {
Iterator<Bubble> it = bubbles.listIterator();
while (it.hasNext ()) {
Bubble b = it.next();
if (b.update()) it.remove();
else b.display(bubbleAlpha, masterShape);
}
}
if (masterShape != null) shape(masterShape);
if (showMouse) showMouseShape();
}
public void showMouseShape() {
if (rightMouseDown) {
fill(0, 10);
if (send.size() > 0) {
stroke(0);
for (int i = 0; i < send.size(); i++) {
int i2 = (i+1) % send.size();
line(send.get(i2).pos.x, send.get(i2).pos.y, send.get(i).pos.x, send.get(i).pos.y);
}
fill(0);
ellipse(send.get(0).pos.x, send.get(0).pos.y,5,5);
noFill();
}
if (send.size() < 2) {
stroke(64);
float size = dist(xMarker, yMarker, mouseX, mouseY) * 2.0;
ellipse(xMarker, yMarker, size, size);
}
}
}
public class Bubble {
ArrayList<Point> points;
float collapse = 5;
float expand = 11; // more than double the collapse value
int col = 0;
boolean dead = false;
public Bubble() {
points = new ArrayList<Point>();
}
public Bubble(float x, float y, float r, int seg, int col) {
this();
for (int i = 0; i < seg; i++) {
float ang = i*TWO_PI/seg;
points.add(new Point( x+cos(ang)*r, y+sin(ang)*r ));
}
this.col = col;
}
public Bubble(ArrayList<Point> list, int col) {
this();
points.addAll(list);
this.col = col;
}
public boolean update() {
if (points.size() > 1) { // if there is only 1 point it can't grow.
for (Point p : points) {
grid.sendForce( p );
p.update();
//p.display();
}
/// I think my logic here is sound, 'vortexes' can cause the points to grow though
// check for points that are too close, or need to be split
// the index will 'follow' an object if it's previous is deleted
ArrayList<Point> pointsToAdd = new ArrayList<Point>(); // remove points too close to index, until the index is flagged or ignored
for (int i = points.size () -1; i > 0; i--) { // go backwards so objects aren't skipped when one is deleted
float distbetween = points.get(i).distance(points.get(i-1));
if (distbetween > expand) {
pointsToAdd.add(points.get(i)); // flag the current one .add adds before the object at the index
} else if (distbetween < collapse) {
points.remove(i-1); // don't remove the current index because we may have previously flagged it to be increased
}
}
// check the first index against the last if there is more than 2
if (points.size() > 1) {
float distbetween = points.get(0).distance(points.get(points.size()-1));
if (distbetween > expand) {
pointsToAdd.add(points.get(0));
} else if (distbetween < collapse) {
points.remove(0); // remove 0, because it couldn't have been flagged to add a point (in previous above loop)
}
}
// add new points marked by pointsToAdd
if (pointsToAdd.size() > 1 && points.size() > 1) // there are points to add and at least a point
for (Point p : pointsToAdd) {
int index = points.indexOf(p);
int i2 = (index + points.size() - 1) % points.size();
points.add(index, new Point((points.get(index).pos.x + points.get(i2).pos.x)*0.5, (points.get(index).pos.y + points.get(i2).pos.y)*0.5));
}
return false;
} else {
return true;
}
}
public void display(PShape group) {
display( 255, group );
}
public void display(int alpha, PShape group) {
if (points.size() > 2) {
PShape shape = createShape();
shape.beginShape();
shape.noStroke();
shape.fill( col, alpha );
for (Point p : points) {
shape.vertex( p.pos.x, p.pos.y );
}
shape.endShape();
group.addChild(shape);
}
}
}
public class Point {
PVector pos;
PVector vel;
PVector forceSum;
boolean accelerate = false;
public Point ( float x, float y, float xv, float yv) {
pos = new PVector( x, y );
vel = new PVector( xv, yv );
forceSum = new PVector( 0, 0 );
}
public Point( float x, float y ) {
this ( x, y, 0, 0 );
}
public void update() {
if (accelerate) {
vel.add( forceSum );
} else {
vel.x = forceSum.x;
vel.y = forceSum.y;
}
pos.add( vel );
forceSum.x = 0;
forceSum.y = 0;
}
public void display() {
noStroke();
fill(0);
ellipse((int)pos.x, (int)pos.y, 4, 4);
}
public float distance( Point p ) {
return dist( pos.x, pos.y, p.pos.x, p.pos.y );
}
public float mDistance( Point p ) {
return max( abs( p.pos.x - pos.x ), abs( p.pos.y - pos.y ) );
}
}
public class Grid {
PVector[] f;
int xg, yg, w, h;
public Grid(int xx, int yy, int ww, int hh) {
xg = xx;
yg = yy;
w = abs(w);
h = abs(h);
if (w > 0 && h > 0) { // grid has dimensions
f = new PVector[w*h];
for (int r = 0; r < h; r++) {
for (int c = 0; c < w; c++) {
addNoise( c, r, f[c + r*w] );
}
}
}
}
public void addNoise(float x, float y, PVector p) { // for different grids with different noiseFunctions
noiseFunction(x, y, p); // did this so the noise can be a method up near draw
}
public void sendForce( Point p ) {
if (f != null) {
int xi = (int)p.pos.x - xg; // position in grid
int yi = (int)p.pos.y - yg;
if (xi >= 0 && xi < w && yi >= 0 && yi < h ) { // inside grid
p.forceSum.add(f[xi + yi * w]);
return;
}
}
addNoise(p.pos.x, p.pos.y, p.forceSum); // refer to the noise function if value isn't a saved one
}
}
public void mousePressed() {
if ((mouseButton == LEFT) && rightMouseDown) {
send.add(new Point(mouseX, mouseY));
}
if (mouseButton == RIGHT) {
rightMouseDown = true;
xMarker = mouseX;
yMarker = mouseY;
}
}
public void mouseReleased() {
if (mouseButton == RIGHT) {
if (send.size() > 1) {
bubbles.add(new Bubble(send, randCol() ));
send.clear();
} else {
bubbles.add(new Bubble(xMarker, yMarker, dist(xMarker, yMarker, mouseX, mouseY), circleSegments, randCol() ));
}
rightMouseDown = false;
}
}