planck-js
Version:
2D physics engine for JavaScript/HTML5 game development
249 lines (198 loc) • 5.9 kB
JavaScript
/*
* Copyright (c) 2016 Ali Shakiba http://shakiba.me/planck.js
* Copyright (c) 2006-2011 Erin Catto http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
function RopeDef() {
this.vertices = [];
this.count = 0;
this.masses = [];
this.gravity = Vec2().SetZero();
this.damping = 0.1;
// Stretching stiffness
this.k2 = 0.9;
// Bending stiffness. Values above 0.5 can make the simulation blow up.
this.k3 = 0.1;
};
function Rope() {
// int32
// this.m_count;
// Vec2 * this.m_ps;
// Vec2 * this.m_p0s;
// Vec2 * this.m_vs;
//
// float * this.m_ims;
//
// float * this.m_Ls;
// float * this.m_as;
this.m_gravity;// Vec2
this.m_damping;
this.m_count = 0;
this.m_ps = null;
this.m_p0s = null;
this.m_vs = null;
this.m_ims = null;
this.m_Ls = null;
this.m_as = null;
this.m_gravity.SetZero();
this.m_k2 = 1.0;
this.m_k3 = 0.1;
}
Rope.prototype.Initialize = function(def) {
Assert(def.count >= 3);
this.m_count = def.count;
this.m_ps = [];// Vec2[m_count]
this.m_p0s = [];// Vec2[m_count]
this.m_vs = [];// Vec2[m_count]
this.m_ims = [];// float[m_count]
for (var i = 0; i < this.m_count; ++i) {
this.m_ps[i] = def.vertices[i];
this.m_p0s[i] = def.vertices[i];
this.m_vs[i].SetZero();
var m = def.masses[i];// float
if (m > 0.0) {
this.m_ims[i] = 1.0 / m;
} else {
this.m_ims[i] = 0.0;
}
}
var count2 = this.m_count - 1;
var count3 = this.m_count - 2;
this.m_Ls = [];// float[count2]
this.m_as = [];// float[count3]
for (var i = 0; i < count2; ++i) {
var p1 = this.m_ps[i];
var p2 = this.m_ps[i + 1];
this.m_Ls[i] = Vec2.Distance(p1, p2);
}
for (var i = 0; i < count3; ++i) {
var p1 = this.m_ps[i];
var p2 = this.m_ps[i + 1];
var p3 = this.m_ps[i + 2];
var d1 = Sub(p2, p1);
var d2 = Sub(p3, p2);
var a = Cross(d1, d2);
var b = Dot(d1, d2);
this.m_as[i] = Atan2(a, b);
}
this.m_gravity = def.gravity;
this.m_damping = def.damping;
this.m_k2 = def.k2;
this.m_k3 = def.k3;
}
Rope.prototype.GetVertexCount = function() {
return this.m_count;
}
Rope.prototype.GetVertices = function() {
return this.m_ps;
}
// h: timeStep
Rope.prototype.Step = function(h, iterations) {
if (h == 0.0) {
return;
}
var d = expf(-h * this.m_damping);
for (var i = 0; i < this.m_count; ++i) {
this.m_p0s[i] = this.m_ps[i];
if (this.m_ims[i] > 0.0) {
this.m_vs[i] += h * this.m_gravity;
}
this.m_vs[i] *= d;
this.m_ps[i] += h * this.m_vs[i];
}
for (var i = 0; i < iterations; ++i) {
SolveC2();
SolveC3();
SolveC2();
}
var inv_h = 1.0 / h;
for (var i = 0; i < this.m_count; ++i) {
this.m_vs[i] = inv_h * (this.m_ps[i] - this.m_p0s[i]);
}
}
Rope.prototype.SolveC2 = function() {
var count2 = this.m_count - 1;
for (var i = 0; i < count2; ++i) {
var p1 = this.m_ps[i]; // Vec2
var p2 = this.m_ps[i + 1]; // Vec2
var d = p2 - p1; // Vec2
var L = d.Normalize();
var im1 = this.m_ims[i];
var im2 = this.m_ims[i + 1];
if (im1 + im2 == 0.0) {
continue;
}
var s1 = im1 / (im1 + im2);
var s2 = im2 / (im1 + im2);
p1 -= this.m_k2 * s1 * (this.m_Ls[i] - L) * d;
p2 += this.m_k2 * s2 * (this.m_Ls[i] - L) * d;
this.m_ps[i] = p1;
this.m_ps[i + 1] = p2;
}
}
Rope.prototype.SetAngle = function(angle) {
var count3 = this.m_count - 2;
for (var i = 0; i < count3; ++i) {
this.m_as[i] = angle;
}
}
Rope.prototype.SolveC3 = function() {
var count3 = this.m_count - 2;
for (var i = 0; i < count3; ++i) {
var p1 = this.m_ps[i]; // Vec2
var p2 = this.m_ps[i + 1]; // Vec2
var p3 = this.m_ps[i + 2]; // Vec2
var m1 = this.m_ims[i];
var m2 = this.m_ims[i + 1];
var m3 = this.m_ims[i + 2];
var d1 = p2 - p1;
var d2 = p3 - p2;
var L1sqr = d1.LengthSquared();
var L2sqr = d2.LengthSquared();
if (L1sqr * L2sqr == 0.0) {
continue;
}
var a = Cross(d1, d2);
var b = Dot(d1, d2);
var angle = Atan2(a, b);
var Jd1 = (-1.0 / L1sqr) * d1.Skew(); // Vec2
var Jd2 = (1.0 / L2sqr) * d2.Skew(); // Vec2
var J1 = -Jd1;
var J2 = Jd1 - Jd2;
var J3 = Jd2;
var mass = m1 * Dot(J1, J1) + m2 * Dot(J2, J2) + m3 * Dot(J3, J3);
if (mass == 0.0) {
continue;
}
mass = 1.0 / mass;
var C = angle - this.m_as[i];
while (C > Math.PI) {
angle -= 2 * Math.PI;
C = angle - this.m_as[i];
}
while (C < -Math.PI) {
angle += 2.0 * Math.PI;
C = angle - this.m_as[i];
}
var impulse = -this.m_k3 * mass * C;
p1 += (m1 * impulse) * J1;
p2 += (m2 * impulse) * J2;
p3 += (m3 * impulse) * J3;
this.m_ps[i] = p1;
this.m_ps[i + 1] = p2;
this.m_ps[i + 2] = p3;
}
}