View
Theme
Font Style
7pt
8pt
9pt
10pt
11pt
Line Style
100%
110%
120%
130%
140%
Bold Keyword
Default
Inspector
Kkaefer
Eclipse
SQ Light
Lesser
Dark
Cobalt
Monokai
Rubyblue
Night
SQ Dark
Ambiance
Blackboard
Line Num.
Wrap Lines
Preview
Redraw
JS Tab
HTML Tab
CSS Tab
Live Tab
Prev. Tab
Next Tab
Browser
History…
Help
Edit
Settings
Auto Complete
Match Brackets
Match Highlight
Strip Whitespace
Auto Close Brackets
Auto Close Quotes
Show Print Margin
Undo
Redo
Delete
Select Line
Select All
Find & Replace
Find
Find in Repo.
Find Next
Find Previous
Replace Single
Replace All
Wrap Search
Revert
As Template
Diff Revision
Format
Compress
Text
Zen Coding
Indent
Tab Width
1
2
3
4
5
6
7
8
Indent Unit
1
2
3
4
5
6
7
8
Smart Indent
Use Tabs
Visible Tabs
Shift Left
Shift Right
Put Indent
Number
Increment by 1
Decrement by 1
Increment by 0.1
Decrement by 0.1
Increment by 10
Decrement by 10
Simple Math
Comment
Line
Move Up
Move Down
Copy Up
Copy Down
Go to Line…
Remove Line
Next Point
Prev. Point
Help
Share
Login
You can jump to the latest bin by adding
/latest
to your URL
×
z
Find
→
←
⟲
Replace
⊗
All
Replace
/* Copyright (c) 2006, 2007 Alec Cove Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* ported by flashrod 2008 ActionScript ported by flashrod 2011 JavaScript */ var Util = { concat: function(d, s) { for (var key in s) { if (s.hasOwnProperty(key)) { var getter = s.__lookupGetter__(key); if (getter) { d.__defineGetter__(key, getter); } var setter = s.__lookupSetter__(key); if (setter) { d.__defineSetter__(key, setter); } if (!(getter || setter)) { d[key] = s[key]; } } } return d; } }; var Vector = function(x, y) { this.x = x || 0; this.y = y || 0; }; Vector.prototype = { setTo: function(x, y) { this.x = x; this.y = y; }, copy: function(v) { this.x = v.x; this.y = v.y; }, dot: function(v) { return this.x * v.x + this.y * v.y; }, cross: function(v) { return this.x * v.y - this.y * v.x; }, plus: function(v) { return new Vector(this.x + v.x, this.y + v.y); }, plusEquals: function(v) { this.x += v.x; this.y += v.y; return this; }, minus: function(v) { return new Vector(this.x - v.x, this.y - v.y); }, minusEquals: function(v) { this.x -= v.x; this.y -= v.y; return this; }, mult: function(s) { return new Vector(this.x * s, this.y * s); }, multEquals: function(s) { this.x *= s; this.y *= s; return this; }, times: function(v) { return new Vector(this.x * v.x, this.y * v.y); }, divEquals: function(s) { if (s == 0) { s = 0.0001; } this.x /= s; this.y /= s; return this; }, magnitude: function() { return Math.sqrt(this.x * this.x + this.y * this.y); }, distance: function(v) { var delta = this.minus(v); return delta.magnitude(); }, normalize: function() { var m = this.magnitude(); if (m == 0) { m = 0.0001; } return this.mult(1 / m); }, toString: function() { return "(" + this.x + ", " + this.y + ")"; } }; var Particle = function(x, y, fixed, mass, elasticity, friction) { this.samp = new Vector(0, 0); this.interval = { min:0, max:0 }; this.forces = new Vector(0, 0); this.collision = {vn: new Vector(0, 0), vt: new Vector(0, 0)}; this._collidable = true; this._multisample = 0; this.curr = new Vector(x, y); this.prev = new Vector(x, y); this._fixed = fixed; this._mass = mass; this._invMass = fixed ? 0 : 1 / mass; this._elasticity = elasticity; this._friction = friction; }; Particle.prototype = { get mass() { return this._mass; }, set mass(m) { if (m <= 0) { throw new ArgumentError("mass may not be set <= 0"); } this._mass = m; this._invMass = this._fixed ? 0 : 1 / this._mass; }, get invMass() { return this._invMass; }, get elasticity() { return this._elasticity; }, set elasticity(k) { this._elasticity = k; }, get multisample() { return this._multisample; }, set multisample(m) { this._multisample = m; }, get friction() { return this._friction; }, set friction(f) { if (f < 0 || f > 1) throw new ArgumentError("Legal friction must be >= 0 and <=1"); this._friction = f; }, get fixed() { return this._fixed; }, set fixed(f) { this._fixed = f; }, get position() { return new Vector(this.curr.x, this.curr.y); }, set position(p) { this.curr.copy(p); this.prev.copy(p); }, get x() { return this.curr.x; }, set x(x) { this.prev.x = this.curr.x = x; }, get y() { return this.curr.y; }, set y(y) { this.prev.y = this.curr.y = y; }, get velocity() { return this.curr.minus(this.prev); }, set velocity(v) { this.prev = this.curr.minus(v); }, get collidable() { return this._collidable; }, set collidable(b) { this._collidable = b; }, addForce: function(f) { this.forces.plusEquals(f.mult(this.invMass)); }, addMasslessForce: function(f) { this.forces.plusEquals(f); }, update: function(dt2, engine) { if (this.fixed) { return; } this.addForce(engine.force); this.addMasslessForce(engine.gravity); var temp = new Vector(this.curr.x, this.curr.y); var nv = this.velocity.plus(this.forces.multEquals(dt2)); this.curr.plusEquals(nv.multEquals(engine.damping)); this.prev.copy(temp); this.forces.setTo(0, 0); }, getComponents: function(collisionNormal) { var vel = this.velocity; var vdotn = collisionNormal.dot(vel); this.collision.vn = collisionNormal.mult(vdotn); this.collision.vt = vel.minus(this.collision.vn); return this.collision; }, resolveCollision: function(mtd, vel, n, d, o, p) { this.curr.plusEquals(mtd); this.velocity = vel; }, updatePosition: function() {}, paint: function() {}, toString: function() { return "Particle()"; } }; var Circle = function(x, y, radius, fixed, mass, elasticity, friction) { fixed = fixed || false; mass = mass || 1; elasticity = elasticity || 0.3; friction = friction || 0; Particle.call(this, x, y, fixed, mass, elasticity, friction); this.radius = radius; }; Circle.prototype = Util.concat(new Particle(), { getProjection: function(axis) { var c = this.samp.dot(axis); this.interval.min = c - this.radius; this.interval.max = c + this.radius; return this.interval; }, getIntervalX: function() { this.interval.min = this.curr.x - this.radius; this.interval.max = this.curr.x + this.radius; return this.interval; }, getIntervalY: function() { this.interval.min = this.curr.y - this.radius; this.interval.max = this.curr.y + this.radius; return this.interval; }, toString: function() { return "Circle("+this.x+", "+this.y+", "+this.radius+")"; } }); var Rect = function(x, y, width, height, rotation, fixed, mass, elasticity, friction) { rotation = rotation || 0; fixed = fixed || false; mass = mass || 1; elasticity = elasticity || 0.3; friction = friction || 0; Particle.call(this, x, y, fixed, mass, elasticity, friction); this._axes = [new Vector(0, 0), new Vector(0, 0)]; this._extents = [width / 2, height / 2]; this.radian = rotation; }; Rect.prototype = Util.concat(new Particle(), { get radian() { return this._radian; }, set radian(t) { this._radian = t; this.setAxes(t); }, get angle() { return this.radian * (180 / Math.PI); }, set angle(a) { this.radian = a * (Math.PI / 180); }, get width() { return this._extents[0] * 2; }, set width(w) { this._extents[0] = w / 2; }, get height() { return this._extents[1] * 2; }, set height(h) { this._extents[1] = h / 2; }, get axes() { return this._axes; }, get extents() { return this._extents; }, getProjection: function(axis) { var radius = this._extents[0] * Math.abs(axis.dot(this._axes[0]))+ this._extents[1] * Math.abs(axis.dot(this._axes[1])); var c = this.samp.dot(axis); this.interval.min = c - radius; this.interval.max = c + radius; return this.interval; }, setAxes: function(t) { var s = Math.sin(t); var c = Math.cos(t); this._axes[0].x = c; this._axes[0].y = s; this._axes[1].x = -s; this._axes[1].y = c; }, toString: function() { return "Rect("+this.x+","+this.y+","+this.width+","+this.height+")"; } }); var Constraint = function(stiffness) { this.stiffness = stiffness; }; Constraint.prototype = { get collidableParticle() { return null; }, set collidableParticle(p) {}, isConnectedTo: function(p) { return false; }, resolve: function() {}, paint: function() {}, toString: function() { return "Constraint("+this.stiffness+")"; } }; var Spring = function(p1, p2, stiffness) { stiffness = stiffness || 0.5; Constraint.call(this, stiffness); if (p1 && p2) { this.p1 = p1; this.p2 = p2; this.checkParticlesLocation(); this._restLength = this.currLength; } this._scp = null; }; Spring.prototype = Util.concat(new Constraint(), { get radian() { var d = this.delta; return Math.atan2(d.y, d.x); }, get angle() { return this.radian * (180 / Math.PI); }, get center() { return (this.p1.curr.plus(this.p2.curr)).divEquals(2); }, set rectScale(s) { if (this._scp != null) { this._scp.rectScale = s; } }, get rectScale() { if (this._scp != null) { return this._scp.rectScale; } return NaN; }, get currLength() { return this.p1.curr.distance(this.p2.curr); }, get rectHeight() { if (this._scp != null) { return this._scp.rectHeight; } return NaN; }, set rectHeight(h) { if (this._scp != null) { this._scp.rectHeight = h; } }, get restLength() { return this._restLength; }, set restLength(r) { if (r <= 0) { throw new ArgumentError("restLength must be greater than 0"); } this._restLength = r; }, get fixedEndLimit() { if (this._scp != null) { return this._scp.fixedEndLimit; } return NaN; }, set fixedEndLimit(f) { if (this._scp != null) { this._scp.fixedEndLimit = f; } }, get collidableParticle() { return this._scp; }, set collidableParticle(p) { this._scp = p; }, isConnectedTo: function(p) { return (p == this.p1 || p == this.p2); }, get fixed() { return (this.p1.fixed && this.p2.fixed); }, get delta() { return this.p1.curr.minus(this.p2.curr); }, resolve: function() { if (this.p1.fixed && this.p2.fixed) { return; } var deltaLength = this.currLength; var diff = (deltaLength - this.restLength) / (deltaLength * (this.p1.invMass + this.p2.invMass)); var dmds = this.delta.mult(diff * this.stiffness); this.p1.curr.minusEquals(dmds.mult(this.p1.invMass)); this.p2.curr.plusEquals (dmds.mult(this.p2.invMass)); }, checkParticlesLocation: function() { if (this.p1.curr.x == this.p2.curr.x && this.p1.curr.y == this.p2.curr.y) { this.p2.curr.x += 0.0001; } }, paint: function() { if (this._scp != null) { this._scp.paint(); } }, toString: function() { return "Spring()"; } }); var Collection = function() { this.particles = []; this.constraints = []; }; Collection.prototype = { integrate: function(dt2, engine) { var a = this.particles; var l = a.length; for (var i = 0; i < l; ++i) { var p = a[i]; p.update(dt2, engine); } }, satisfyConstraints: function() { var a = this.constraints; var l = a.length; for (var i = 0; i < l; ++i) { var c = a[i]; c.resolve(); } }, checkInternalCollisions: function() { var a1 = this.particles; var l1 = a1.length; var a2 = this.constraints; var l2 = a2.length; for (var j = 0; j < l1; j++) { var pj = a1[j]; if (! pj.collidable) { continue; } for (var i = j + 1; i < l1; i++) { var pi = a1[i]; if (pi.collidable) { collisionTest(pj, pi); } } for (var k = 0; k < l2; ++k) { var c = a2[k]; var pk = c.collidableParticle; if (pk != null && ! c.isConnectedTo(pj)) { pk.updatePosition(); collisionTest(pj, pk); } } } }, checkCollisionsVsCollection: function(that) { var a1 = this.particles; var l1 = a1.length; var a2 = that.particles; var l2 = a2.length; var a3 = this.constraints; var l3 = a3.length; var a4 = that.constraints; var l4 = a4.length; for (var i1 = 0; i1 < l1; ++i1) { var pga = a1[i1]; if (! pga.collidable) { continue; } for (var i2 = 0; i2 < l2; ++i2) { var pgb = a2[i2]; if (pgb.collidable) { collisionTest(pga, pgb); } } for (var i4 = 0; i4 < l4; ++i4) { var cgb = a4[i4]; var pk = cgb.collidableParticle; if (pk != null && ! cgb.isConnectedTo(pga)) { pk.updatePosition(); collisionTest(pga, pk); } } } for (var i3 = 0; i3 < l3; ++i3) { var cga = a3[i3]; var pl = cga.collidableParticle; if (pl == null) { continue; } for (i2 = 0; i2 < l2; ++i2) { pgb = a2[i2]; if (pgb.collidable && !cga.isConnectedTo(pgb)) { pl.updatePosition(); collisionTest(pgb, pl); } } } }, paint: function() { var a1 = this.particles; var l1 = a1.length; for (var i1 = 0; i1 < l1; ++i1) { var p = a1[i1]; p.paint(); } var a2 = this.constraints; var l2 = a2.length; for (var i2 = 0; i2 < l2; ++i2) { var c = a2[i2]; c.paint(); } }, toString: function() { return "Collection()"; } }; var Composite = function() { Collection.call(this); }; Composite.prototype = Util.concat(new Collection(), { rotate: function(angleRadians, center) { var a = this.particles; var l = a.length; for (var i = 0; i < l; ++i) { var p = a[i]; var other = p.position; var radius = other.distance(center); var angle = Math.atan2(other.y - center.y, other.x - center.x) + angleRadians; p.x = (Math.cos(angle) * radius) + center.x; p.y = (Math.sin(angle) * radius) + center.y; } }, toString: function() { return "Composite()"; } }); var Group = function(collideInternal) { Collection.call(this); this.collideInternal = collideInternal || false; this.composites = []; this.collisionList = []; }; Group.prototype = Util.concat(new Collection(), { integrate: function(dt2, engine) { Collection.prototype.integrate.call(this, dt2, engine); var a = this.composites; var l = a.length; for (var i = 0; i < l; ++i) { var cmp = a[i]; cmp.integrate(dt2, engine); } }, satisfyConstraints: function() { Collection.prototype.satisfyConstraints.call(this); var a = this.composites; var l = a.length; for (var i = 0; i < l; ++i) { var cmp = a[i]; cmp.satisfyConstraints(); } }, checkCollisions: function() { if (this.collideInternal) { this.checkCollisionGroupInternal(); } var a = this.collisionList; var l = a.length; for (var i = 0; i < l; ++i) { var g = a[i]; this.checkCollisionVsGroup(g); } }, checkCollisionGroupInternal: function() { this.checkInternalCollisions(); var clen = this.composites.length; for (var j = 0; j < clen; j++) { var cj = this.composites[j]; cj.checkCollisionsVsCollection(this); for (var i = j + 1; i < clen; i++) { var ci = this.composites[i]; cj.checkCollisionsVsCollection(ci); } } }, checkCollisionVsGroup: function(that) { this.checkCollisionsVsCollection(that); var a1 = this.composites; var l1 = a1.length; var a2 = that.composites; var l2 = a2.length; for (var i = 0; i < l1; ++i) { var c = a1[i]; c.checkCollisionsVsCollection(that); for (var j = 0; j < l2; ++j) { var gc = a2[j]; c.checkCollisionsVsCollection(gc); } } for (j = 0; j < l2; ++j) { gc = a2[j]; this.checkCollisionsVsCollection(gc); } }, paint: function() { Collection.prototype.paint.call(this); var a = this.composites; var l = a.length; for (var i = 0; i < l; ++i) { var c = a[i]; c.paint(); } }, toString: function() { return "Group()"; } }); function collisionTest(objA, objB) { if (objA.fixed && objB.fixed) { return; } if (objA.multisample == 0 && objB.multisample == 0) { normVsNorm(objA, objB); } else if (objA.multisample > 0 && objB.multisample == 0) { sampVsNorm(objA, objB); } else if (objB.multisample > 0 && objA.multisample == 0) { sampVsNorm(objB, objA); } else if (objA.multisample == objB.multisample) { sampVsSamp(objA, objB); } else { normVsNorm(objA, objB); } } function normVsNorm(objA, objB) { objA.samp.copy(objA.curr); objB.samp.copy(objB.curr); testTypes(objA, objB); } function sampVsNorm(objA, objB) { var s = 1 / (objA.multisample + 1); var t = s; objB.samp.copy(objB.curr); for (var i = 0; i <= objA.multisample; i++) { objA.samp.setTo(objA.prev.x + t * (objA.curr.x - objA.prev.x), objA.prev.y + t * (objA.curr.y - objA.prev.y)); if (testTypes(objA, objB)) { return; } t += s; } } function sampVsSamp(objA, objB) { var s = 1 / (objA.multisample + 1); var t = s; for (var i = 0; i <= objA.multisample; i++) { objA.samp.setTo(objA.prev.x + t * (objA.curr.x - objA.prev.x), objA.prev.y + t * (objA.curr.y - objA.prev.y)); objB.samp.setTo(objB.prev.x + t * (objB.curr.x - objB.prev.x), objB.prev.y + t * (objB.curr.y - objB.prev.y)); if (testTypes(objA, objB)) { return; } t += s; } } function testTypes(objA, objB) { if ((objA instanceof Circle) && (objB instanceof Circle)) { testCirclevsCircle(objA, objB); } else if ((objA instanceof Circle) && (objB instanceof Rect)) { testCirclevsOBB(objA, objB); } else if ((objA instanceof Rect) && (objB instanceof Circle)) { testOBBvsCircle(objA, objB); } else { testOBBvsOBB(objA, objB); } } function testOBBvsOBB(ra, rb) { var collisionNormal; var collisionDepth = Number.POSITIVE_INFINITY; for (var i = 0; i < 2; i++) { var axisA = ra.axes[i]; var depthA = testIntervals(ra.getProjection(axisA), rb.getProjection(axisA)); if (depthA == 0) { return false; } var axisB = rb.axes[i]; var depthB = testIntervals(ra.getProjection(axisB), rb.getProjection(axisB)); if (depthB == 0) { return false; } var absA = Math.abs(depthA); var absB = Math.abs(depthB); if (absA < Math.abs(collisionDepth) || absB < Math.abs(collisionDepth)) { var altb = absA < absB; collisionNormal = altb ? axisA : axisB; collisionDepth = altb ? depthA : depthB; } } resolveParticleParticle(ra, rb, collisionNormal, collisionDepth); return true; } function testCirclevsOBB(ca, ra) { return testOBBvsCircle(ra, ca); } function testOBBvsCircle(ra, ca) { var collisionNormal; var collisionDepth = Number.POSITIVE_INFINITY; var depths = new Array(2); // first go through the axes of the rectangle for (var i = 0; i < 2; i++) { var boxAxis = ra.axes[i]; var depth = testIntervals(ra.getProjection(boxAxis), ca.getProjection(boxAxis)); if (depth == 0) { return false; } if (Math.abs(depth) < Math.abs(collisionDepth)) { collisionNormal = boxAxis; collisionDepth = depth; } depths[i] = depth; } // determine if the circle's center is in a vertex region var r = ca.radius; if (Math.abs(depths[0]) < r && Math.abs(depths[1]) < r) { var vertex = closestVertexOnOBB(ca.samp, ra); // get the distance from the closest vertex on rect to circle center collisionNormal = vertex.minus(ca.samp); var mag = collisionNormal.magnitude(); collisionDepth = r - mag; if (collisionDepth > 0) { // there is a collision in one of the vertex regions collisionNormal.divEquals(mag); } else { // ra is in vertex region, but is not colliding return false; } } resolveParticleParticle(ra, ca, collisionNormal, collisionDepth); return true; } function testCirclevsCircle(ca, cb) { var depthX = testIntervals(ca.getIntervalX(), cb.getIntervalX()); if (depthX == 0) { return false; } var depthY = testIntervals(ca.getIntervalY(), cb.getIntervalY()); if (depthY == 0) { return false; } var collisionNormal = ca.samp.minus(cb.samp); var mag = collisionNormal.magnitude(); var collisionDepth = (ca.radius + cb.radius) - mag; if (collisionDepth > 0) { collisionNormal.divEquals(mag); resolveParticleParticle(ca, cb, collisionNormal, collisionDepth); return true; } return false; } function testIntervals(intervalA, intervalB) { if (intervalA.max < intervalB.min) { return 0; } if (intervalB.max < intervalA.min) { return 0; } var lenA = intervalB.max - intervalA.min; var lenB = intervalB.min - intervalA.max; return (Math.abs(lenA) < Math.abs(lenB)) ? lenA : lenB; } function closestVertexOnOBB(p, r) { var d = p.minus(r.samp); var q = new Vector(r.samp.x, r.samp.y); for (var i = 0; i < 2; i++) { var dist = d.dot(r.axes[i]); if (dist >= 0) { dist = r.extents[i]; } else if (dist < 0) { dist = -r.extents[i]; } q.plusEquals(r.axes[i].mult(dist)); } return q; } function clamp01(n) { if (n < 0) return 0; if (n > 1) return 1; return n; } // thanks to Jim Bonacci for changes using the inverse mass instead of mass function resolveParticleParticle(pa, pb, normal, depth) { // a collision has occured. set the current positions to sample locations pa.curr.copy(pa.samp); pb.curr.copy(pb.samp); var mtd = normal.mult(depth); var te = pa.elasticity + pb.elasticity; var sumInvMass = pa.invMass + pb.invMass; // the total friction in a collision is combined but clamped to [0,1] var tf = clamp01(1 - (pa.friction + pb.friction)); // get the collision, vn and vt var ca = pa.getComponents(normal); var cb = pb.getComponents(normal); // calculate the coefficient of restitution based on the mass, as the normal component var vnA = (cb.vn.mult((te + 1) * pa.invMass).plus(ca.vn.mult(pb.invMass - te * pa.invMass))).divEquals(sumInvMass); var vnB = (ca.vn.mult((te + 1) * pb.invMass).plus(cb.vn.mult(pa.invMass - te * pb.invMass))).divEquals(sumInvMass); // apply friction to the tangental component ca.vt.multEquals(tf); cb.vt.multEquals(tf); // scale the mtd by the ratio of the masses. heavier particles move less var mtdA = mtd.mult( pa.invMass / sumInvMass); var mtdB = mtd.mult(-pb.invMass / sumInvMass); // add the tangental component to the normal component for the new velocity vnA.plusEquals(ca.vt); vnB.plusEquals(cb.vt); if (! pa.fixed) { pa.resolveCollision(mtdA, vnA, normal, depth, -1, pb); } if (! pb.fixed) { pb.resolveCollision(mtdB, vnB, normal, depth, 1, pa); } } var Rim = function(r, mt, parent) { this.curr = new Vector(r, 0); this.prev = new Vector(0, 0); this.sp = 0; this.av = 0; this.maxTorque = mt; this.wr = r; this.parent = parent; }; Rim.prototype = { get speed() { return this.sp; }, set speed(s) { this.sp = s; }, get angularVelocity() { return this.av; }, set angularVelocity(s) { this.av = s; }, update: function(dt, engine) { //clamp torques to valid range this.sp = Math.max(-this.maxTorque, Math.min(this.maxTorque, this.sp + this.av)); //apply torque //this is the tangent vector at the rim particle var dx = -this.curr.y; var dy = this.curr.x; //normalize so we can scale by the rotational speed var len = Math.sqrt(dx * dx + dy * dy); dx /= len; dy /= len; this.curr.x += this.sp * dx; this.curr.y += this.sp * dy; var ox = this.prev.x; var oy = this.prev.y; var px = this.prev.x = this.curr.x; var py = this.prev.y = this.curr.y; this.curr.x += engine.damping * (px - ox); this.curr.y += engine.damping * (py - oy); // hold the rim particle in place var clen = Math.sqrt(this.curr.x * this.curr.x + this.curr.y * this.curr.y); var diff = (clen - this.wr) / clen; this.curr.x -= this.curr.x * diff; this.curr.y -= this.curr.y * diff; }, toString: function() { return "Rim()"; } }; var Wheel = function(x, y, radius, fixed, mass, elasticity, friction, traction) { fixed = fixed || false; mass = mass || 1; elasticity = elasticity || 0.3; friction = friction || 0; traction = traction || 1; Circle.call(this, x, y, radius, fixed, mass, elasticity, friction); this.tan = new Vector(); this.normSlip = new Vector(); this.orientation = new Vector(); this.rim = new Rim(radius, 2, this); this.traction = traction; }; Wheel.prototype = Util.concat(new Circle(), { get speed() { return this.rim.speed; }, set speed(s) { this.rim.speed = s; }, get angularVelocity() { return this.rim.angularVelocity; }, set angularVelocity(a) { this.rim.angularVelocity = a; }, get traction() { return 1 - this._traction; }, set traction(t) { this._traction = 1 - t; }, get radian() { this.orientation.setTo(this.rim.curr.x, this.rim.curr.y); return Math.atan2(this.orientation.y, this.orientation.x) + Math.PI; }, get angle() { return this.radian * (180 / Math.PI); }, update: function(dt, engine) { Circle.prototype.update.call(this, dt, engine); this.rim.update(dt, engine); }, resolveCollision: function(mtd, vel, n, d, o, p) { // review the o (order) need here - its a hack fix Circle.prototype.resolveCollision.call(this, mtd, vel, n, d, o, p); this.resolve(n.mult(Wheel.sign(d * o))); }, resolve: function(n) { // this is the tangent vector at the this.rim particle this.tan.setTo(-this.rim.curr.y, this.rim.curr.x); // normalize so we can scale by the rotational speed this.tan = this.tan.normalize(); // velocity of the wheel's surface var wheelSurfaceVelocity = this.tan.mult(this.rim.speed); // the velocity of the wheel's surface relative to the ground var combinedVelocity = this.velocity.plusEquals(wheelSurfaceVelocity); // the wheel's comb velocity projected onto the contact normal var cp = combinedVelocity.cross(n); // set the wheel's spinspeed to track the ground this.tan.multEquals(cp); this.rim.prev.copy(this.rim.curr.minus(this.tan)); // some of the wheel's torque is removed and converted into linear displacement var slipSpeed = (1 - this._traction) * this.rim.speed; this.normSlip.setTo(slipSpeed * n.y, slipSpeed * n.x); this.curr.plusEquals(this.normSlip); this.rim.speed = this.rim.speed * this._traction; }, toString: function() { return "Wheel()"; } }); Wheel.sign = function(val) { return (val < 0) ? -1 : 1; } var SpringRect = function(spring, rectHeight, rectScale, scaleToLength) { Rect.call(this, 0, 0, 0, 0, 0, false); if (spring) { this._spring = spring; this.p1 = spring.p1; this.p2 = spring.p2; } this.rectScale = rectScale || 1; this.rectHeight = rectHeight || 1; this.scaleToLength = scaleToLength || false; this.avgVelocity = new Vector(); this.lambda = new Vector(); this.rca = new Vector(); this.rcb = new Vector(); this.fixedEndLimit = 0; }; SpringRect.prototype = Util.concat(new Rect(), { get mass() { return (this.p1.mass + this.p2.mass) / 2; }, get elasticity() { return (this.p1.elasticity + this.p2.elasticity) / 2; }, get friction() { return (this.p1.friction + this.p2.friction) / 2; }, get velocity() { var p1v = this.p1.velocity; var p2v = this.p2.velocity; this.avgVelocity.setTo(((p1v.x + p2v.x) / 2), ((p1v.y + p2v.y) / 2)); return this.avgVelocity; }, get invMass() { if (this.p1.fixed && this.p2.fixed) { return 0; } return 1 / ((this.p1.mass + this.p2.mass) / 2); }, updatePosition: function() { var c = this._spring.center; this.curr.setTo(c.x, c.y); this.width = this.scaleToLength ? this._spring.currLength * this.rectScale : this._spring.restLength * this.rectScale; this.height = this.rectHeight; this.radian = this._spring.radian; }, resolveCollision: function(mtd, vel, n, d, o, p) { var t = this.getContactPointParam(p); var c1 = 1 - t; var c2 = t; // if one is fixed then move the other particle the entire way out of collision. // also, dispose of collisions at the sides of the scp. The higher the this.fixedEndLimit // value, the more of the scp not be effected by collision. if (this.p1.fixed) { if (c2 <= this.fixedEndLimit) { return; } this.lambda.setTo(mtd.x / c2, mtd.y / c2); this.p2.curr.plusEquals(this.lambda); this.p2.velocity = vel; } else if (this.p2.fixed) { if (c1 <= this.fixedEndLimit) { return; } this.lambda.setTo(mtd.x / c1, mtd.y / c1); this.p1.curr.plusEquals(this.lambda); this.p1.velocity = vel; // else both non fixed - move proportionally out of collision } else { var denom = (c1 * c1 + c2 * c2); if (denom == 0) { return; } this.lambda.setTo(mtd.x / denom, mtd.y / denom); this.p1.curr.plusEquals(this.lambda.mult(c1)); this.p2.curr.plusEquals(this.lambda.mult(c2)); // if collision is in the middle of SCP set the velocity of both end particles if (t == 0.5) { this.p1.velocity = vel; this.p2.velocity = vel; // otherwise change the velocity of the particle closest to contact } else { var corrParticle = (t < 0.5) ? this.p1 : this.p2; corrParticle.velocity = vel; } } }, closestParamPoint: function(c) { var ab = this.p2.curr.minus(this.p1.curr); var t = (ab.dot(c.minus(this.p1.curr))) / (ab.dot(ab)); return clamp01(t); }, getContactPointParam: function(p) { var t; if (p instanceof Circle) { t = this.closestParamPoint(p.curr); } else if (p instanceof Rect) { // go through the sides of the colliding rectangle as line segments var shortestIndex; var paramList = new Array(4); var shortestDistance = Number.POSITIVE_INFINITY; for (var i = 0; i < 4; i++) { this.setCorners(p, i); // check for closest points on SCP to side of rectangle var d = this.closestPtSegmentSegment(); if (d < shortestDistance) { shortestDistance = d; shortestIndex = i; paramList[i] = this.s; } } t = paramList[shortestIndex]; } return t; }, setCorners: function(r, i) { var rx = r.curr.x; var ry = r.curr.y; var axes = r.axes; var extents = r.extents; var ae0_x = axes[0].x * extents[0]; var ae0_y = axes[0].y * extents[0]; var ae1_x = axes[1].x * extents[1]; var ae1_y = axes[1].y * extents[1]; var emx = ae0_x - ae1_x; var emy = ae0_y - ae1_y; var epx = ae0_x + ae1_x; var epy = ae0_y + ae1_y; if (i == 0) { // 0 and 1 this.rca.x = rx - epx; this.rca.y = ry - epy; this.rcb.x = rx + emx; this.rcb.y = ry + emy; } else if (i == 1) { // 1 and 2 this.rca.x = rx + emx; this.rca.y = ry + emy; this.rcb.x = rx + epx; this.rcb.y = ry + epy; } else if (i == 2) { // 2 and 3 this.rca.x = rx + epx; this.rca.y = ry + epy; this.rcb.x = rx - emx; this.rcb.y = ry - emy; } else if (i == 3) { // 3 and 0 this.rca.x = rx - emx; this.rca.y = ry - emy; this.rcb.x = rx - epx; this.rcb.y = ry - epy; } }, closestPtSegmentSegment: function() { var pp1 = this.p1.curr; var pq1 = this.p2.curr; var pp2 = this.rca; var pq2 = this.rcb; var d1 = pq1.minus(pp1); var d2 = pq2.minus(pp2); var r = pp1.minus(pp2); var t; var a = d1.dot(d1); var e = d2.dot(d2); var f = d2.dot(r); var c = d1.dot(r); var b = d1.dot(d2); var denom = a * e - b * b; if (denom != 0.0) { this.s = clamp01((b * f - c * e) / denom); } else { this.s = 0.5; // give the midpoint for parallel lines } t = (b * this.s + f) / e; if (t < 0) { t = 0; this.s = clamp01(-c / a); } else if (t > 0) { t = 1; this.s = clamp01((b - c) / a); } var c1 = pp1.plus(d1.mult(this.s)); var c2 = pp2.plus(d2.mult(t)); var c1mc2 = c1.minus(c2); return c1mc2.dot(c1mc2); }, toString: function() { return "SpringRect()"; } }); /** * Initializes the engine. You must call this method prior to adding * any particles or constraints. * * @param dt The delta time value for the engine. This * parameter can be used -- in conjunction with speed at which *
Engine.step()
is called -- to change the speed * of the simulation. Typical values are 1/3 or 1/4. Lower * values result in slower, but more accurate simulations, and * higher ones result in faster, less accurate ones. Note * that this only applies to the forces added to particles. If * you do not add any forces, the
dt
value won't * matter. */ var Engine = function(dt) { dt = dt || 0.25; this.force = new Vector(0, 0); this.gravity = new Vector(0, 0); this.groups = []; this._timeStep = dt * dt; this.damping = 1; this.constraintCycles = 0; this.constraintCollisionCycles = 1; }; Engine.prototype = { step: function() { var a = this.groups; var l = a.length; for (var i = 0; i < l; ++i) { var g = a[i]; g.integrate(this._timeStep, this); } for (var j = 0; j < this.constraintCycles; j++) { for (i = 0; i < l; ++i) { g = a[i]; g.satisfyConstraints(); } } for (j = 0; j < this.constraintCollisionCycles; j++) { for (i = 0; i < l; ++i) { g = a[i]; g.satisfyConstraints(); } for (i = 0; i < l; ++i) { g = a[i]; g.checkCollisions(); } } }, paint: function() { var a = this.groups; var l = a.length; for (var i = 0; i < l; ++i) { var g = a[i]; g.paint(); } } }; ////////////////////////////////////////////////////////////////////// const SVG = "http://www.w3.org/2000/svg"; var Style = function(lineThickness, lineColor, fillColor) { this.lineThickness = lineThickness || 0; this.lineColor = lineColor || '#000000'; this.fillColor = fillColor || '#ffffff'; }; var CircleShape = function(style, x, y, radius, fixed, mass, elasticity, friction) { Circle.call(this, x, y, radius, fixed, mass, elasticity, friction); var shape = document.createElementNS(SVG, "circle"); shape.setAttributeNS(null, "cx", x); shape.setAttributeNS(null, "cy", y); shape.setAttributeNS(null, "r", radius); shape.setAttributeNS(null, "fill", style.fillColor); shape.setAttributeNS(null, "stroke", style.lineColor); shape.setAttributeNS(null, "stroke-width", style.lineThickness); this.shape = shape; }; CircleShape.prototype = Util.concat(new Circle(), { paint: function() { this.shape.setAttributeNS(null, "cx", this.x); this.shape.setAttributeNS(null, "cy", this.y); } }); var RectShape = function(style, x, y, width, height, rotation, fixed, mass, elasticity, friction) { Rect.call(this, x, y, width, height, rotation, fixed, mass, elasticity, friction); var x0 = x - width / 2; var y0 = y - height / 2; var shape = document.createElementNS(SVG, "rect"); shape.setAttributeNS(null, "x", x0); shape.setAttributeNS(null, "y", y0); shape.setAttributeNS(null, "width", width); shape.setAttributeNS(null, "height", height); shape.setAttributeNS(null, "fill", style.fillColor); shape.setAttributeNS(null, "stroke", style.lineColor); shape.setAttributeNS(null, "stroke-width", style.lineThickness); shape.setAttributeNS(null, "transform", "rotate("+this.angle+" "+x+" "+y+")"); this.shape = shape; }; RectShape.prototype = Util.concat(new Rect(), { paint: function() { var shape = this.shape; var x = this.x; var x0 = x - this.width/2; var y = this.y; var y0 = y - this.height/2; var a = this.angle; shape.setAttributeNS(null, "x", x0); shape.setAttributeNS(null, "y", y0); shape.setAttributeNS(null, "transform", "rotate("+a+" "+x+" "+y+")"); } }); var SpringLineShape = function(style, p1, p2, stiffness) { Spring.call(this, p1, p2, stiffness); var line = document.createElementNS(SVG, "line"); line.setAttributeNS(null, "stroke", style.lineColor); line.setAttributeNS(null, "stroke-width", style.lineThickness); this.shape = line; }; SpringLineShape.prototype = Util.concat(new Spring(), { paint: function() { var line = this.shape; var p1 = this.p1.curr; var p2 = this.p2.curr; line.setAttributeNS(null, "x1", p1.x); line.setAttributeNS(null, "y1", p1.y); line.setAttributeNS(null, "x2", p2.x); line.setAttributeNS(null, "y2", p2.y); } }); var Motor = function(attach, radius, style) { Composite.call(this); this.wheel = new Wheel(attach.x, attach.y - .01, radius); var axle = new Spring(this.wheel, attach); this._rimA = new Circle(0, 0, 2, true); this._rimB = new Circle(0, 0, 2, true); this._rimC = new Circle(0, 0, 2, true); this.wheel.collidable = false; this._rimA.collidable = false; this._rimB.collidable = false; this._rimC.collidable = false; this.particles = [this._rimA, this._rimB, this._rimC, this.wheel]; this.constraints = [axle]; this.radius = radius; var g = document.createElementNS(SVG, "g"); g.setAttributeNS(null, "fill", style.fillColor); g.setAttributeNS(null, "stroke", style.lineColor); g.setAttributeNS(null, "stroke-width", style.lineThickness); this.shape = g; // run it once to make sure the rim particles are in the right place this.run(); this.build(g); }; Motor.prototype = Util.concat(new Composite(), { get power() { return this.wheel.speed; }, set power(p) { this.wheel.speed = p; }, get rimA() { return this._rimA; }, get rimB() { return this._rimB; }, get rimC() { return this._rimC; }, run: function() { // align the rim particle based on the this.wheel rotation var theta = this.wheel.radian; this._rimA.x = -this.radius * Math.sin(theta) + this.wheel.x; this._rimA.y = this.radius * Math.cos(theta) + this.wheel.y; theta += Motor.ONE_THIRD; this._rimB.x = -this.radius * Math.sin(theta) + this.wheel.x; this._rimB.y = this.radius * Math.cos(theta) + this.wheel.y; theta += Motor.ONE_THIRD; this._rimC.x = -this.radius * Math.sin(theta) + this.wheel.x; this._rimC.y = this.radius * Math.cos(theta) + this.wheel.y; }, build: function(g) { var c0 = document.createElementNS(SVG, "circle"); c0.setAttributeNS(null, "cx", 0); c0.setAttributeNS(null, "cx", 0); c0.setAttributeNS(null, "r", 3); g.appendChild(c0); var theta = 0; var cx = -this.radius * Math.sin(theta); var cy = this.radius * Math.cos(theta); var l1 = document.createElementNS(SVG, "line"); l1.setAttributeNS(null, "x1", 0); l1.setAttributeNS(null, "y1", 0); l1.setAttributeNS(null, "x2", cx); l1.setAttributeNS(null, "y2", cy); g.appendChild(l1); var c1 = document.createElementNS(SVG, "circle"); c1.setAttributeNS(null, "cx", cx); c1.setAttributeNS(null, "cy", cy); c1.setAttributeNS(null, "r", 2); g.appendChild(c1); theta += Motor.ONE_THIRD; cx = -this.radius * Math.sin(theta); cy = this.radius * Math.cos(theta); var l2 = document.createElementNS(SVG, "line"); l2.setAttributeNS(null, "x1", 0); l2.setAttributeNS(null, "y1", 0); l2.setAttributeNS(null, "x2", cx); l2.setAttributeNS(null, "y2", cy); g.appendChild(l2); var c2 = document.createElementNS(SVG, "circle"); c2.setAttributeNS(null, "cx", cx); c2.setAttributeNS(null, "cy", cy); c2.setAttributeNS(null, "r", 2); g.appendChild(c2); theta += Motor.ONE_THIRD; cx = -this.radius * Math.sin(theta); cy = this.radius * Math.cos(theta); var l3 = document.createElementNS(SVG, "line"); l3.setAttributeNS(null, "x1", 0); l3.setAttributeNS(null, "y1", 0); l3.setAttributeNS(null, "x2", cx); l3.setAttributeNS(null, "y2", cy); g.appendChild(l3); var c3 = document.createElementNS(SVG, "circle"); c3.setAttributeNS(null, "cx", cx); c3.setAttributeNS(null, "cy", cy); c3.setAttributeNS(null, "r", 2); g.appendChild(c3); }, paint: function() { var x = this.wheel.x; var y = this.wheel.y; var a = this.wheel.angle; this.shape.setAttributeNS(null, "transform", "translate("+x+" "+y+") rotate("+a+")"); } }); Motor.ONE_THIRD = (Math.PI * 2) / 3; var Leg = function(px, py, orientation, scale, style) { Composite.call(this); this.style = style; // top triangle -- this.pa is the attach point to the body var os = orientation * scale; this.pa = new Circle(px + 31 * os, py - 8 * scale, 1); this.pb = new Circle(px + 25 * os, py - 37 * scale, 1); this.pc = new Circle(px + 60 * os, py - 15 * scale, 1); // bottom triangle particles -- this.pf is the foot this.pd = new Circle(px + 72 * os, py + 12 * scale, 1); this.pe = new Circle(px + 43 * os, py + 19 * scale, 1); this.pf = new Circle(px + 54 * os, py + 61 * scale, 2); // strut attach point particle this.ph = new Circle(px, py, 3); // top triangle constraints var cAB = new Spring(this.pa, this.pb, 1); var cBC = new Spring(this.pb, this.pc, 1); var cCA = new Spring(this.pc, this.pa, 1); // middle leg constraints var cCD = new Spring(this.pc, this.pd, 1); var cAE = new Spring(this.pa, this.pe, 1); // bottom leg constraints var cDE = new Spring(this.pd, this.pe, 1); var cDF = new Spring(this.pd, this.pf, 1); var cEF = new Spring(this.pe, this.pf, 1); // cam constraints var cBH = new Spring(this.pb, this.ph, 1); var cEH = new Spring(this.pe, this.ph, 1); this.particles = [this.pa, this.pb, this.pc, this.pd, this.pe, this.pf, this.ph]; this.constraints = [cAB, cBC, cCA, cCD, cAE, cDE, cDF, cEF, cBH, cEH]; // for added efficiency, only test the feet (this.pf) for collision. these // selective tweaks should always be considered for best performance. this.pa.collidable = false; this.pb.collidable = false; this.pc.collidable = false; this.pd.collidable = false; this.pe.collidable = false; this.ph.collidable = false; var g = document.createElementNS(SVG, "g"); g.setAttributeNS(null, "fill", style.fillColor); g.setAttributeNS(null, "stroke", style.lineColor); g.setAttributeNS(null, "stroke-width", style.lineThickness); var p1 = document.createElementNS(SVG, "polygon"); g.appendChild(p1); var p2 = document.createElementNS(SVG, "polygon"); g.appendChild(p2); var l1 = document.createElementNS(SVG, "line"); g.appendChild(l1); var l2 = document.createElementNS(SVG, "line"); g.appendChild(l2); var l3 = document.createElementNS(SVG, "line"); g.appendChild(l3); var l4 = document.createElementNS(SVG, "line"); g.appendChild(l4); var c0 = document.createElementNS(SVG, "circle"); c0.setAttributeNS(null, "cx", 0); c0.setAttributeNS(null, "cy", 0); c0.setAttributeNS(null, "r", this.pf.radius); g.appendChild(c0); // this.p1 = p1; this.p2 = p2; this.l1 = l1; this.l2 = l2; this.l3 = l3; this.l4 = l4; this.c0 = c0; this.shape = g; }; Leg.prototype = Util.concat(new Composite(), { get cam() { return this.ph; }, get fix() { return this.pa; }, paint: function() { var pa = this.pa.curr; var pb = this.pb.curr; var pc = this.pc.curr; var pd = this.pd.curr; var pe = this.pe.curr; var pf = this.pf.curr; var ph = this.ph.curr; this.p1.setAttributeNS(null, "points", [pa.x, pa.y, pb.x, pb.y, pc.x, pc.y].join(" ")); this.p2.setAttributeNS(null, "points", [pd.x, pd.y, pe.x, pe.y, pf.x, pf.y].join(" ")); this.l1.setAttributeNS(null, "x1", pa.x); this.l1.setAttributeNS(null, "y1", pa.y); this.l1.setAttributeNS(null, "x2", pe.x); this.l1.setAttributeNS(null, "y2", pe.y); this.l2.setAttributeNS(null, "x1", pc.x); this.l2.setAttributeNS(null, "y1", pc.y); this.l2.setAttributeNS(null, "x2", pd.x); this.l2.setAttributeNS(null, "y2", pd.y); this.l3.setAttributeNS(null, "x1", pb.x); this.l3.setAttributeNS(null, "y1", pb.y); this.l3.setAttributeNS(null, "x2", ph.x); this.l3.setAttributeNS(null, "y2", ph.y); this.l4.setAttributeNS(null, "x1", pe.x); this.l4.setAttributeNS(null, "y1", pe.y); this.l4.setAttributeNS(null, "x2", ph.x); this.l4.setAttributeNS(null, "y2", ph.y); this.c0.setAttributeNS(null, "cx", pf.x); this.c0.setAttributeNS(null, "cy", pf.y); }, set visible(b) { this.shape.setAttributeNS(null, "visibility", b ? "visible" : "hidden"); } }); var Body = function(left, right, height, style1, style2) { Composite.call(this); var sprite = document.createElementNS(SVG, "g"); this.sprite = sprite; var cpx = (right.x + left.x) / 2; var cpy = right.y; this.rgt = new CircleShape(style1, right.x, right.y, 1); sprite.appendChild(this.rgt.shape); this.lft = new CircleShape(style1, left.x, left.y, 1); sprite.appendChild(this.lft.shape); this.ctr = new Circle(cpx, cpy, 1); this.top = new Circle(cpx, cpy - height / 2, 1); this.bot = new Circle(cpx, cpy + height / 2, 1); // outer constraints var tr = new Spring(this.top, this.rgt, 1); var rb = new Spring(this.rgt, this.bot, 1); var bl = new Spring(this.bot, this.lft, 1); var lt = new Spring(this.lft, this.top, 1); // inner constrainst var ct = new Spring(this.top, this.ctr, 1); var cr = new SpringLineShape(style2, this.rgt, this.ctr, 1); sprite.appendChild(cr.shape); var cb = new Spring(this.bot, this.ctr, 1); //cb.visible = false; var cl = new SpringLineShape(style2, this.lft, this.ctr, 1); sprite.appendChild(cl.shape); this.ctr.collidable = false; this.top.collidable = false; this.rgt.collidable = false; this.bot.collidable = false; this.lft.collidable = false; this.particles = [this.ctr, this.top, this.rgt, this.bot, this.lft]; this.constraints = [tr, rb, bl, lt, ct, cr, cb, cl]; }; Body.prototype = Util.concat(new Composite(), { get left() { return this.lft; }, get center() { return this.ctr; }, get right() { return this.rgt; } }); var Robot = function(px, py, scale, power) { Group.call(this); var sprite = document.createElementNS(SVG, "g"); this.sprite = sprite; // styles var style1 = new Style(2, '#444444', '#222222'); var style2 = new Style(2, '#666666', '#444444'); var style3 = new Style(2, '#888888', '#666666'); var style4 = new Style(3, '#336699', '#336699'); var style5 = new Style(2, '#336699'); var style6 = new Style(1, '#336699', '#336699'); var style7 = new Style(2, '#999999'); // legs this.legLA = new Leg(px, py, -1, scale, style1); sprite.appendChild(this.legLA.shape); this.legRA = new Leg(px, py, 1, scale, style1); sprite.appendChild(this.legRA.shape); this.legLB = new Leg(px, py, -1, scale, style2); sprite.appendChild(this.legLB.shape); this.legRB = new Leg(px, py, 1, scale, style2); sprite.appendChild(this.legRB.shape); this.legLC = new Leg(px, py, -1, scale, style3); sprite.appendChild(this.legLC.shape); this.legRC = new Leg(px, py, 1, scale, style3); sprite.appendChild(this.legRC.shape); // this.body this.body = new Body(this.legLA.fix, this.legRA.fix, 30 * scale, style4, style5); sprite.appendChild(this.body.sprite); // this.motor this.motor = new Motor(this.body.center, 8 * scale, style6); sprite.appendChild(this.motor.shape); // connect the this.body to the legs var connLA = new Spring(this.body.left, this.legLA.fix, 1); var connRA = new Spring(this.body.right, this.legRA.fix, 1); var connLB = new Spring(this.body.left, this.legLB.fix, 1); var connRB = new Spring(this.body.right, this.legRB.fix, 1); var connLC = new Spring(this.body.left, this.legLC.fix, 1); var connRC = new Spring(this.body.right, this.legRC.fix, 1); // connect the legs to the this.motor this.legLA.cam.position = this.motor.rimA.position; this.legRA.cam.position = this.motor.rimA.position; var connLAA = new SpringLineShape(style7, this.legLA.cam, this.motor.rimA, 1); sprite.appendChild(connLAA.shape); var connRAA = new SpringLineShape(style7, this.legRA.cam, this.motor.rimA, 1); sprite.appendChild(connRAA.shape); this.legLB.cam.position = this.motor.rimB.position; this.legRB.cam.position = this.motor.rimB.position; var connLBB = new SpringLineShape(style7, this.legLB.cam, this.motor.rimB, 1); sprite.appendChild(connLBB.shape); var connRBB = new SpringLineShape(style7, this.legRB.cam, this.motor.rimB, 1); sprite.appendChild(connRBB.shape); this.legLC.cam.position = this.motor.rimC.position; this.legRC.cam.position = this.motor.rimC.position; var connLCC = new SpringLineShape(style7, this.legLC.cam, this.motor.rimC, 1); sprite.appendChild(connLCC.shape); var connRCC = new SpringLineShape(style7, this.legRC.cam, this.motor.rimC, 1); sprite.appendChild(connRCC.shape); // add to the engine this.composites = [this.legLA, this.legRA, this.legLB, this.legRB, this.legLC, this.legRC, this.body, this.motor]; this.constraints = [connLA, connRA, connLB, connRB, connLC, connRC, connLAA, connRAA, connLBB, connRBB, connLCC, connRCC]; this.direction = -1; this.powerLevel = power; this.powered = true; this.legsVisible = true; }; Robot.prototype = Util.concat(new Group(), { get x() { return this.body.center.x; }, get y() { return this.body.center.y; }, run: function() { this.motor.run(); }, togglePower: function(engine) { this.powered = !this.powered; if (this.powered) { this.motor.power = this.powerLevel * this.direction; this.stiffness = 1; engine.damping = 0.99; } else { this.motor.power = 0; this.stiffness = 0.2; engine.damping = 0.35; } }, toggleDirection: function() { this.direction *= -1; this.motor.power = this.powerLevel * this.direction; }, toggleLegs: function() { this.legsVisible = ! this.legsVisible; if (!this.legsVisible) { this.legLA.visible = false; this.legRA.visible = false; this.legLB.visible = false; this.legRB.visible = false; } else { this.legLA.visible = true; this.legRA.visible = true; this.legLB.visible = true; this.legRB.visible = true; } }, set stiffness(s) { // top level constraints in the group for (var i = 0; i < this.constraints.length; ++i) { var c = this.constraints[i]; c.stiffness = s; } // constraints within this groups composites for (var j = 0; j < this.composites.length; ++j) { var cmp = this.composites[j]; for (i = 0; i < cmp.constraints.length; ++i) { c = cmp.constraints[i]; c.stiffness = s; } } } }); var RobotDemo = function(canvas) { var engine = new Engine(1/4); engine.gravity = new Vector(0, 4); engine.damping = .99; engine.constraintCollisionCycles = 10; // var sprite = document.createElementNS(SVG, "g"); canvas.appendChild(sprite); this.root = sprite; // var robot = new Robot(1250, 260, 1.6, 0.02); sprite.appendChild(robot.sprite); var terrainA = new Group(); var terrainB = new Group(true); var terrainC = new Group(); // var style1 = new Style(0, '#000000', '#999999'); var style2 = new Style(1, '#999999', '#336699'); // var floor = new RectShape(style1, 600, 390, 1700, 100, 0, true, 1, 0, 1); sprite.appendChild(floor.shape); // pyramid of boxes var box0 = new RectShape(style2, 600, 337, 600, 7, 0, true, 10, 0, 1); sprite.appendChild(box0.shape); var box1 = new RectShape(style2, 600, 330, 500, 7, 0, true, 10, 0, 1); sprite.appendChild(box1.shape); var box2 = new RectShape(style2, 600, 323, 400, 7, 0, true, 10, 0, 1); sprite.appendChild(box2.shape); var box3 = new RectShape(style2, 600, 316, 300, 7, 0, true, 10, 0, 1); sprite.appendChild(box3.shape); var box4 = new RectShape(style2, 600, 309, 200, 7, 0, true, 10, 0, 1); sprite.appendChild(box4.shape); var box5 = new RectShape(style2, 600, 302, 100, 7, 0, true, 10, 0, 1); sprite.appendChild(box5.shape); // left side floor var floor2 = new RectShape(style1, -100, 390, 1100, 100, 0.3, true, 1, 0, 1); sprite.appendChild(floor2.shape); var floor3 = new RectShape(style1, -959, 232, 700, 100, 0, true, 1, 0, 1); sprite.appendChild(floor3.shape); var box6 = new RectShape(style2, -990, 12, 50, 25, 0); sprite.appendChild(box6.shape); var floor5 = new RectShape(style1, -1284, 170, 50, 100, 0, true); sprite.appendChild(floor5.shape); // right side floor var floor6 = new RectShape(style1, 1430, 320, 50, 60, 0, true); sprite.appendChild(floor6.shape); engine.groups = [robot, terrainA, terrainB, terrainC]; robot.collisionList = [terrainA, terrainB, terrainC]; robot.togglePower(engine); terrainA.particles = [floor, box0, box1, box2, box3, box4, box5]; terrainB.particles = [floor2, floor3, box6, floor5]; terrainC.particles = [floor6]; // this.engine = engine; this.robot = robot; }; RobotDemo.prototype = { run: function(evt) { var engine = this.engine; var robot = this.robot; engine.step(); robot.run(); engine.paint(); var x = -robot.x + (450/2); this.root.setAttributeNS(null, "transform", "translate("+x+" 0)"); }, keyUp: function(evt) { switch (evt.keyCode) { case 80: // p this.robot.togglePower(this.engine); break; case 82: // r this.robot.toggleDirection(); break; case 72: // h this.robot.toggleLegs(); break; } } }; ////////////////////////////////////////////////////////////////////// var demo = null; var enabled = true; function loaded() { var root = document.createElementNS(SVG, "svg"); root.setAttributeNS(null, "width", "450px"); root.setAttributeNS(null, "height", "350px"); root.setAttributeNS(null, "viewBox", "0 0 450 350"); root.setAttributeNS(null, "version", "1.1"); var rect = document.createElementNS(SVG, "rect"); rect.setAttributeNS(null, "x", "0"); rect.setAttributeNS(null, "y", "0"); rect.setAttributeNS(null, "width", "450"); rect.setAttributeNS(null, "height", "350"); rect.setAttributeNS(null, "fill", "#000000"); root.appendChild(rect); document.body.appendChild(root); demo = new RobotDemo(root); window.setInterval(function() { if (enabled) { demo.run(); } }, 1000/30); } function onoff(button) { if (enabled) { enabled = false; button.innerHTML = "START"; } else { enabled = true; button.innerHTML = "STOP"; } } function poweronoff() { demo.keyUp({keyCode:80}); } function reverseonoff() { demo.keyUp({keyCode:82}); } function visibilityonoff() { demo.keyUp({keyCode:72}); } loaded();
Particle Clock::forked from: Human Clock - js do it
STOP
POWER
REVERSE
VISIBILITY
body { background-color: #DDDDDD; font: 12px sans-serif; }
Pop out
Help
About
×
×