• fullscreen
• Circle.pde
• Constraints.pde
• Grid.pde
• GridObject.pde
• Physics.pde
• PointMass.pde
• dough.pde
• ```/* Circle class */
class Circle implements GridObject {
/* Which PointMass is the circle attached to? */
boolean attachedToPointMass = false;
int attachedPointMass;

float damping = 0.95;
/* Constructor */
Circle (float r) {
//position = pos.get();
}
/* Constraint solving algorithm */
// Here we find out if the circle is overlapping the surfacem, and act accordingly.
void solveConstraints (boolean preserveImpulse) {
// set the wheel's position to it's attached PointMass's position
float x = xPos[attachedPointMass];
float y = yPos[attachedPointMass];

float prevX = lastX[attachedPointMass];
float prevY = lastY[attachedPointMass];

// Make sure it isn't outside of the screen
float vx = 0, vy = 0;
if (preserveImpulse) {
vx = (prevX - x) * damping;
vy = (prevY - y) * damping;
}

if (x - radius < 0) {
if (preserveImpulse)
prevX = x - vx;
}
if (x + radius > width) {
x = 2 * (width - radius) - x;
if (preserveImpulse)
prevX = x - vx;
}
if (y - radius < 0) {
if (preserveImpulse)
prevY = y - vy;
}
if (y + radius > height) {
y = 2 * (height - radius) - y;
if (preserveImpulse)
prevY = y - vy;
}

xPos[attachedPointMass] = x;
yPos[attachedPointMass] = y;

lastX[attachedPointMass] = prevX;
lastY[attachedPointMass] = prevY;

grid.update(this);
List nearBy = grid.nearByObjects(x,y);
for (int i = 0; i < nearBy.size(); i++) {
Circle nextPM = (Circle) nearBy.get(i);
avoid(nextPM, preserveImpulse);
}
}
// Checks for a collision between two circles, and solves for it
// Also creates a link if their velocities are similar.
void avoid (Circle what, boolean preserveImpulse) {
if (what != this) {
float x = xPos[attachedPointMass];
float y = yPos[attachedPointMass];

float otherX = xPos[what.attachedPointMass];
float otherY = yPos[what.attachedPointMass];

float diffX = x - otherX;
float diffY = y - otherY;

float diffSquared = diffX * diffX + diffY * diffY;

if (diffSquared <= sq(radius + what.radius)) { // first make sure they're intersecting

// Previous velocity
float v1x = x - lastX[attachedPointMass];
float v1y = y - lastY[attachedPointMass];
float v2x = otherX - lastX[what.attachedPointMass];
float v2y = otherY - lastY[what.attachedPointMass];

// distance between centers
float d = sqrt(diffSquared);

// minimum translation distance to push balls apart after intersecting
float mtdX;
float mtdY;
if (d == 0) {
diffY = 0;
}

mtdX = diffX * difference;
mtdY = diffY * difference;

// resolve intersection
float im1 = 1f / 2f; // inverse mass quantities
float im2 = 1f / 2f;

// push-pull them based on mass
xPos[attachedPointMass] += mtdX * (im1 / (im1 + im2));
yPos[attachedPointMass] += mtdY * (im1 / (im1 + im2));

xPos[what.attachedPointMass] -= mtdX * (im1 / (im1 + im2));
yPos[what.attachedPointMass] -= mtdY * (im1 / (im1 + im2));

if (preserveImpulse) { // preserve velocities/impulse based on http://codeflow.org/entries/2010/nov/29/verlet-collision-with-impulse-preservation/
float f1 = (damping * (diffX * v1x + diffY * v1y)) / diffSquared;
float f2 = (damping * (diffX * v2x + diffY * v2y)) / diffSquared;

v1x += f2 * diffX - f1 * diffX;
v2x += f1 * diffX - f2 * diffX;
v1y += f2 * diffY - f1 * diffY;
v2y += f1 * diffY - f2 * diffY;

lastX[attachedPointMass] = xPos[attachedPointMass] - v1x;
lastY[attachedPointMass] = yPos[attachedPointMass] - v1y;

lastX[what.attachedPointMass] = xPos[what.attachedPointMass] - v2x;
lastY[what.attachedPointMass] = yPos[what.attachedPointMass] - v2y;
}

// Now we see if they're moving at similar speeds
float vX = xPos[attachedPointMass] - lastX[attachedPointMass];
float vY = yPos[attachedPointMass] - lastY[attachedPointMass];
float otherVX = xPos[what.attachedPointMass] - lastX[what.attachedPointMass];
float otherVY = yPos[what.attachedPointMass] - lastY[what.attachedPointMass];

float diffVX = vX - otherVX;
float diffVY = vY - otherVY;

if (diffVX * diffVX + diffVY * diffVY < 1) {

// make sure we're not already attached
return;

for (int i = 0; i < links[attachedPointMass].length; i++) {
break;
}
}
return;

}
}
}
}

/* The circle's draw function */
void draw () {
noFill();
stroke(255);
}

/* Set the attached PointMass */
void attachToPointMass (int p) {
attachedPointMass = p;
}

float getX() {
return xPos[attachedPointMass];
}
float getY() {
return yPos[attachedPointMass];
}
float getLastX() {
return lastX[attachedPointMass];
}
float getLastY() {
return lastY[attachedPointMass];
}
}
```
```/* Constraints */
// includes links, boundary, and pinned (unused here) constraints
void solveConstraints () {
for (int pmIndex = 0; pmIndex < pointMassCount; pmIndex++) { // loop through every PointMass

float deltaX = xPos[pmIndex] - xPos[otherPMIndex];
float deltaY = yPos[pmIndex] - yPos[otherPMIndex];

float d = sqrt(deltaX * deltaX + deltaY * deltaY);

// break the link and go on to the next one if it stretched too far.
continue;
}

float deltaXScalar = deltaX * scalar;
float deltaYScalar = deltaY * scalar;

xPos[pmIndex] += deltaXScalar;
yPos[pmIndex] += deltaYScalar;

xPos[otherPMIndex] -= deltaXScalar;
yPos[otherPMIndex] -= deltaYScalar;
}
}

/* Boundary */
if (yPos[pmIndex] < 1)
yPos[pmIndex] = 2 * (1) - yPos[pmIndex];
if (yPos[pmIndex] > height-1)
yPos[pmIndex]= 2 * (height - 1) - yPos[pmIndex];
if (xPos[pmIndex] > width-1)
xPos[pmIndex] = 2 * (width - 1) - xPos[pmIndex];
if (xPos[pmIndex] < 1)
xPos[pmIndex] = 2 * (1) - xPos[pmIndex];

/* Pinned */
if (pinned[pmIndex]) {
xPos[pmIndex] = pinnedX[pmIndex];
yPos[pmIndex] = pinnedY[pmIndex];
}

}
}
```
```// Grid is an algorithm I made about 2 years ago
// I revamped it, optimized it, and cleaned it significantly
// it takes objects, stores them in separate cells,
// and when called on, will provide a list of objects in a cell and cells adjacent to that cell
// it allows to save resources by only having collision checks between objects potentially close enough

class Grid {
// The cells map keeps track of where each object is
// The grid list keeps track of which objects are in each cell

Map cells = new HashMap(); // Key: Object object, Value: grid position... (x/scale,y/scale)
// holds the position of the object in the grid. This makes sure that the object gets removed and replaced each time
// it changes position in the grid.

List [][] grid; // grid[x/scale][y/scale] = List of objects
int cellSize;
Grid (int cellSize, List<GridObject> objects) {
this.cellSize = cellSize;
grid = new ArrayList[int(width/cellSize)+1][int(height/cellSize)+1];

// construct a list for each cell
for (int x = 0; x < grid.length; x++) {
for (int y = 0; y < grid[x].length; y++) {
grid[x][y] = new ArrayList(); // a list of objects for every cell
}
}

// add the objects to their cells
// and keep track of which cell each object is in
for (GridObject object : objects) {
cells.put(object, new PVector(int(object.getX() / cellSize), int(object.getY() / cellSize)));
}
}
void drawGrid () {
stroke(50);
for (int x = 0; x < grid.length; x++) {
for (int y = 0; y < grid[x].length; y++) {
noFill();
rect(x*cellSize, y*cellSize,cellSize,cellSize);
}
}
}
// Update needs to be called on by each object after they update their position
// this makes sure that the object is still in the correct cell
void update(GridObject object) {
// first check if it needs to be moved

// Find cell we stored it in
PVector oldCellPos = (PVector) cells.get(object);
int oldCellX = (int)oldCellPos.x;
int oldCellY = (int)oldCellPos.y;

// find cell it is in currently
int cellX = (int)(object.getX() / cellSize);
int cellY = (int)(object.getY() / cellSize);

// if they match, stop there
if (cellX == oldCellX && cellY == oldCellY)
return;

// safety check
if (cellX >= 0 && cellX < grid.length && cellY >= 0 && cellY < grid[cellX].length) {
// if the grid the object is in now doesn't contain that object...
if (!grid[cellX][cellY].contains(object)) {
// add the object and remove the object from its former cell
grid[oldCellX][oldCellY].remove(object);
// memorize which cell the object is in
cells.put(object, new PVector(cellX, cellY));
}
}
}

// Find a list of nearby objects
// goes through each cell adjacent to the current cell, and the current cell,
// and generates a list of objects contained in those cells
List nearByObjects (float x, float y) {
List nearBy = new ArrayList();
int cellX = (int)(x / cellSize);
int cellY = (int)(y / cellSize);

if (cellX >= 0 && cellX < grid.length) {
// center column
if (cellY >= 0 && cellY < grid[cellX].length) {
// middle
for (Object object : grid[cellX][cellY])
// top
// cellY+1 >= 0 can be assumed since cellY >= 0 is checked in this block
if (cellY+1 < grid[cellX].length) {
for (Object object : grid[cellX][cellY + 1])
}
}
// bottom
if (cellY-1 >= 0 && cellY-1 < grid[cellX].length) {
for (Object object : grid[cellX][cellY - 1])
}

// right column
if (cellX+1 < grid.length) {
if (cellY >= 0 && cellY < grid[cellX + 1].length) {
// middle right
for (Object object : grid[cellX + 1][cellY])
if (cellY+1 < grid[cellX + 1].length) {
// top right
for (Object object : grid[cellX + 1][cellY + 1])
}
}
if (cellY-1 >= 0 && cellY-1 < grid[cellX + 1].length) {
// bottom right
for (Object object : grid[cellX + 1][cellY - 1])
}
}
}

// left column
if (cellX-1 >= 0 && cellX-1 < grid.length) {
if (cellY >= 0 && cellY < grid[cellX - 1].length) {
// center left
for (Object object : grid[cellX - 1][cellY])
// top left
if (cellY+1 < grid[cellX - 1].length) {
for (Object object : grid[cellX - 1][cellY + 1])
}
}
if (cellY-1 >= 0 && cellY-1 < grid[cellX - 1].length) {
// bottom left
for (Object object : grid[cellX - 1][cellY - 1])
}
}
return nearBy;
}
}
```
```// Used by Grid so we can hook it up with any other program
// To use, type "implements GridObject" after "class Object," like in Bird
// and add float getX() and float getY() methods to return its position
// create a Grid with a list of GridObjects in the constructor, and a cell size with the largest distance between two objects
// (for a ball simulator, that would be the radius of the largest ball x 2
// store a Grid object somewhere, and have each object use the grid's update() method to store its location
public interface GridObject {
float getX();
float getY();
}
```
```/* Physics */
// Here we apply velocity integration

/* Accelerate */
// (mainly used for gravity)
void accelerate(float timestepSquared) {
for (int pmIndex = 0; pmIndex < pointMassCount; pmIndex++) {
accY[pmIndex] += gravity;

float nextX = xPos[pmIndex] + 0.5 * accX[pmIndex] * timestepSquared;
float nextY = yPos[pmIndex] + 0.5 * accY[pmIndex] * timestepSquared;

xPos[pmIndex] = nextX;
yPos[pmIndex] = nextY;

accX[pmIndex] = 0;
accY[pmIndex] = 0;
}
}
/* Intertia */
// Objects in motion will stay in motion
void inertia() {
for (int pmIndex = 0; pmIndex < pointMassCount; pmIndex++) {
// find velocity
float velX = xPos[pmIndex] - lastX[pmIndex];
float velY = yPos[pmIndex] - lastY[pmIndex];

// add the velocity to the position
float nextX = xPos[pmIndex] + velX;
float nextY = yPos[pmIndex] + velY;

// reset last position
lastX[pmIndex] = xPos[pmIndex];
lastY[pmIndex] = yPos[pmIndex];

xPos[pmIndex] = nextX;
yPos[pmIndex] = nextY;
}
}
/* Interactions */
// allow the user to turn and knead the dough
void updateInteractions() {
for (int pmIndex = 0; pmIndex < pointMassCount; pmIndex++) {
if (!pinned[pmIndex]) {
if (mousePressed) {
// we use distPointToSegmentSquared to find out how close the pointmass is to the segment mouse to last mouse position
// this makes the interactions consistent and not spotty (esp. at low framerates)
float distanceSquared = distPointToSegmentSquared(pmouseX, pmouseY, mouseX, mouseY, xPos[pmIndex], yPos[pmIndex]);
if (mouseButton == LEFT) {
if (distanceSquared < mouseInfluenceSquared) {
// Set the pointmass velocity to the direction the mouse is moving
lastX[pmIndex] = xPos[pmIndex] - (mouseX-pmouseX) * mouseInfluenceScalar;
lastY[pmIndex] = yPos[pmIndex] - (mouseY-pmouseY) * mouseInfluenceScalar;
}
}
else {
if (distanceSquared < mouseTearInfluenceSquared) {
// remove links if the user is right clicking
for (int lnk = 0; lnk < linkStiff[pmIndex].length; lnk++)
}
}
}
}
}
}
// Credit to: http://www.codeguru.com/forum/showpost.php?p=1913101&postcount=16
float distPointToSegmentSquared (float lineX1, float lineY1, float lineX2, float lineY2, float pointX, float pointY) {
if (lineX1 == lineX2 && lineY1 == lineY2)
return sq(pointX - lineX1) + sq(pointY - lineY1);

float vx = lineX1 - pointX;
float vy = lineY1 - pointY;
float ux = lineX2 - lineX1;
float uy = lineY2 - lineY1;

float len = ux*ux + uy*uy;
float det = (-vx * ux) + (-vy * uy);
if ((det < 0) || (det > len)) {
ux = lineX2 - pointX;
uy = lineY2 - pointY;
return min(vx*vx+vy*vy, ux*ux+uy*uy);
}

det = ux*vy - uy*vx;
return (det*det) / len;
}
```
```/* PointMass Variables */
float [] xPos = new float [pointMassCount];
float [] yPos = new float [pointMassCount];
float [] lastX = new float [pointMassCount];
float [] lastY = new float [pointMassCount];
float [] accX = new float [pointMassCount];
float [] accY = new float [pointMassCount];

boolean [] pinned = new boolean [pointMassCount];
float [] pinnedX = new float [pointMassCount];
float [] pinnedY = new float [pointMassCount];

/* Create a Point Mass */
// Create a new point mass
void initParticle (int index, float x, float y) {
xPos[index] = x;
yPos[index] = y;

lastX[index] = x;
lastY[index] = y;

accX[index] = 0;
accY[index] = 0;
}
// attach two pointmasses
void attach (int index1, int index2, float restingDist, float stiffness) {
}
/* Render */
// render a point's links (or the point, if there are no points)
void renderPoint (int index) {
vertex(xPos[index], yPos[index]);
}
}
vertex(xPos[index], yPos[index]);
vertex(xPos[index], yPos[index]);
}
}
// check if a link exists between two point masses
boolean linked(int index1, int index2) {
for (int i = 0; i < links[index1].length; i++) {
return true;
return true;
}
return false;
}
```
```/*
Dough
Written by Jared "BlueThen" C. on July 8, 2012

http://bluethen.com
http://openprocessing.org/portal/?userID=3044
http://hawkee.com/profile/37047/

Email me: bluethen (@) gmail . com

Click and drag to interact
'g' to toggle gravity
*/

int pointMassCount = 900; // how many pointmasses are there?

float stiffness = 0.1; // (0 .. 1) Stiffness between each link
int maxLinks = 4; // how many links is each pointmass allowed to?
int circleRadius = 6; // 1/2 minimum distance allowed between any two pointmasses
float maxLinkDist = 40; // distance a link can stretch before breaking

float gravity = 980;

float mouseInfluenceSquared = sq(60);
float mouseTearInfluenceSquared = sq(10);

/* Timestep stuff */
long previousTime;
long currentTime;
final int fixedDeltaTime = 15;
float fixedDeltaTimeSeconds = (float)fixedDeltaTime / 1000.0;
float timestepSquared = sq(fixedDeltaTimeSeconds);
int leftOverDeltaTime = 0;

int constraintAccuracy = 1;

float mouseInfluenceScalar; // set during timestep calculation

/* Grid stuff */
// see Grid.pde
Grid grid;
List<GridObject> objects;

// performance tracking
float updateTime = 0;
float renderTime = 0;
int lastPrint = 0;

/* Setup */
// initialize all our stuff
void setup () {
size(640,480, JAVA2D); // P2D and OPENGL are way faster, but lots of people can't run it

// crate an objects
objects = new ArrayList<GridObject>();

for (int i = 0; i < pointMassCount; i++) {
// create our pointmass
initParticle(i, random(width), random(height));

// make it a ball
circle.attachToPointMass(i);

// add it to our objects
}

// create our grid
}
/* "Draw" */
// also "Physics"
void draw() {
/******** Physics ********/
/*
Timestep inspired by Glenn Fiedler
http://gafferongames.com/game-physics/fix-your-timestep/
Velocity preservation of ball-to-ball collision inspired by Florian Boesch
http://codeflow.org/entries/2010/nov/29/verlet-collision-with-impulse-preservation/
*/
currentTime = millis();
long deltaTimeMS = currentTime - previousTime;
previousTime = currentTime; // reset previousTime
// Break up the elapsed time into constant-sized timesteps
int timeStepAmt = (int)((float)(deltaTimeMS + leftOverDeltaTime) / (float)fixedDeltaTime);

timeStepAmt = min(timeStepAmt, 5);

// keep track of left over elapsed time for the next frame
leftOverDeltaTime += (int)deltaTimeMS - (timeStepAmt * fixedDeltaTime);

mouseInfluenceScalar = 1.0 / timeStepAmt;

for (int iteration = 1; iteration <= timeStepAmt; iteration++) {

for (int c = 0; c < constraintAccuracy; c++)
solveConstraints();

// update mouse-to-pointmass interactions
updateInteractions();

// accelerations (gravity)
accelerate(timestepSquared);

// solve circle-to-circle collisions with no velocity preservation
for (int c = 0; c < 2; c++)
for (GridObject go : objects) {
if (go instanceof Circle)
((Circle)go).solveConstraints(false);
}

// apply inertia
inertia();

// solve circle-to-circle collisions with velocity preservation
for (GridObject go : objects) {
if (go instanceof Circle)
((Circle)go).solveConstraints(true);
}

} // end timestep
long updateEnd = millis();

/* Rendering */
long renderStart = millis();
// clear background
background(0);
// use white for drawing
stroke(255);

beginShape(LINES);
for (int pmIndex = 0; pmIndex < pointMassCount; pmIndex++)
renderPoint(pmIndex);
endShape();
long renderEnd = millis();

// perfromance
// calculate performances of each part of the program
// the times for this frame is factored in only as a small portion
updateTime = (updateTime * 29 + (updateEnd - updateStart)) / 30;
renderTime = (renderTime * 29 + (renderEnd - renderStart)) / 30;
// Print performances every few seconds
if (lastPrint != second() && second() % 4 == 0 && second() != 1) {
lastPrint = second();
float total = updateTime + renderTime;
println("Update Time: " + (int)updateTime + "ms (" + int(100*updateTime/total) + "%)" +
" | Render Time: " + (int)renderTime + "ms (" + int(100*renderTime/total) + "%)");
println("Total " + (int)total + "ms");
println("Framerate: " + (int)frameRate);
}
}
void toggleGravity () {
if (gravity > 0)
gravity = 0;
else
gravity = 392;
}
void keyPressed() {
if ((key == 'g') || (key == 'G'))
toggleGravity();
}
```

### tweaks (0)

This sketch is running as Java applet, exported from Processing.

Report Sketch

Report for inappropriate content

Your have successfully reported the sketch. Thank you very much for helping to keep OpenProcessing clean and tidy :)

## Dough

48

Dough, sort of a hybrid between Balls and Curtain.

Click and drag to interact.

http://bluethen.com

Josue Page
15 Oct 2012
Really nice!!
not_55
20 Oct 2012
Amazing, thanks for sharing! (add some user instructions though!) :D
Jared Counts
20 Oct 2012
not_55: I didn't think to do that! Thanks.
Dushan Milic
17 Oct 2013
Fantastic!
Why u
6 Jun 2014
Geniusss!!! Did you do any commercial works?
Why u
6 Jun 2014
Geniusss!!! Did you do any commercial works?
Jared Counts
12 Jun 2014
Hey Why u.

More academic than commercial, but last semester I got to work with the wonderful Tangible Media Group at MIT's Media Lab on their TRANSFORM. http://tangible.media.mit.edu/project/transform/

This summer I'm interning at Intel.
oggy
3 Jul 2014
wonderful
Julián Puppo
1 Mar 2015
cannot find a class o type name "List"
You need to login/register to comment.