| <!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> |
i broke it,went off screen now the screen flashes only red and green with stuff flying everywhere
this is mindblowing
That’s amazing stuff!
Excellent Job
It’s really amazing …
This application is’nt aproved in new opera browser
This is interesting. Opera 10.51. The fastest browser on Earth. Not for my poor ragdoll apparently
Only word I want to say is “Awesome”
amazing , it’s= flash+animation….supper