Verlet Integration (aka Ragdoll Physics) converted to Processing
Didn't get much (any =\) love last time I posted this. I've done a lot since and maybe more will be interested. For those who don't know, Processing is the upcomming easy-to-use Java (which is what AS is based on) writing environment/complier. If you don't have it please check out the site and download the FREE beta. It will only work on the latest version.
Why Processing? The power. Java may be a tad harder to learn, but the reward is much greater. Running a similar script in Flash ran slowly for me on my 1.5ghz Mac, but in Processing it soars at 60 fps. Nuff said, here it is:
Code:
Point[] pL;
Segment[] sL;
Circle[] cL;
void setup() {
size(600, 400);
framerate(60);
stroke(20, 60, 40);
ellipseMode(CENTER);
noSmooth();
noFill();
pL = new Point[14];
pL[0] = new Point(0, 0, 0);
pL[1] = new Point(50, 0, 0);
pL[2] = new Point(50, 50, 0);
pL[3] = new Point(0, 50, 0);
pL[4] = new Point(0, 100, 25);
pL[5] = new Point(0, 150, 25);
pL[6] = new Point(0, 200, 25);
pL[7] = new Point(0, 250, 25);
pL[8] = new Point(0, 300, 25);
pL[9] = new Point(0, 350, 25);
pL[10] = new Point(0, 400, 25);
pL[11] = new Point(0, 450, 25);
pL[12] = new Point(0, 500, 25);
pL[13] = new Point(550, 350, 50);
sL = new Segment[14];
sL[0] = new Segment(0, 1, true);
sL[1] = new Segment(1, 2, true);
sL[2] = new Segment(2, 3, true);
sL[3] = new Segment(3, 0, true);
sL[4] = new Segment(0, 2, false);
sL[5] = new Segment(1, 3, false);
sL[6] = new Segment(4, 5, true);
sL[7] = new Segment(5, 6, true);
sL[8] = new Segment(6, 7, true);
sL[9] = new Segment(7, 8, true);
sL[10] = new Segment(8, 9, true);
sL[11] = new Segment(9, 10, true);
sL[12] = new Segment(10, 11, true);
sL[13] = new Segment(11, 12, true);
cL = new Circle[1];
cL[0] = new Circle(400, 250, 50);
}
float initX = 0;
float initY = 0;
float drag = .95;
float grav = .5;
float a0x = 200;
float a0y = 100;
float a1x = 400;
float a1y = 100;
float x0, y0, x1, y1, cx, cy, cr, p0r, p1r, a, px, py, dx, dy, d0, d1, d;
int p0, p1;
int pD = -1;
float getDistance(int p0, int p1) {
return sqrt(sq(pL[p0].x-pL[p1].x)+sq(pL[p0].y-pL[p1].y));
}
void draw() {
background(120, 140, 100);
if (mousePressed && keyPressed) {
if (keyCode == SHIFT) {
pL[0].x += (mouseX-pL[0].x)/25;
pL[0].y += (mouseY-pL[0].y)/25;
}
if (pD != -1) {
pL[pD].x += (mouseX-pL[pD].x)/25;
pL[pD].y += (mouseY-pL[pD].y)/25;
if (pD == 4) {
a0x = pL[pD].x;
a0y = pL[pD].y;
}
if (pD == 12) {
a1x = pL[pD].x;
a1y = pL[pD].y;
}
}
}
pL[4].x = a0x;
pL[4].y = a0y;
pL[12].x = a1x;
pL[12].y = a1y;
verlet();
segmentCollide();
pointCollide();
render();
}
void mousePressed() {
if (keyPressed && keyCode == CONTROL) {
pD = -1;
for (int i=0; i<pL.length; i++) {
if (sqrt(sq(pL[i].x-mouseX)+sq(pL[i].y-mouseY)) < pL[i].r) {
pD = i;
break;
}
}
}
}
void mouseReleased() {
pD = -1;
}
void verlet() {
for (int i=0; i<pL.length; i++) {
px = pL[i].oX;
py = pL[i].oY;
pL[i].oX = pL[i].x;
pL[i].oY = pL[i].y;
pL[i].x += drag*(pL[i].oX-px);
pL[i].y += drag*(pL[i].oY-py)+grav;
}
for (int i=0; i<sL.length; i++) {
p0 = int(sL[i].p0);
p1 = int(sL[i].p1);
dx = pL[p0].x-pL[p1].x;
dy = pL[p0].y-pL[p1].y;
d = getDistance(p0, p1);
d0 = d1 = (d-sL[i].rL)/d;
d0 *= 0.5;
d1 *= 0.5;
pL[p0].x -= dx*d0;
pL[p0].y -= dy*d0;
pL[p1].x += dx*d1;
pL[p1].y += dy*d1;
}
}
void segmentCollide() {
for (int i=0; i<sL.length; i++) {
if (!sL[i].collide) {
continue;
}
p0 = int(sL[i].p0);
p1 = int(sL[i].p1);
p0r = pL[p0].r;
p1r = pL[p1].r;
//Segment vs Circle
for (int j=0; j<cL.length; j++) {
cx = cL[j].x;
cy = cL[j].y;
cr = cL[j].r;
x0 = pL[p0].x;
y0 = pL[p0].y;
x1 = pL[p1].x;
y1 = pL[p1].y;
a = ((x1-x0)*(cx-x0)+(y1-y0)*(cy-y0))/(sq(x1-x0)+sq(y1-y0));
a = a<0?0:a;
a = a>1?1:a;
px = pL[p0].x+(pL[p1].x-pL[p0].x)*a;
py = pL[p0].y+(pL[p1].y-pL[p0].y)*a;
d = sqrt(sq(px-cx)+sq(py-cy));
if (d < cr) {
d = 2*(cr-d);
dx = (px-cx)/cr;
dy = (py-cy)/cr;
pL[p0].x += d*dx*(1-a);
pL[p0].y += d*dy*(1-a);
pL[p1].x += d*dx*a;
pL[p1].y += d*dy*a;
}
}
//Segment vs Point
for (int j=0; j<pL.length; j++) {
if (j == p0 || j == p1) {
continue;
}
cx = pL[j].x;
cy = pL[j].y;
cr = pL[j].r;
x0 = pL[p0].x;
y0 = pL[p0].y;
x1 = pL[p1].x;
y1 = pL[p1].y;
a = ((x1-x0)*(cx-x0)+(y1-y0)*(cy-y0))/(sq(x1-x0)+sq(y1-y0));
a = a<0?0:a;
a = a>1?1:a;
px = pL[p0].x+(pL[p1].x-pL[p0].x)*a;
py = pL[p0].y+(pL[p1].y-pL[p0].y)*a;
d = sqrt(sq(px-cx)+sq(py-cy));
if (d < cr) {
d = (cr-d);
dx = (px-cx)/cr;
dy = (py-cy)/cr;
pL[j].x -= d*dx;
pL[j].y -= d*dy;
pL[p0].x += d*dx*(1-a);
pL[p0].y += d*dy*(1-a);
pL[p1].x += d*dx*a;
pL[p1].y += d*dy*a;
}
}
}
}
void pointCollide() {
for (int i=0; i<pL.length; i++) {
p0r = pL[i].r;
//Point vs Circle
for (int j=0; j<cL.length; j++) {
cx = cL[j].x;
cy = cL[j].y;
cr = cL[j].r+p0r;
px = pL[i].x;
py = pL[i].y;
d = sqrt(sq(px-cx)+sq(py-cy));
if (d < cr) {
d = (cr-d);
dx = (px-cx)/cr;
dy = (py-cy)/cr;
pL[i].x += d*dx;
pL[i].y += d*dy;
}
}
//Point vs Point
for (int j=0; j<pL.length; j++) {
if (j == i) {
continue;
}
cx = pL[j].x;
cy = pL[j].y;
cr = pL[j].r+p0r;
px = pL[i].x;
py = pL[i].y;
d = sqrt(sq(px-cx)+sq(py-cy));
if (d < cr) {
d = (cr-d);
dx = (px-cx)/cr;
dy = (py-cy)/cr;
pL[j].x -= d*dx;
pL[j].y -= d*dy;
pL[i].x += d*dx;
pL[i].y += d*dy;
}
}
if (pL[i].x < p0r) {
pL[i].x = p0r;
}
else if (pL[i].x > width-p0r) {
pL[i].x = width-p0r;
}
if (pL[i].y < p0r) {
pL[i].y = p0r;
}
else if (pL[i].y > height-p0r) {
pL[i].y = height-p0r;
}
}
}
void render() {
for (int i=0; i<cL.length; i++) {
ellipse(cL[i].x, cL[i].y, cL[i].r*2, cL[i].r*2);
}
for (int i=0; i<sL.length; i++) {
p0 = int(sL[i].p0);
p1 = int(sL[i].p1);
line(pL[p0].x, pL[p0].y, pL[p1].x, pL[p1].y);
}
for (int i=0; i<pL.length; i++) {
if (pL[i].r > 0) {
ellipse(pL[i].x, pL[i].y, 2*pL[i].r, 2*pL[i].r);
}
}
}
class Point {
float x, y, oX, oY, r;
Point (float Tx, float Ty, float Tr) {
x = Tx+initX;
y = Ty+initY;
oX = x;
oY = y;
r = Tr;
}
}
class Segment {
int p0, p1;
float rL;
boolean collide, fillCollide;
Segment (int Tp0, int Tp1, boolean Tcollide) {
p0 = int(Tp0);
p1 = int(Tp1);
rL = getDistance(p0, p1);
collide = Tcollide;
}
}
class Circle {
float x, y, r;
Circle (float Tx, float Ty, float Tr) {
x = Tx;
y = Ty;
r = Tr;
}
}
EDIT: (For those who don't want to download Processing click HERE) To drag the cube press shift and press and hold your mouse. To drag any of the balls (except the large static one) press control and press and hold your mouse. The balls on the ends are anchors and will remain where they were last dragged. Have fun!
EDIT2: Added a circle to throw around too.