hello, I’m a ragdoll

Direct Link

Source Code

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>verlet ragdoll - Interactive DHTML art-demos</title>
<meta name="Author" content="Gerard Ferrandez at http://www.dhteumeuleu.com">
<style type="text/css">
html {
overflow: hidden;
}
body {
margin: 0px;
padding: 0px;
background: #222;
position: absolute;
width: 100%;
height: 100%;
}
#screen {
position: absolute;
left: 6%;
top: 10%;
width: 88%;
height: 80%;
background: #000;
overflow: hidden;
cursor: default;
}
#setup {
position: absolute;
top: 0px;
left:0 px;
width: 1000px;
text-align: left;
font-family: verdana;
font-size: .9em;
color: #888;
z-index:1;
}
#title {
position: absolute;
font-family: verdana;
width: 100%;
font-size: 7em;
font-weight: bold;
color: #181818;
text-align: center;
z-index:0;
}
</style>
<script type="text/javascript" src="library/svgvml.js"></script>
<script type="text/javascript">
// =====================================================================
// JavaScript written by Gerard Ferrandez -=[ge1doot]=-
// May 5, 2009
// http://www.dhteumeuleu.com
// use under CC-BY-NC license
// ---------------------------------------------------------------------
// Advanced Character Physics by Thomas Jakobsen
// http://www.teknikus.dk/tj/gdc2001.htm
// Maths for Segments Intersection by Paul Bourke
// http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
// =====================================================================
//
/* ==== Vector constructor ==== */
function Vector (x, y) { this.x = x; this.y = y; }
Vector.prototype.equ = function (vector, x, y) { this.x = vector.x || x || 0; this.y = vector.y || y || 0; }
Vector.prototype.add = function (vector, x, y) { this.x += vector.x || x || 0; this.y += vector.y || y || 0; }
/* ==== ragdoll script ==== */
var ragdoll = function () {
/* ---- private vars ---- */
var svg;
var scr;
var xm = 0;
var ym = 0;
var nw = 0;
var nh = 0;
var zoom = 1.5;
var dragDoll = false;
var xd = 0;
var yd = 0;
var dolls = [];
var nodes = [];
var nBlood = 60;
var blood = [];
var collisions = [];
var initScript = function () {
/* ---- blood reserve ---- */
for (var i = 0; i < nBlood; i++) blood.push(new Blood());
/* ---- preset collisions table ---- */
for (var i = 0; i < dolls.length; i++ ) {
for (var j = 0; j < i; j++) {
if (i != j) {
var o1 = dolls[i];
var o2 = dolls[j];
for (var i1 = 0, n1; n1 = o1.nodes[i1++]; ) {
for (var i2 = 0, n2; n2 = o2.nodes[i2++]; ) {
if (n1.testCol == 1 && n2.testCol == 1) {
collisions.push({
n1p : n1.pos,
n1o : n1.oBody.pos,
n2p : n2.pos,
n2o : n2.oBody.pos,
n2s : new Vector(0,0),
r : ((n1.size * .5) + (n2.size * .5)) / n1.h2Body,
m2 : n1.mass / (n1.mass + n2.mass),
m1 : n2.mass / (n1.mass + n2.mass)
});
}
}
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////
/* ==== DOM events ==== */
function addEvent (o, e, f) {
if (window.addEventListener) o.addEventListener(e, f, false);
else if (window.attachEvent) r = o.attachEvent('on' + e, f);
}
/* ==== screen dimensions ==== */
function resize() {
nw = scr.offsetWidth || 0;
nh = scr.offsetHeight || 0;
}
var initEvents = function () {
/* ---- mouse move event ---- */
addEvent(document, 'mousemove', function (e) {
if (window.event) e = window.event;
xm = e.clientX;
ym = e.clientY;
});
/* ---- mouse up event ---- */
addEvent(document, 'mouseup', function () {
if ( dragDoll && document.releaseCapture) dragDoll.dragNode.o.releaseCapture();
scr.style.cursor = "default";
dragDoll = false;
return false;
});
/* ---- screen resize event ---- */
resize();
addEvent(window, 'resize', resize);
}
////////////////////////////////////////////////////////////////////////////
/* ==== blood constructor ==== */
var Blood = function () {
this.o = svg.createLine(6 * zoom, "#F00", "round");
this.o.move(-99,-99,-99,-99);
this.pos = new Vector(0, 0);
this.vel = new Vector(0, 0);
this.bloody = false;
}
Blood.prototype.start = function (x, y, vx, vy) {
this.pos.equ(false, x, y);
this.vel.equ(false, vx, vy);
this.bloody = true;
}
Blood.prototype.anim = function () {
this.vel.x *= .96;
this.vel.y *= .96;
this.pos.add(this.vel);
if (Math.abs(this.vel.x) + Math.abs(this.vel.y) > 1) {
this.o.move(
this.pos.x, this.pos.y,
this.pos.x + this.vel.x, this.pos.y + this.vel.y
);
} else {
this.o.move(-99,-99,-99,-99);
this.bloody = false;
}
}
////////////////////////////////////////////////////////////////////////////
/* ==== Doll constructor ==== */
var Doll = function (x, y, structure) {
this.nodes = [];
/* ==== Node constructor ==== */
this.Node = function (doll, p) {
this.parent = doll;
this.oBody = p[1];
this.oSpring = p[3];
this.hBody = p[2] * p[2] * zoom * zoom;
this.h2Body = p[2] * zoom;
this.hSpring = p[4] * p[4] * zoom * zoom;
this.size = p[5] * zoom;
this.pos = new Vector(x + Math.random(), y);
this.old = new Vector(x + Math.random(), y);
this.vel = new Vector(0, 0);
this.mass = p[6];
this.ind = p[0];
this.bloody = p[8];
this.testCol = p[9];
this.o = svg.createLine(this.size, p[7], "round");
this.s = false;
this.o.style.cursor = "pointer";
this.o.parent = this;
/* ---- drag events ---- */
this.o.onselectstart = function () { return false; }
this.o.ondrag = function () { return false; }
this.o.onmousedown = function () {
dragDoll = this.parent.parent;
dragDoll.dragNode = this.parent;
xd = dragDoll.dragNode.pos.x - xm;
yd = dragDoll.dragNode.pos.y - ym;
scr.style.cursor = "pointer";
if (this.setCapture) this.setCapture();
return false;
}
/* ==== maintain tables ==== */
doll.nodes[this.ind] = this;
nodes.push(this);
}
/* ==== blood splash ==== */
this.Node.prototype.bounce = function (val) {
if (this.bloody)
if (this.vel.x * this.vel.x + this.vel.y * this.vel.y > zoom)
for (var i = 2; i < 2 + Math.random(); i += .25)
if (ragdoll.eBlood)
blood[Math.floor(Math.random() * nBlood)].start(this.pos.x, this.pos.y, .001 - this.vel.x * zoom * i, .001 - this.vel.y * zoom * i);
this.old.equ(this.pos);
return val;
}
/* ==== solving constraints by relaxation ==== */
this.Node.prototype.satisfyConstraints = function (that, len) {
var dx = that.pos.x - this.pos.x;
var dy = that.pos.y - this.pos.y;
var delta = len / (dx * dx + dy * dy + len) - .5;
var m1 = (this.mass + that.mass) * ragdoll.flexibility;
var m2 = this.mass / m1;
m1 = that.mass / m1;
this.pos.add(false, -m1 * dx * delta, -m1 * dy * delta);
that.pos.add(false, m2 * dx * delta, m2 * dy * delta);
}
/* ==== Graphic rendering ==== */
this.Node.prototype.rendering = function () {
/* ---- body ---- */
if (this.size) this.o.move(
this.pos.x, this.pos.y,
this.oBody.pos.x, this.oBody.pos.y
);
/* ---- springs ---- */
if (this.s)
this.s.move(
this.pos.x, this.pos.y,
this.oSpring.pos.x, this.oSpring.pos.y
);
}
/* ==== Verlet Integration ==== */
this.Node.prototype.verlet = function () {
this.vel.x = (ragdoll.viscosity * (this.pos.x - this.old.x)) || 1;
this.vel.y = (ragdoll.viscosity * (this.pos.y - this.old.y) + ragdoll.gravity) || 1;
this.old.equ(this.pos);
this.pos.add(this.vel);
/* --- test borders --- */
if (this.testCol) {
var size = this.size * .5;
if (this.pos.x < size) this.pos.x = this.bounce(size);
else if (this.pos.x > nw - size) this.pos.x = this.bounce(nw - size);
if (this.pos.y < size) this.pos.y = this.bounce(size);
else if (this.pos.y > nh - size) this.pos.y = this.bounce(nh - size);
}
/* --- Constraint --- */
if (this != this.oSpring) this.satisfyConstraints(this.oSpring, this.hSpring);
if (this != this.oBody) this.satisfyConstraints(this.oBody, this.hBody);
}
/* ==== create springs display ==== */
this.Node.prototype.dispSpring = function () {
if (ragdoll.eSprings) {
/* ---- create spring line ---- */
this.s = svg.createLine(1, "#0F0", "");
this.s.onselectstart = function () { return false; }
this.s.ondrag = function () { return false; }
this.s.o = this;
this.s.onmousedown = function () {
this.o.o.onmousedown();
return false;
}
} else {
/* ---- clear DOM ---- */
this.s.parentNode.removeChild(this.s);
this.s = false;
}
}
/* ==== create Nodes ==== */
for (var i = 0, p; p = structure[i++];) new this.Node(this, p);
for (var i = 0, o; o = this.nodes[i++]; ) {
o.oBody = this.nodes[o.oBody];
o.oSpring = this.nodes[o.oSpring];
}
}
////////////////////////////////////////////////////////////////////////////
/* ==== determine Segment Intersection ==== */
var segmentsIntersection = function (p1, p2, p3, p4) {
var dn = ((p4.y - p3.y) * (p2.x - p1.x)) - ((p4.x - p3.x) * (p2.y - p1.y));
if (dn) {
var na = ((p4.x - p3.x) * (p1.y - p3.y)) - ((p4.y - p3.y) * (p1.x - p3.x));
var nb = ((p2.x - p1.x) * (p1.y - p3.y)) - ((p2.y - p1.y) * (p1.x - p3.x));
var ua = na / dn;
var ub = nb / dn;
if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) return ua;
}
return false;
}
////////////////////////////////////////////////////////////////////////////
/* ==== main loop ==== */
var run = function () {
/* --- drag node --- */
if (dragDoll) {
dragDoll.dragNode.pos.x += (xm + xd - dragDoll.dragNode.pos.x) * .2;
dragDoll.dragNode.pos.y += (ym + yd - dragDoll.dragNode.pos.y) * .2;
}
/* ---- dolls ---- */
for (var i = 0, o; o = nodes[i++]; ) o.verlet();
for (var i = 0, o; o = nodes[i++]; ) o.rendering();
/* ---- blood ---- */
for (var i = 0, o; o = blood[i++]; ) if (o.bloody) o.anim();
/* ---- collisions btw dolls ---- */
if (ragdoll.collision) {
for (var i = 0, o; o = collisions[i++]; ) {
o.n2s.x = o.n2p.x - (o.n2o.x - o.n2p.x) * o.r;
o.n2s.y = o.n2p.y - (o.n2o.y - o.n2p.y) * o.r;
var ua = segmentsIntersection(o.n1p, o.n1o, o.n2s, o.n2o);
if (ua != false) {
/* ---- bounce ---- */
var ix = ua * (o.n2o.x - o.n2p.x) * ragdoll.collision;
var iy = ua * (o.n2o.y - o.n2p.y) * ragdoll.collision;
o.n1o.x -= ix * o.m1;
o.n2o.x += ix * o.m2;
o.n1o.y -= iy * o.m1;
o.n2o.y += iy * o.m2;
}
}
}
setTimeout(run, 16);
}
return {
////////////////////////////////////////////////////////////////////////////
/* ==== PUBLIC parameters setup ==== */
viscosity : .999,
flexibility : 3,
gravity : .1,
collision : .05,
eBlood : true,
eSprings : false,
dispSprings : function () {
this.eSprings = !this.eSprings;
for (var i = 0, o; o = nodes[i++]; ) o.dispSpring();
},
/* ==== initialize script ==== */
init : function () {
addEvent(window, 'load', function () {
/* ---- init SVG/VML canvas ---- */
scr = document.getElementById("screen");
initEvents();
svg = new vectorGraphics(scr, true);
zoom = nh / 250;
/* ---- syntax ---- */
// dolls.push(new Doll(ragdoll_init_pos_x, ragdoll_init_pos_y, [
// [node_ind,node_parent_ind,node_length,node_spring_parent_ind,spring_length,node_width,node_mass,node_color,blood_on_off,collisions_on_off],
// note: declare nodes by z-index order (painting algorithm) - thanks SVG for not implementing zIndex property !!!
/* ---- create ragdoll ---- */
dolls.push(new Doll(nw * .5, 40, [
[1,0,3,7,80,10,1, "#888", 0, 0],
[5,3,20,1,60,8,1, "#888", 0, 1],
[6,4,20,1,60,8,1, "#888", 0, 1],
[3,1,20,4,40,10,1, "#FFF", 1, 1],
[4,1,20,2,40,10,1, "#FFF", 1, 1],
[9,7,30,2,80,8,2, "#888", 0, 1],
[10,8,30,2,80,8,2, "#888", 0, 1],
[7,2,30,8,30,13,1, "#F80", 1, 1],
[8,2,30,1,80,13,1, "#F80", 1, 1],
[2,1,30,3,40,20,2, "#FFF", 1, 1],
[0,0,35,2,60,35,1, "#888", 1, 1],
[11,1,1,2,25,0,1, "#000", 0, 0],
[12,11,25,11,25,5,1, "#444", 0, 0]
]));
/* ---- create another ragdoll ---- */
dolls.push(new Doll(nw * .75, nh * .5,[
[1,2,100*.5,3,141*.5,20,2, "#fff", 0, 1],
[2,3,100*.5,0,141*.5,20,2, "#fff", 0, 1],
[3,0,100*.5,1,141*.5,20,2, "#fff", 0, 1],
[0,1,100*.5,2,141*.5,20,2, "#fff", 0, 1],
[4,1,10,1,10,5,.1, "#f80", 0, 2],
[5,4,10,4,10,5,.1, "#f80", 0, 2],
[6,5,10,5,10,5,.1, "#f80", 0, 2],
[7,6,10,6,10,5,.1, "#f80", 0, 2],
[8,7,10,7,10,5,.1, "#f80", 0, 2],
[9,8,10,8,10,5,.1, "#f80", 0, 2],
[10,9,10,9,10,5,.1, "#f80", 0, 2],
[11,10,10,10,10,5,.1, "#f80", 0, 2],
[12,11,10,11,10,5,.1, "#f80", 0, 2],
[13,12,10,12,10,5,.1, "#f80", 0, 2],
[14,13,10,13,10,5,.1, "#f80", 0, 2]
]));
/* ---- start ---- */
initScript();
run();
});
}
}
}();
/* ==== start script ==== */
ragdoll.init();
</script>
</head>
<body>
<div id="screen">
<div id="title">js ragdoll</div>
<form id="setup">
<table>
<tr><td>blood</td><td><input type=checkbox checked onclick="ragdoll.eBlood=!ragdoll.eBlood;" style="cursor:pointer;"></td></tr>
<tr><td>springs</td><td><input type=checkbox onclick="ragdoll.dispSprings();" style="cursor:pointer;"></td><td></td></tr>
<tr><td>Friction</td><td><input onclick="ragdoll.viscosity=.999;" type=radio name=visco><input onclick="ragdoll.viscosity=.98;" type=radio name=visco><input checked onclick="ragdoll.viscosity=.96;" type=radio name=visco><input onclick="ragdoll.viscosity=.85;" type=radio name=visco></td></tr>
<tr><td>Flexibility</td><td><input onclick="ragdoll.flexibility=1;" type=radio name=flexi><input onclick="ragdoll.flexibility=2;" type=radio name=flexi><input checked onclick="ragdoll.flexibility=3;" type=radio name=flexi><input onclick="ragdoll.flexibility=10;" type=radio name=flexi></td><td></td></tr>
<tr><td>Gravity</td><td><input onclick="ragdoll.gravity=0;" type=radio name=gravi><input checked onclick="ragdoll.gravity=.1;" type=radio name=gravi><input onclick="ragdoll.gravity=.2;" type=radio name=gravi><input onclick="ragdoll.gravity=.5;" type=radio name=gravi></td></tr>
<tr><td>Collisions</td><td><input onclick="ragdoll.collision=0;" type=radio name=colli><input checked onclick="ragdoll.collision=.05;" type=radio name=colli><input onclick="ragdoll.collision=.1;" type=radio name=colli><input onclick="ragdoll.collision=.5;" type=radio name=colli></td></tr>
</table>
</form>
</div>
</body>
</html>

Tagged with:
 

9 Responses to “hello, I’m a ragdoll”

  1. lol says:

    i broke it,went off screen now the screen flashes only red and green with stuff flying everywhere

    Comment marked as read read by ge1doot
  2. zia says:

    this is mindblowing

    Comment marked as read read by ge1doot
  3. free ps3 says:

    That’s amazing stuff!

    Comment marked as read read by ge1doot
  4. Raju Makwana says:
    otherI am feeling neutral

    Excellent Job :)

    Comment marked as read read by ge1doot
  5. Gauri says:
    otherI am feeling neutral

    It’s really amazing …

    Comment marked as read read by ge1doot
  6. pelvis says:
    otherI am feeling neutral

    This application is’nt aproved in new opera browser

    Comment marked as read read by ge1doot
    • ge1doot says:
      otherI am feeling neutral

      This is interesting. Opera 10.51. The fastest browser on Earth. Not for my poor ragdoll apparently ;-)

  7. ishan says:
    otherI am feeling positive

    Only word I want to say is “Awesome”

    Comment marked as read read by ge1doot
  8. deepak says:
    ideaI am feeling positive

    amazing , it’s= flash+animation….supper

Leave a Reply

Comment Category

Mood of the Moment

Feed updates subscription

Enter your email address:

Delivered by FeedBurner

Donate

Support www.dhteumeuleu.com...

License

Creative Commons License

Except where otherwise noted, all Javascript code on this site is licensed under a Creative Commons License.