fullscreen
Img2Spring.pdeShapeSpringVII.pdeSpringClass.pdeSpringShapes.pde
class Vert
{
PVector pos;
boolean used=false;
Vert(float x, float y){pos = new PVector(x,y);}
}
Spring Img2Spring(PImage img, int startVertex,boolean makeClosed, int everyNth)
{
// create ArrayList of black pixels
int count=0;
Vert V;
ArrayList Points,sorted;
img.loadPixels();
Points = new ArrayList();
for(int j=0;j<img.height;j++) for (int i=0;i<img.width;i++)
{
if (img.pixels[j*img.width+i]==color(0)) Points.add(new Vert(i,j));
}
sorted = new ArrayList();
sorted = SortVert(Points,startVertex);
Spring S;
S = new Spring();
PVector center=new PVector(mouseX-img.width/2,mouseY-img.height/2);
V = (Vert) sorted.get(0);
S.firstPoint(PVector.add(center,V.pos));
for (int i=1;i<sorted.size()-1;i++)
{
V = (Vert) sorted.get(i);
if(i%everyNth==0) S.addPoint(PVector.add(center,V.pos));
}
V = (Vert) sorted.get(sorted.size()-1);
S.lastPoint(PVector.add(center,V.pos),makeClosed);
return S;
}
ArrayList SortVert(ArrayList Unsorted, int start)
{
Vert V,VT,BV;
ArrayList Sorted;
float mind,d;
Sorted = new ArrayList();
V = (Vert) Unsorted.get(start);
Sorted.add(V);
V.used=true;
while (Sorted.size()<Unsorted.size())
{
mind=100000000;
V=(Vert) Sorted.get(Sorted.size()-1);
BV=V;
for (int i=0;i<Unsorted.size();i++)
{
VT = (Vert) Unsorted.get(i);
if (!VT.used)
{
d = V.pos.dist(VT.pos);
if (d<mind)
{
mind=d;
BV=VT;
}
}
}
BV.used=true;
Sorted.add(BV);
}
return Sorted;
}
// Base scirpt and Spring-Physics
// By Asher Salomon 2010
// AsherSalomon@gmail.com
// see openprocessing.com
// The script was then modified by Bejoscha to allow collisions.
int num = 2000; // Biggest number of Points per Spring
int whichOnes;
ArrayList sprs;
Spring S;
PGraphics buffer;
int count=0;
boolean showBuffer=false;
boolean collide=true;
boolean drawFill=true;
boolean pause=false;
boolean useGravity=true;
float step=0.01;
void setup() {
size(500, 400,JAVA2D);
sprs = new ArrayList();
smooth();
buffer = createGraphics(width,height,JAVA2D);
//buffer.smooth();
buffer.background(0);
}
void draw()
{
background(230);
if (!pause)
{
if (sprs.size()>0){
if (drawing) whichOnes=sprs.size()-1;
else whichOnes=sprs.size();
for (int j=0; j<whichOnes; j++)
{
S = ( Spring ) sprs.get(j);
if (collide) S.drawBuffer(S.VertexDraw,S.LineDraw,drawFill,true);
S.Tick();
if (collide) S.drawBuffer(S.VertexDraw,S.LineDraw,drawFill,false);
}
}
}
buffer.beginDraw();
buffer.background(0);
if (sprs.size()>0){
for (int j=0; j<sprs.size(); j++){
S = ( Spring ) sprs.get(j);
S.draw(S.VertexDraw,S.LineDraw,drawFill);
}
if (collide) for (int j=0; j<sprs.size(); j++)
{
S = ( Spring ) sprs.get(j);
S.drawBuffer(S.VertexDraw,S.LineDraw,drawFill,false);
}
}
buffer.endDraw();
if (showBuffer) set(0,0,buffer);
}
boolean drawing = false;
void mousePressed() {
if (drawing == false){
if (mouseButton == LEFT){
S = new Spring(color(10+random(245),10+random(245),10+random(245)));
if (!useGravity) S.gravity=0;
sprs.add( S );
S.firstPoint(new PVector(mouseX,mouseY));
drawing = true;
}
}
if (mouseButton == RIGHT){
if (sprs.size()>0){
for (int j=0; j<sprs.size(); j++){
S = ( Spring ) sprs.get(j);
S.testGrab();
}
}
}
}
void mouseDragged() {
if (drawing)
{
S = ( Spring ) sprs.get(sprs.size()-1);
S.addPoint(new PVector(mouseX,mouseY));
}
}
boolean cleared = true;
void mouseReleased() {
if (mouseButton == LEFT){
if (drawing) {
S = ( Spring ) sprs.get(sprs.size()-1);
S.lastPoint(new PVector(mouseX,mouseY),false);
S.CloseGap(10);
drawing = false;
}
}
if (mouseButton == RIGHT){
if (sprs.size()>0){
for (int j=0; j<sprs.size(); j++){
S = ( Spring ) sprs.get(j);
S.hold = false;
}
}
}
}
void keyReleased(){
Spring S2;
PImage img;
int len,wid,wind;
switch (key)
{
case ' ':
sprs = new ArrayList();
break;
case 'c':
collide=!collide;
break;
case 'p':
pause=!pause;
break;
case '1':
S2 = makeCircle(50,80);
S = makeCircle(50,40);
S.CopyToStore();
S.dX=S2.dX;
S.CopyToNew();
S.warping=true;
sprs.add(S);
break;
case '2':
len = round ( random(50,200) );
wid = round ( random(10,40) );
wind = round (random(4,20) );
S2 = makeSpringSpring(new PVector(mouseX,mouseY),new PVector(mouseX,mouseY+len),wind,wid,10);
S = makeSpringSpring(new PVector(mouseX,mouseY),new PVector(mouseX,mouseY+len/3),wind,wid,10);
// S2 = makeSpringSpring(new PVector(mouseX,mouseY),new PVector(mouseX,mouseY+150),10,25,10);
// S = makeSpringSpring(new PVector(mouseX,mouseY),new PVector(mouseX,mouseY+30),10,25,10);
S.CopyToNew();
S.dX=S2.dX;
S.CopyToStore();
S.warping=true;
sprs.add(S);
break;
case '3':
img = loadImage("H.gif");
S = Img2Spring(img,0,true,3);
S.CopyToNew();
S2 = makeCircle(S.numUsed,35);
S.dX=S2.dX;
S.CopyToStore();
S.Shift(-85,5);
S.warping=true;
S.warplevel=1;
sprs.add(S);
img = loadImage("A.gif");
S = Img2Spring(img,0,true,3);
S.CopyToNew();
S2 = makeCircle(S.numUsed,35);
S.dX=S2.dX;
S.CopyToStore();
S.warping=true;
S.warplevel=1;
sprs.add(S);
img = loadImage("P.gif");
S = Img2Spring(img,0,true,3);
S.CopyToNew();
S2 = makeCircle(S.numUsed,35);
S.dX=S2.dX;
S.CopyToStore();
S.Shift(80,5);
S.warping=true;
S.warplevel=1;
sprs.add(S);
S = Img2Spring(img,0,true,3);
S.CopyToNew();
S2 = makeCircle(S.numUsed,35);
S.dX=S2.dX;
S.CopyToStore();
S.Shift(155,5);
S.warping=true;
S.warplevel=1;
sprs.add(S);
img = loadImage("Y.gif");
S = Img2Spring(img,0,true,3);
S.CopyToNew();
S2 = makeCircle(S.numUsed,35);
S.dX=S2.dX;
S.CopyToStore();
S.Shift(225,5);
S.warping=true;
S.warplevel=1;
sprs.add(S);
break;
case '4':
collide=false;
img = loadImage("Happy.gif");
S = Img2Spring(img,100,false,5);
S.Scale(0.8);
S.VertexDraw=2;
S.LineDraw=1;
S.lineCol=color(230);
S.gravity=0;
S.CopyToNew();
S2 = makeCircle(S.numUsed,35);
S.dX=S2.dX;
S.CopyToStore();
sprs.add(S);
img = loadImage("Birthday.gif");
S = Img2Spring(img,20,false,3);
S.Shift(0,100);
S.gravity=0;
S.Scale(0.8);
S.VertexDraw=2;
S.LineDraw=1;
S.lineCol=color(230);
S.CopyToNew();
S2 = makeCircle(S.numUsed,35);
S.dX=S2.dX;
S.CopyToStore();
sprs.add(S);
break;
case 'a':
for (int j=0; j<sprs.size(); j++){
S = ( Spring ) sprs.get(j);
if (S.hold)
{
S.anchored=!S.anchored;
S.anchorID=S.held;
S.anchor=new PVector(mouseX,mouseY);
}
}
break;
case '+':
for (int j=0; j<sprs.size(); j++)
{
S = ( Spring ) sprs.get(j);
if (S.hold)S.Scale(1.1);
}
break;
case '-':
for (int j=0; j<sprs.size(); j++)
{
S = ( Spring ) sprs.get(j);
if (S.hold)S.Scale(0.9);
}
break;
case 'f':
for (int j=0; j<sprs.size(); j++)
{
S = ( Spring ) sprs.get(j);
if (S.hold)S.FreezeToNew();
}
break;
case 'x':
for (int i=0;i<sprs.size();i++)
{
S = (Spring) sprs.get(i);
S.warping=true;
if (S.warplevel==0) S.warplevel=1;
else S.warplevel=0;
}
break;
case 'g':
useGravity=!useGravity;
for (int j=0; j<sprs.size(); j++)
{
S = ( Spring ) sprs.get(j);
if (useGravity) S.gravity = 0.00001;
else S.gravity=0;
}
break;
case 's':
for (int j=0; j<sprs.size(); j++)
{
S = ( Spring ) sprs.get(j);
if (S.hold) if (!S.closed) S.straighten=!S.straighten;
}
break;
}
}
class Spring{
int pCount=0;
float k = 0.025;
float c = 0.2;
float I = 100;
float kw = 20.0;
float cw = 0.02;
float damp=0.5;
float gravity = 0.00001;
int VertexDraw=0;
int LineDraw=1;
int iterations = 175;
int numUsed = 0;
PVector[] X = new PVector[num];
PVector[] dX = new PVector[num];
PVector[] dXold = new PVector[num];
PVector[] dXnew = new PVector[num];
PVector[] V = new PVector[num];
PVector[] F = new PVector[num];
float[] phi = new float[num];
float[] omega = new float[num];
float[] tork = new float[num];
boolean closed = false;
boolean straighten=false;
boolean warping=false;
boolean anchored=false;
int anchorID=0;
PVector anchor;
float warplevel=0;
color fillCol;
color vertCol;
color lineCol;
color idCol;
void CreateSpring(color IDc){
idCol = IDc;
for (int i=0; i<num; i++) {
X[i] = new PVector();
V[i] = new PVector();
F[i] = new PVector();
dX[i] = new PVector();
dXold[i]= new PVector();
dXnew[i]= new PVector();
}
fillCol = color(int(random(0,255)),
int(random(0,255)),
int(random(0,255)));
vertCol = color(255,0,0);
lineCol = color(0,0,0);
anchor=new PVector(0,0);
}
boolean hold = false;
int held;
Spring(){CreateSpring(color(255,random(0,255),random(0,255)));}
Spring(color IN){CreateSpring(IN);}
void physics() {
for (int i=0; i<=numUsed; i++) {
F[i].y += gravity;
}
int ione = 1;
if (closed==true){ione=0;}
for (int i=ione; i<=numUsed; i++) {
int linked = i-1;
if (i==0){linked=numUsed;}
PVector io = new PVector(cos(phi[i]), sin(phi[i]));
PVector jo = new PVector(-sin(phi[i]), cos(phi[i]));
PVector delta = PVector.sub(X[i], X[linked]);
PVector deltaPrime = new PVector();
deltaPrime.x = delta.x * io.x + delta.y * io.y;
deltaPrime.y = delta.x * jo.x + delta.y * jo.y;
PVector ddX = PVector.sub(deltaPrime, dX[linked]);
PVector GPrime = PVector.mult(ddX, -k);
PVector G = new PVector();
G.x = GPrime.x * io.x + GPrime.y * jo.x;
G.y = GPrime.x * io.y + GPrime.y * jo.y;
G.sub(PVector.mult(PVector.sub(V[i], V[linked]), c));
F[i].add(G);
F[linked].sub(G);
// The principal of equivilent systems
// may be helpfull for this model.
PVector curl = delta.cross(G);
float Gw = (phi[i] - phi[linked]) * kw;
float dW = (omega[i] - omega[linked]) * cw;
tork[i] -= Gw + curl.z - dW;
tork[linked] += Gw - curl.z - dW;
}
// BoarderCollsion
for (int i=0; i<=numUsed; i++)
{
if (X[i].x < 0) {
F[i].x += (-X[i].x) * k - V[i].x * c;
F[i].y -= V[i].y * c /3;
}
if (X[i].x > width) {
F[i].x -= (X[i].x - width) * k + V[i].x * c;
F[i].y -= V[i].y * c /3;
}
if (X[i].y < 0) {
F[i].y += (-X[i].y) * k - V[i].y * c;
F[i].x -= V[i].x * c /3;
}
if (X[i].y > height-1) {
F[i].y -= (X[i].y - height+1) * k + V[i].y * c;
F[i].x -= V[i].x * c /3;
}
}
// BufferCollision
for (int i=0; i<=numUsed; i++)
{
PVector PV = new PVector(round(X[i].x+V[i].x+F[i].x),round(X[i].y+V[i].y+F[i].y));
color testCol = buffer.get(round(PV.x),round(PV.y));
if ((testCol!=idCol)&&(testCol!=color(0))&&(PV.x>=0)&&(PV.y>=0)&&(PV.x<width)&&(PV.y<height))
{
// find collision spring
Spring CS=this;
for (int j=0; j<sprs.size(); j++)
{
CS = ( Spring ) sprs.get(j);
if (CS.CheckSpringCol(testCol)) break;
}
// find closest 2 vertices of collision spring
int j[]=CS.FindClosestVertex(PV,2);
// calculate collision direction vectors
PVector C1 = PVector.sub(CS.X[j[0]],PV);
PVector C2 = PVector.sub(CS.X[j[1]],PV);
C1.normalize();
C2.normalize();
// calculate transferred velocity vectors
float strength=C1.dot(V[i]);
C1.mult(strength*damp);
strength=C2.dot(V[i]);
C2.mult(strength*damp);
V[i].mult(-1*damp);
CS.V[j[0]].add(C1);
CS.V[j[1]].add(C2);
F[i].mult(-1*damp);
}
}
if (hold) {
PVector M = new PVector();
M.x = mouseX;
M.y = mouseY;
PVector fromMouse = PVector.sub(X[held], M);
F[held].sub(PVector.mult(fromMouse, k));
F[held].sub(PVector.mult(V[held], c));
}
if (anchored)
{
PVector fromA = PVector.sub(X[anchorID], anchor);
F[anchorID].sub(PVector.mult(fromA, k));
F[anchorID].sub(PVector.mult(V[anchorID], c));
}
for (int i=0; i<=numUsed; i++) {
V[i].add(F[i]);
F[i].x = 0;
F[i].y = 0;
X[i].add(V[i]);
omega[i] += tork[i] / I;
tork[i] = 0;
phi[i] += omega[i];
omega[i] *= 0.999;
if (i>0) {phi[i] = constrain(phi[i],
phi[i-1] - PI / 2,
phi[i-1] + PI / 2);}
omega[i] = constrain(omega[i], -PI/256, PI/256);
}
}
void Tick()
{
for (int ic=0;ic<iterations;ic++) this.physics();
if (warping)
{
for (int i=0; i<=numUsed; i++)
{
dX[i].x=lerp(dXold[i].x,dXnew[i].x,warplevel); // WORK HERE
dX[i].y=lerp(dXold[i].y,dXnew[i].y,warplevel);
}
}
if (straighten) for (int i=1; i<=numUsed; i++)
{
PVector Old = new PVector(dX[i].x,dX[i].y);
PVector DD = new PVector(dX[i-1].x,dX[i-1].y);
DD.normalize();
float P = abs(PVector.dot(DD,dX[i]));
if (P<=0.1) P=0.1;
float L = dX[i].mag();
DD.mult(P);
dX[i].sub(DD);
if (dX[i].mag()>P/100) dX[i].mult(0.7);
dX[i].add(DD);
dX[i].normalize();
dX[i].mult(L);
dXnew[i]=new PVector(dX[i].x,dX[i].y);
}
}
int[] FindClosestVertex(PVector pos,int rank)
{
float d=width*height;
float d2;
int[] best;
best = new int[rank];
for (int j=0;j<rank;j++) best[j]=0;
for (int i=0; i<=numUsed; i++)
{
d2=PVector.dist(X[i],pos);
if (d2<=d)
{
for (int j=rank-1;j>0;j--)
{
best[j]=best[j-1];
}
d=d2;
best[0]=i;
}
}
return best;
}
boolean CheckSpringCol(color in)
{
if (in==idCol) return true;
else return false;
}
void CopyToStore()
{
for (int i=0; i<=numUsed; i++)
{
dXold[i].x = dX[i].x;
dXold[i].y = dX[i].y;
}
}
void CopyToNew()
{
for (int i=1; i<=numUsed; i++)
{
dXnew[i].x = dX[i].x;
dXnew[i].y = dX[i].y;
}
}
void Scale(float sc)
{
for (int i=0; i<=numUsed; i++) dX[i].mult(sc);
CopyToNew();
warplevel=1;
warping=true;
}
void FreezeToNew()
{
for (int i=1; i<=numUsed; i++)
dXnew[i-1] = PVector.sub(X[i],X[i-1]);
}
void CopyFromStore()
{
for (int i=0; i<=numUsed; i++)
{
dX[i].x = dXold[i].x;
dX[i].y = dXold[i].y;
}
}
void CopyFromNew()
{
for (int i=1; i<=numUsed; i++)
{
dX[i].x = dXnew[i].x;
dX[i].y = dXnew[i].y;
}
}
void draw(int drawPointsStr, int drawConnectStr, boolean drawFill)
{
if ((closed)&&(drawFill)) fill(fillCol, 150);
else noFill();
if (drawConnectStr>0)
{
stroke(lineCol);
if (hold) strokeWeight(drawConnectStr*3);
else strokeWeight(drawConnectStr);
beginShape();
for (int i=0; i<=numUsed; i++) {
vertex(X[i].x, X[i].y);
}
if (closed==false){
endShape();
}else{
endShape(CLOSE);
}
strokeWeight(1);
}
if (drawPointsStr>0)
{
stroke(vertCol);
strokeWeight(drawPointsStr);
for (int i=0; i<=numUsed; i++) point(X[i].x, X[i].y);
strokeWeight(1);
}
}
void drawBuffer(int drawPointsStr, int drawConnectStr, boolean drawFill, boolean Clear)
{
color c;
if (Clear) c=color(0);
else c=color(idCol);
if ((closed)&&(drawFill)) buffer.fill(c);
else buffer.noFill();
if (drawConnectStr>0)
{
buffer.stroke(c);
buffer.strokeWeight(drawConnectStr);
buffer.beginShape();
for (int i=0; i<=numUsed; i++) {
buffer.vertex(X[i].x, X[i].y);
}
if (closed==false){
buffer.endShape();
}else{
buffer.endShape(CLOSE);
}
buffer.strokeWeight(1);
}
if (drawPointsStr>0)
{
buffer.stroke(c);
buffer.strokeWeight(drawPointsStr);
for (int i=0; i<=numUsed; i++) buffer.point(X[i].x, X[i].y);
buffer.strokeWeight(1);
}
}
void firstPoint(PVector NewPoint)
{
numUsed = 0;
X[numUsed] = NewPoint;
V[numUsed].x = 0;
V[numUsed].y = 0;
}
void addPoint(PVector NewPoint)
{
if (numUsed < num-1) {
numUsed++;
X[numUsed] = NewPoint;
V[numUsed].x = 0;
V[numUsed].y = 0;
dX[numUsed-1] = PVector.sub(X[numUsed], X[numUsed-1]);
}
}
void lastPoint(PVector NewPoint, boolean Makeclosed){
addPoint(NewPoint);
dX[numUsed] = PVector.sub(X[0], X[numUsed]);
closed = Makeclosed;
drawing = false;
cleared = false;
CopyToStore();
CopyToNew();
}
void Shift(PVector SVec)
{
for (int i=0; i<=numUsed; i++) X[i].add(SVec);
}
void Shift(float x,float y)
{
Shift(new PVector(x,y));
}
void CloseGap(int MaxGap)
{
if (dX[numUsed].mag()<MaxGap) closed = true;
else closed=false;
}
boolean testGrab(){
for (int i=0; i<=numUsed; i++) {
PVector M = new PVector();
M.x = mouseX;
M.y = mouseY;
PVector fromMouse = PVector.sub(X[i], M);
if (fromMouse.mag() < 5) {
hold = true;
held = i;
}
}
boolean val = false;
if (hold==true){val = true;}
return val;
}
}
Spring makeSpringSpring(PVector SpringStart, PVector SpringEnd, int Windings,int len, int joints)
{
Spring S;
PVector AddVec;
float angle;
float rotAngle;
int Points = Windings*joints;
S = new Spring();
PVector Point=SpringStart;
// Calculate Spring geometry defined by vector Start->End, the number of windings, and the length of windings
AddVec = PVector.sub(SpringEnd,SpringStart);
float h = AddVec.mag()/Windings;
angle = degrees(atan(abs(h)/len));
rotAngle= PVector.angleBetween(AddVec,new PVector(1,0));
if (AddVec.y>0) rotAngle=TWO_PI-rotAngle;
AddVec = new PVector(len*cos(radians(angle)),(-1)*len*sin(radians(angle))); //-1 because y is defined DOWNWARDS on the screen!
for (int i=0;i<Points;i+=joints)
{
AddVec.normalize();
AddVec.mult(len/joints);
for(int j=1;j<=joints;j++)
{
Point = new PVector(Point.x-AddVec.x*sin(rotAngle)-AddVec.y*cos(rotAngle),Point.y+AddVec.y*sin(rotAngle)-AddVec.x*cos(rotAngle));
if ((i!=0) || (j>(joints/2))) S.addPoint(Point);
else S.firstPoint(Point);
}
AddVec.x*=-1;
}
AddVec.normalize();
AddVec.mult(len/joints);
for(int j=1;j<joints/2;j++)
{
Point = new PVector(Point.x-AddVec.x*sin(rotAngle)-AddVec.y*cos(rotAngle),Point.y+AddVec.y*sin(rotAngle)-AddVec.x*cos(rotAngle));
S.addPoint(Point);
}
Point = new PVector(Point.x-AddVec.x*sin(rotAngle)-AddVec.y*cos(rotAngle),Point.y+AddVec.y*sin(rotAngle)-AddVec.x*cos(rotAngle));
S.lastPoint(Point,false);
return S;
}
Spring makeRandomSpring(int Points)
{
Spring S;
Points = max(3,Points);
S = new Spring();
PVector Point = new PVector(mouseX,mouseY);
S.firstPoint(Point);
PVector AddVec= new PVector(random(0,5),random(0,5));
for (int i=1;i<Points-1;i++)
{
S.addPoint(Point);
float len = AddVec.mag();
AddVec.normalize();
float angle;
angle= PVector.angleBetween(AddVec,new PVector(1,0));
if (AddVec.y>0) angle=TWO_PI-angle;
angle+=random(0,PI/(Points*0.3));
AddVec = new PVector(len*cos(angle),(-1)*len*sin(angle)); //-1 because y is defined DOWNWARDS on the screen!
Point = new PVector(Point.x+AddVec.x,Point.y+AddVec.y);
}
S.lastPoint(Point,false);
S.CloseGap(10);
return S;
}
Spring makeCircle(int Points, float radius)
{
Spring S;
S = new Spring();
PVector center=new PVector(mouseX,mouseY);
PVector R=new PVector(1,0);
S.firstPoint(PVector.add(center,PVector.mult(R,radius)));
for (int i=1;i<Points;i++)
{
R=new PVector(cos(i*TWO_PI/Points),sin(i*TWO_PI/Points));
if (i<Points-1) S.addPoint(PVector.add(center,PVector.mult(R,radius)));
else S.lastPoint(PVector.add(center,PVector.mult(R,radius)),false);
}
S.closed=true;
return S;
}