Processing Assignment 1 – Miles Shamo

I’m not sure this is the best experiment I’ve created in processing (the code is horrendous) but it fits best the idea of a drawing machine.  Clicking and dragging your mouse spawns in various circles, the size largely determined by the speed of your mouse.

These circles jitter around, trying their best to escape each-other.  When they do so, they emit bright, connecting lines.  With everything pushing away, this is a horribly unstable structure; first the circles fade away and soon the pattern devolves into scattered “lightning” in pulsing colors.

 

I think you can make some rather interesting patterns with this.  Of course, images don’t really do them justice: everything is but a transitionary state back towards a blank screen, every stroke on the canvas gets wiped away once it is spent by the (mostly) random motion of countless long faded circles.  These stills are just one frame of that pattern and feel rather different when viewed alone.

———————————————————

The code is below.  It’s draw function is at the very bottom as that is how I thought it would be good to organize this at the time.  It instead opens with the classes as I didn’t know you could separate them into other files.  Hopefully it is still readable.  To properly function, the code needs the gifAnimation library in Processing’s packages folder.  While this can’t be acquired through the built-in package manager, it can be downloaded from GitHub.  If gif’s aren’t that relevant, simply comment out the import on the first line, as well as any mention of GifExport, the object used to render gifs.

———————————————————

/**
* Experimental_Behaviors
* A strange, rather impermenant set of triangles
*/

import gifAnimation.*; //here for gifs!

//drawable ensures all objects can be displayed on screen in some fashion
interface Drawable
{
void update();
}

class Circle implements Drawable
{
// (x, y)
private PVector pos = new PVector();
private PVector vel = new PVector();

private float size;

// just an identifier in case
private int ID;

// a lifespan counter
int life;

//constructors
Circle(float xI, float yI, float vXI, float vYI, float sizeI, int ID)
{
pos.x = xI;
pos.y = yI;

vel.x = vXI;
vel.y = vYI;

size = sizeI;

this.ID = ID;

life = 0;
}

//get/set
public PVector getPos()
{
return pos;
}

public PVector getVel()
{
return vel;
}

public float getSize()
{
return size;
}

public float getID()
{
return ID;
}

//other functions for behavior

private void updatePos()
{
//adds velocity to position
pos.x += vel.x;
pos.y += vel.y;
}

//velocity helpers
private void strangeWobble()
{
vel.x = (abs(vel.x) > 5) ? vel.x – (randomGaussian()/2) : vel.x + (randomGaussian()/2);
vel.y = (abs(vel.y) > 5) ? vel.y – (randomGaussian()/2) : vel.y + (randomGaussian()/2);
}

private void bounceEdges()
{
if ((pos.x > CanvasSize && vel.x > 0) || (pos.x < 0 && vel.x < 0))
{
vel.x *= -1;
}

if ((pos.y > CanvasSize && vel.y > 0) || (pos.y < 0 && vel.y < 0))
{
vel.y *= -1;
}
}

private void deleteOnEdge()
{
if (pos.x <= 0 || pos.y <= 0 || pos.x >= 1000 || pos.y >= 1000)
{
circles.remove(this);
}
}

//check collisions
private void collideAndPush()
{
int maxConnections = 5;
int connections = 0;

for (Circle c : circles)
{
if (c != null)
{
//get’s abs distance between two circles

float dist = PVector.dist(this.getPos(), c.getPos());

//this circle is touching the other’s center
if (dist < (this.getSize() + c.getSize()))
{
//we have a collision – move velocity away from circle
PVector push = PVector.sub(c.getPos(), this.getPos()).normalize().div(3);

vel.sub(push);

//ensures drawing won’t crash by exponential connections
if (connections <= maxConnections)
{
//draws a line
strokeWeight(vel.mag()/2);
float r = ((c.getPos().x + c.getPos().y) * 255 / 2000) + 30 * (sin(frameRate / 10 + PI / 3) + cos(frameRate / 10 + PI / 3));
float g = (vel.mag() / 7 * 255) + 30 * (sin(frameRate / 10 + 2 * PI / 3) + cos(frameRate / 10 + 2 * PI / 3));
float b = (abs(pos.y – pos.x) * 255 / 1000) + 30 * (sin(frameRate / 10 + PI) + cos(frameRate / 10 + PI));

stroke(r, g, b);
line(this.getPos().x, this.getPos().y, c.getPos().x, c.getPos().y);
stroke(0, 0, 0);
noFill();
strokeWeight(2);
point(this.getPos().x, this.getPos().y);

//adds one to the # of connections
connections++;
}
}

fill(0, 0, 0);
stroke(0, 0, 0);
strokeWeight(0);
}
}
}

private void drawBoundary()
{
stroke(255, 255, 255, (life < 255) ? (255 – life) : 0);
noFill();
ellipse(pos.x, pos.y, size, size);
}

private void capVelocity()
{
vel.limit(3);
}

//Drawable
void update()
{
life += 3;

updatePos();

strangeWobble();
//bounceEdges();
deleteOnEdge();
collideAndPush();

//for visualizing underlying mechanics
drawBoundary();

//ensures infinite energy doesn’t happen
capVelocity();
}
}

int CanvasSize = 1000;

GifMaker gifExport;

void setup()
{
size(1000, 1000);
background(0, 0, 0);

frameRate(60);

//sets up gif recording
gifExport = new GifMaker(this, “export.gif”);
gifExport.setRepeat(0);

// append(circles, new Circle(random(0, 1000), random(0, 1000), random(0, 1), random(0, 1), random (15, 30), i));
}

//defines an array of circles
ArrayList<Circle> circles = new ArrayList<Circle>();

void draw()
{
fill(0, 0, 0, 50);
noStroke();
rect(0, 0, 1000, 1000);

for (int i = 0; i < circles.size(); i++)
{
if (circles.get(i) != null)
{
circles.get(i).update();
}
}

//if we’re recording, add the frame
if (recording)
{
gifExport.setDelay(1);
gifExport.addFrame();
} else
{
}

//debug to show # of circles on screen
//println(circles.size());
}

int count = 0;
void mouseDragged()
{
//limiting circles
if (random(0, 2) < 1)
{
float mspeed = sqrt(pow((mouseX – pmouseX), 2) + pow((mouseY – pmouseY), 2));
float max = 100;
float min = 20;
float grate = 0.4;
float logistic = (float) (max / (1 + Math.pow(Math.E, (-1 * grate * (mspeed – 10))))) + min;
circles.add(new Circle(mouseX, mouseY, random(0, 1), random(0, 1), logistic + random(-10, 10), count++)); //used to have size random(10, 75)
}
}

boolean recording = false;

void keyTyped()
{
if (key==’s’)
{
saveFrame(“frameCapture-####.png”);
}

if (key==’g’)
{
recording = !recording;
if (!recording)
gifExport.finish();
}
}