cocos-enhance-kit/engine/external/box2d/box2d.js
2022-06-25 00:23:03 +08:00

22335 lines
1.0 MiB

(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.box2d = {})));
}(this, (function (exports) { 'use strict';
/*
* Copyright (c) 2006-2009 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 b2Assert(condition) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
if (!condition) {
// debugger;
throw new (Error.bind.apply(Error, [void 0].concat(args)))();
}
}
function b2Maybe(value, def) {
return value !== undefined ? value : def;
}
var b2_maxFloat = 1E+37; // FLT_MAX instead of Number.MAX_VALUE;
var b2_epsilon = 1E-5; // FLT_EPSILON instead of Number.MIN_VALUE;
var b2_epsilon_sq = (b2_epsilon * b2_epsilon);
var b2_pi = 3.14159265359; // Math.PI;
/// @file
/// Global tuning constants based on meters-kilograms-seconds (MKS) units.
///
// Collision
/// The maximum number of contact points between two convex shapes. Do
/// not change this value.
var b2_maxManifoldPoints = 2;
/// The maximum number of vertices on a convex polygon. You cannot increase
/// this too much because b2BlockAllocator has a maximum object size.
var b2_maxPolygonVertices = 8;
/// This is used to fatten AABBs in the dynamic tree. This allows proxies
/// to move by a small amount without triggering a tree adjustment.
/// This is in meters.
var b2_aabbExtension = 0.1;
/// This is used to fatten AABBs in the dynamic tree. This is used to predict
/// the future position based on the current displacement.
/// This is a dimensionless multiplier.
var b2_aabbMultiplier = 2;
/// A small length used as a collision and constraint tolerance. Usually it is
/// chosen to be numerically significant, but visually insignificant.
var b2_linearSlop = 0.008; // 0.005;
/// A small angle used as a collision and constraint tolerance. Usually it is
/// chosen to be numerically significant, but visually insignificant.
var b2_angularSlop = 2 / 180 * b2_pi;
/// The radius of the polygon/edge shape skin. This should not be modified. Making
/// this smaller means polygons will have an insufficient buffer for continuous collision.
/// Making it larger may create artifacts for vertex collision.
var b2_polygonRadius = 2 * b2_linearSlop;
/// Maximum number of sub-steps per contact in continuous physics simulation.
var b2_maxSubSteps = 8;
// Dynamics
/// Maximum number of contacts to be handled to solve a TOI impact.
var b2_maxTOIContacts = 32;
/// A velocity threshold for elastic collisions. Any collision with a relative linear
/// velocity below this threshold will be treated as inelastic.
var b2_velocityThreshold = 1;
/// The maximum linear position correction used when solving constraints. This helps to
/// prevent overshoot.
var b2_maxLinearCorrection = 0.2;
/// The maximum angular position correction used when solving constraints. This helps to
/// prevent overshoot.
var b2_maxAngularCorrection = 8 / 180 * b2_pi;
/// The maximum linear velocity of a body. This limit is very large and is used
/// to prevent numerical problems. You shouldn't need to adjust this.
var b2_maxTranslation = 2;
var b2_maxTranslationSquared = b2_maxTranslation * b2_maxTranslation;
/// The maximum angular velocity of a body. This limit is very large and is used
/// to prevent numerical problems. You shouldn't need to adjust this.
var b2_maxRotation = 0.5 * b2_pi;
var b2_maxRotationSquared = b2_maxRotation * b2_maxRotation;
/// This scale factor controls how fast overlap is resolved. Ideally this would be 1 so
/// that overlap is removed in one time step. However using values close to 1 often lead
/// to overshoot.
var b2_baumgarte = 0.2;
var b2_toiBaumgarte = 0.75;
// #if B2_ENABLE_PARTICLE
// Particle
/// A symbolic constant that stands for particle allocation error.
var b2_invalidParticleIndex = -1;
var b2_maxParticleIndex = 0x7FFFFFFF;
/// The default distance between particles, multiplied by the particle diameter.
var b2_particleStride = 0.75;
/// The minimum particle weight that produces pressure.
var b2_minParticleWeight = 1.0;
/// The upper limit for particle pressure.
var b2_maxParticlePressure = 0.25;
/// The upper limit for force between particles.
var b2_maxParticleForce = 0.5;
/// The maximum distance between particles in a triad, multiplied by the particle diameter.
var b2_maxTriadDistance = 2.0;
var b2_maxTriadDistanceSquared = (b2_maxTriadDistance * b2_maxTriadDistance);
/// The initial size of particle data buffers.
var b2_minParticleSystemBufferCapacity = 256;
/// The time into the future that collisions against barrier particles will be detected.
var b2_barrierCollisionTime = 2.5;
// #endif
// Sleep
/// The time that a body must be still before it will go to sleep.
var b2_timeToSleep = 0.5;
/// A body cannot sleep if its linear velocity is above this tolerance.
var b2_linearSleepTolerance = 0.01;
/// A body cannot sleep if its angular velocity is above this tolerance.
var b2_angularSleepTolerance = 2 / 180 * b2_pi;
// Memory Allocation
/// Implement this function to use your own memory allocator.
function b2Alloc(size) {
return null;
}
/// If you implement b2Alloc, you should also implement this function.
function b2Free(mem) {
}
/// Logging function.
function b2Log(message) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
// console.log(message, ...args);
}
/// Version numbering scheme.
/// See http://en.wikipedia.org/wiki/Software_versioning
var b2Version = /** @class */ (function () {
function b2Version(major, minor, revision) {
if (major === void 0) { major = 0; }
if (minor === void 0) { minor = 0; }
if (revision === void 0) { revision = 0; }
this.major = 0; ///< significant changes
this.minor = 0; ///< incremental changes
this.revision = 0; ///< bug fixes
this.major = major;
this.minor = minor;
this.revision = revision;
}
b2Version.prototype.toString = function () {
return this.major + "." + this.minor + "." + this.revision;
};
return b2Version;
}());
/// Current version.
var b2_version = new b2Version(2, 3, 2);
var b2_branch = "master";
var b2_commit = "fbf51801d80fc389d43dc46524520e89043b6faf";
function b2ParseInt(v) {
return parseInt(v, 10);
}
function b2ParseUInt(v) {
return Math.abs(parseInt(v, 10));
}
function b2MakeArray(length, init) {
var a = [];
for (var i = 0; i < length; ++i) {
a.push(init(i));
}
return a;
}
function b2MakeNullArray(length) {
var a = [];
for (var i = 0; i < length; ++i) {
a.push(null);
}
return a;
}
function b2MakeNumberArray(length, init) {
if (init === void 0) { init = 0; }
var a = [];
for (var i = 0; i < length; ++i) {
a.push(init);
}
return a;
}
/*
* Copyright (c) 2006-2009 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.
*/
var b2_pi_over_180 = b2_pi / 180;
var b2_180_over_pi = 180 / b2_pi;
var b2_two_pi = 2 * b2_pi;
var b2Abs = Math.abs;
var b2Min = Math.min;
var b2Max = Math.max;
function b2Clamp(a, lo, hi) {
return (a < lo) ? (lo) : ((a > hi) ? (hi) : (a));
}
function b2Swap(a, b) {
// DEBUG: b2Assert(false);
var tmp = a[0];
a[0] = b[0];
b[0] = tmp;
}
/// This function is used to ensure that a floating point number is
/// not a NaN or infinity.
var b2IsValid = isFinite;
function b2Sq(n) {
return n * n;
}
/// This is a approximate yet fast inverse square-root.
function b2InvSqrt(n) {
return 1 / Math.sqrt(n);
}
var b2Sqrt = Math.sqrt;
var b2Pow = Math.pow;
function b2DegToRad(degrees) {
return degrees * b2_pi_over_180;
}
function b2RadToDeg(radians) {
return radians * b2_180_over_pi;
}
var b2Cos = Math.cos;
var b2Sin = Math.sin;
var b2Acos = Math.acos;
var b2Asin = Math.asin;
var b2Atan2 = Math.atan2;
function b2NextPowerOfTwo(x) {
x |= (x >> 1) & 0x7FFFFFFF;
x |= (x >> 2) & 0x3FFFFFFF;
x |= (x >> 4) & 0x0FFFFFFF;
x |= (x >> 8) & 0x00FFFFFF;
x |= (x >> 16) & 0x0000FFFF;
return x + 1;
}
function b2IsPowerOfTwo(x) {
return x > 0 && (x & (x - 1)) === 0;
}
function b2Random() {
return Math.random() * 2 - 1;
}
function b2RandomRange(lo, hi) {
return (hi - lo) * Math.random() + lo;
}
/// A 2D column vector.
var b2Vec2 = /** @class */ (function () {
function b2Vec2(x, y) {
if (x === void 0) { x = 0; }
if (y === void 0) { y = 0; }
this.x = x;
this.y = y;
}
b2Vec2.prototype.Clone = function () {
return new b2Vec2(this.x, this.y);
};
b2Vec2.prototype.SetZero = function () {
this.x = 0;
this.y = 0;
return this;
};
b2Vec2.prototype.Set = function (x, y) {
this.x = x;
this.y = y;
return this;
};
b2Vec2.prototype.Copy = function (other) {
this.x = other.x;
this.y = other.y;
return this;
};
b2Vec2.prototype.SelfAdd = function (v) {
this.x += v.x;
this.y += v.y;
return this;
};
b2Vec2.prototype.SelfAddXY = function (x, y) {
this.x += x;
this.y += y;
return this;
};
b2Vec2.prototype.SelfSub = function (v) {
this.x -= v.x;
this.y -= v.y;
return this;
};
b2Vec2.prototype.SelfSubXY = function (x, y) {
this.x -= x;
this.y -= y;
return this;
};
b2Vec2.prototype.SelfMul = function (s) {
this.x *= s;
this.y *= s;
return this;
};
b2Vec2.prototype.SelfMulAdd = function (s, v) {
this.x += s * v.x;
this.y += s * v.y;
return this;
};
b2Vec2.prototype.SelfMulSub = function (s, v) {
this.x -= s * v.x;
this.y -= s * v.y;
return this;
};
b2Vec2.prototype.Dot = function (v) {
return this.x * v.x + this.y * v.y;
};
b2Vec2.prototype.Cross = function (v) {
return this.x * v.y - this.y * v.x;
};
b2Vec2.prototype.Length = function () {
var x = this.x, y = this.y;
return Math.sqrt(x * x + y * y);
};
b2Vec2.prototype.LengthSquared = function () {
var x = this.x, y = this.y;
return (x * x + y * y);
};
b2Vec2.prototype.Normalize = function () {
var length = this.Length();
if (length >= b2_epsilon) {
var inv_length = 1 / length;
this.x *= inv_length;
this.y *= inv_length;
}
return length;
};
b2Vec2.prototype.SelfNormalize = function () {
var length = this.Length();
if (length >= b2_epsilon) {
var inv_length = 1 / length;
this.x *= inv_length;
this.y *= inv_length;
}
return this;
};
b2Vec2.prototype.SelfRotate = function (radians) {
var c = Math.cos(radians);
var s = Math.sin(radians);
var x = this.x;
this.x = c * x - s * this.y;
this.y = s * x + c * this.y;
return this;
};
b2Vec2.prototype.IsValid = function () {
return isFinite(this.x) && isFinite(this.y);
};
b2Vec2.prototype.SelfCrossVS = function (s) {
var x = this.x;
this.x = s * this.y;
this.y = -s * x;
return this;
};
b2Vec2.prototype.SelfCrossSV = function (s) {
var x = this.x;
this.x = -s * this.y;
this.y = s * x;
return this;
};
b2Vec2.prototype.SelfMinV = function (v) {
this.x = b2Min(this.x, v.x);
this.y = b2Min(this.y, v.y);
return this;
};
b2Vec2.prototype.SelfMaxV = function (v) {
this.x = b2Max(this.x, v.x);
this.y = b2Max(this.y, v.y);
return this;
};
b2Vec2.prototype.SelfAbs = function () {
this.x = b2Abs(this.x);
this.y = b2Abs(this.y);
return this;
};
b2Vec2.prototype.SelfNeg = function () {
this.x = (-this.x);
this.y = (-this.y);
return this;
};
b2Vec2.prototype.SelfSkew = function () {
var x = this.x;
this.x = -this.y;
this.y = x;
return this;
};
b2Vec2.MakeArray = function (length) {
return b2MakeArray(length, function (i) { return new b2Vec2(); });
};
b2Vec2.AbsV = function (v, out) {
out.x = b2Abs(v.x);
out.y = b2Abs(v.y);
return out;
};
b2Vec2.MinV = function (a, b, out) {
out.x = b2Min(a.x, b.x);
out.y = b2Min(a.y, b.y);
return out;
};
b2Vec2.MaxV = function (a, b, out) {
out.x = b2Max(a.x, b.x);
out.y = b2Max(a.y, b.y);
return out;
};
b2Vec2.ClampV = function (v, lo, hi, out) {
out.x = b2Clamp(v.x, lo.x, hi.x);
out.y = b2Clamp(v.y, lo.y, hi.y);
return out;
};
b2Vec2.RotateV = function (v, radians, out) {
var v_x = v.x, v_y = v.y;
var c = Math.cos(radians);
var s = Math.sin(radians);
out.x = c * v_x - s * v_y;
out.y = s * v_x + c * v_y;
return out;
};
b2Vec2.DotVV = function (a, b) {
return a.x * b.x + a.y * b.y;
};
b2Vec2.CrossVV = function (a, b) {
return a.x * b.y - a.y * b.x;
};
b2Vec2.CrossVS = function (v, s, out) {
var v_x = v.x;
out.x = s * v.y;
out.y = -s * v_x;
return out;
};
b2Vec2.CrossVOne = function (v, out) {
var v_x = v.x;
out.x = v.y;
out.y = -v_x;
return out;
};
b2Vec2.CrossSV = function (s, v, out) {
var v_x = v.x;
out.x = -s * v.y;
out.y = s * v_x;
return out;
};
b2Vec2.CrossOneV = function (v, out) {
var v_x = v.x;
out.x = -v.y;
out.y = v_x;
return out;
};
b2Vec2.AddVV = function (a, b, out) { out.x = a.x + b.x; out.y = a.y + b.y; return out; };
b2Vec2.SubVV = function (a, b, out) { out.x = a.x - b.x; out.y = a.y - b.y; return out; };
b2Vec2.MulSV = function (s, v, out) { out.x = v.x * s; out.y = v.y * s; return out; };
b2Vec2.MulVS = function (v, s, out) { out.x = v.x * s; out.y = v.y * s; return out; };
b2Vec2.AddVMulSV = function (a, s, b, out) { out.x = a.x + (s * b.x); out.y = a.y + (s * b.y); return out; };
b2Vec2.SubVMulSV = function (a, s, b, out) { out.x = a.x - (s * b.x); out.y = a.y - (s * b.y); return out; };
b2Vec2.AddVCrossSV = function (a, s, v, out) {
var v_x = v.x;
out.x = a.x - (s * v.y);
out.y = a.y + (s * v_x);
return out;
};
b2Vec2.MidVV = function (a, b, out) { out.x = (a.x + b.x) * 0.5; out.y = (a.y + b.y) * 0.5; return out; };
b2Vec2.ExtVV = function (a, b, out) { out.x = (b.x - a.x) * 0.5; out.y = (b.y - a.y) * 0.5; return out; };
b2Vec2.IsEqualToV = function (a, b) {
return a.x === b.x && a.y === b.y;
};
b2Vec2.DistanceVV = function (a, b) {
var c_x = a.x - b.x;
var c_y = a.y - b.y;
return Math.sqrt(c_x * c_x + c_y * c_y);
};
b2Vec2.DistanceSquaredVV = function (a, b) {
var c_x = a.x - b.x;
var c_y = a.y - b.y;
return (c_x * c_x + c_y * c_y);
};
b2Vec2.NegV = function (v, out) { out.x = -v.x; out.y = -v.y; return out; };
b2Vec2.ZERO = new b2Vec2(0, 0);
b2Vec2.UNITX = new b2Vec2(1, 0);
b2Vec2.UNITY = new b2Vec2(0, 1);
b2Vec2.s_t0 = new b2Vec2();
b2Vec2.s_t1 = new b2Vec2();
b2Vec2.s_t2 = new b2Vec2();
b2Vec2.s_t3 = new b2Vec2();
return b2Vec2;
}());
var b2Vec2_zero = new b2Vec2(0, 0);
/// A 2D column vector with 3 elements.
var b2Vec3 = /** @class */ (function () {
function b2Vec3(x, y, z) {
if (x === void 0) { x = 0; }
if (y === void 0) { y = 0; }
if (z === void 0) { z = 0; }
this.x = x;
this.y = y;
this.z = z;
}
b2Vec3.prototype.Clone = function () {
return new b2Vec3(this.x, this.y, this.z);
};
b2Vec3.prototype.SetZero = function () {
this.x = 0;
this.y = 0;
this.z = 0;
return this;
};
b2Vec3.prototype.SetXYZ = function (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
return this;
};
b2Vec3.prototype.Copy = function (other) {
this.x = other.x;
this.y = other.y;
this.z = other.z;
return this;
};
b2Vec3.prototype.SelfNeg = function () {
this.x = (-this.x);
this.y = (-this.y);
this.z = (-this.z);
return this;
};
b2Vec3.prototype.SelfAdd = function (v) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
};
b2Vec3.prototype.SelfAddXYZ = function (x, y, z) {
this.x += x;
this.y += y;
this.z += z;
return this;
};
b2Vec3.prototype.SelfSub = function (v) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
return this;
};
b2Vec3.prototype.SelfSubXYZ = function (x, y, z) {
this.x -= x;
this.y -= y;
this.z -= z;
return this;
};
b2Vec3.prototype.SelfMul = function (s) {
this.x *= s;
this.y *= s;
this.z *= s;
return this;
};
b2Vec3.DotV3V3 = function (a, b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
};
b2Vec3.CrossV3V3 = function (a, b, out) {
var a_x = a.x, a_y = a.y, a_z = a.z;
var b_x = b.x, b_y = b.y, b_z = b.z;
out.x = a_y * b_z - a_z * b_y;
out.y = a_z * b_x - a_x * b_z;
out.z = a_x * b_y - a_y * b_x;
return out;
};
b2Vec3.ZERO = new b2Vec3(0, 0, 0);
b2Vec3.s_t0 = new b2Vec3();
return b2Vec3;
}());
/// A 2-by-2 matrix. Stored in column-major order.
var b2Mat22 = /** @class */ (function () {
function b2Mat22() {
this.ex = new b2Vec2(1, 0);
this.ey = new b2Vec2(0, 1);
}
b2Mat22.prototype.Clone = function () {
return new b2Mat22().Copy(this);
};
b2Mat22.FromVV = function (c1, c2) {
return new b2Mat22().SetVV(c1, c2);
};
b2Mat22.FromSSSS = function (r1c1, r1c2, r2c1, r2c2) {
return new b2Mat22().SetSSSS(r1c1, r1c2, r2c1, r2c2);
};
b2Mat22.FromAngle = function (radians) {
return new b2Mat22().SetAngle(radians);
};
b2Mat22.prototype.SetSSSS = function (r1c1, r1c2, r2c1, r2c2) {
this.ex.Set(r1c1, r2c1);
this.ey.Set(r1c2, r2c2);
return this;
};
b2Mat22.prototype.SetVV = function (c1, c2) {
this.ex.Copy(c1);
this.ey.Copy(c2);
return this;
};
b2Mat22.prototype.SetAngle = function (radians) {
var c = Math.cos(radians);
var s = Math.sin(radians);
this.ex.Set(c, s);
this.ey.Set(-s, c);
return this;
};
b2Mat22.prototype.Copy = function (other) {
this.ex.Copy(other.ex);
this.ey.Copy(other.ey);
return this;
};
b2Mat22.prototype.SetIdentity = function () {
this.ex.Set(1, 0);
this.ey.Set(0, 1);
return this;
};
b2Mat22.prototype.SetZero = function () {
this.ex.SetZero();
this.ey.SetZero();
return this;
};
b2Mat22.prototype.GetAngle = function () {
return Math.atan2(this.ex.y, this.ex.x);
};
b2Mat22.prototype.GetInverse = function (out) {
var a = this.ex.x;
var b = this.ey.x;
var c = this.ex.y;
var d = this.ey.y;
var det = a * d - b * c;
if (det !== 0) {
det = 1 / det;
}
out.ex.x = det * d;
out.ey.x = (-det * b);
out.ex.y = (-det * c);
out.ey.y = det * a;
return out;
};
b2Mat22.prototype.Solve = function (b_x, b_y, out) {
var a11 = this.ex.x, a12 = this.ey.x;
var a21 = this.ex.y, a22 = this.ey.y;
var det = a11 * a22 - a12 * a21;
if (det !== 0) {
det = 1 / det;
}
out.x = det * (a22 * b_x - a12 * b_y);
out.y = det * (a11 * b_y - a21 * b_x);
return out;
};
b2Mat22.prototype.SelfAbs = function () {
this.ex.SelfAbs();
this.ey.SelfAbs();
return this;
};
b2Mat22.prototype.SelfInv = function () {
this.GetInverse(this);
return this;
};
b2Mat22.prototype.SelfAddM = function (M) {
this.ex.SelfAdd(M.ex);
this.ey.SelfAdd(M.ey);
return this;
};
b2Mat22.prototype.SelfSubM = function (M) {
this.ex.SelfSub(M.ex);
this.ey.SelfSub(M.ey);
return this;
};
b2Mat22.AbsM = function (M, out) {
var M_ex = M.ex, M_ey = M.ey;
out.ex.x = b2Abs(M_ex.x);
out.ex.y = b2Abs(M_ex.y);
out.ey.x = b2Abs(M_ey.x);
out.ey.y = b2Abs(M_ey.y);
return out;
};
b2Mat22.MulMV = function (M, v, out) {
var M_ex = M.ex, M_ey = M.ey;
var v_x = v.x, v_y = v.y;
out.x = M_ex.x * v_x + M_ey.x * v_y;
out.y = M_ex.y * v_x + M_ey.y * v_y;
return out;
};
b2Mat22.MulTMV = function (M, v, out) {
var M_ex = M.ex, M_ey = M.ey;
var v_x = v.x, v_y = v.y;
out.x = M_ex.x * v_x + M_ex.y * v_y;
out.y = M_ey.x * v_x + M_ey.y * v_y;
return out;
};
b2Mat22.AddMM = function (A, B, out) {
var A_ex = A.ex, A_ey = A.ey;
var B_ex = B.ex, B_ey = B.ey;
out.ex.x = A_ex.x + B_ex.x;
out.ex.y = A_ex.y + B_ex.y;
out.ey.x = A_ey.x + B_ey.x;
out.ey.y = A_ey.y + B_ey.y;
return out;
};
b2Mat22.MulMM = function (A, B, out) {
var A_ex_x = A.ex.x, A_ex_y = A.ex.y;
var A_ey_x = A.ey.x, A_ey_y = A.ey.y;
var B_ex_x = B.ex.x, B_ex_y = B.ex.y;
var B_ey_x = B.ey.x, B_ey_y = B.ey.y;
out.ex.x = A_ex_x * B_ex_x + A_ey_x * B_ex_y;
out.ex.y = A_ex_y * B_ex_x + A_ey_y * B_ex_y;
out.ey.x = A_ex_x * B_ey_x + A_ey_x * B_ey_y;
out.ey.y = A_ex_y * B_ey_x + A_ey_y * B_ey_y;
return out;
};
b2Mat22.MulTMM = function (A, B, out) {
var A_ex_x = A.ex.x, A_ex_y = A.ex.y;
var A_ey_x = A.ey.x, A_ey_y = A.ey.y;
var B_ex_x = B.ex.x, B_ex_y = B.ex.y;
var B_ey_x = B.ey.x, B_ey_y = B.ey.y;
out.ex.x = A_ex_x * B_ex_x + A_ex_y * B_ex_y;
out.ex.y = A_ey_x * B_ex_x + A_ey_y * B_ex_y;
out.ey.x = A_ex_x * B_ey_x + A_ex_y * B_ey_y;
out.ey.y = A_ey_x * B_ey_x + A_ey_y * B_ey_y;
return out;
};
b2Mat22.IDENTITY = new b2Mat22();
return b2Mat22;
}());
/// A 3-by-3 matrix. Stored in column-major order.
var b2Mat33 = /** @class */ (function () {
function b2Mat33() {
this.ex = new b2Vec3(1, 0, 0);
this.ey = new b2Vec3(0, 1, 0);
this.ez = new b2Vec3(0, 0, 1);
}
b2Mat33.prototype.Clone = function () {
return new b2Mat33().Copy(this);
};
b2Mat33.prototype.SetVVV = function (c1, c2, c3) {
this.ex.Copy(c1);
this.ey.Copy(c2);
this.ez.Copy(c3);
return this;
};
b2Mat33.prototype.Copy = function (other) {
this.ex.Copy(other.ex);
this.ey.Copy(other.ey);
this.ez.Copy(other.ez);
return this;
};
b2Mat33.prototype.SetIdentity = function () {
this.ex.SetXYZ(1, 0, 0);
this.ey.SetXYZ(0, 1, 0);
this.ez.SetXYZ(0, 0, 1);
return this;
};
b2Mat33.prototype.SetZero = function () {
this.ex.SetZero();
this.ey.SetZero();
this.ez.SetZero();
return this;
};
b2Mat33.prototype.SelfAddM = function (M) {
this.ex.SelfAdd(M.ex);
this.ey.SelfAdd(M.ey);
this.ez.SelfAdd(M.ez);
return this;
};
b2Mat33.prototype.Solve33 = function (b_x, b_y, b_z, out) {
var a11 = this.ex.x, a21 = this.ex.y, a31 = this.ex.z;
var a12 = this.ey.x, a22 = this.ey.y, a32 = this.ey.z;
var a13 = this.ez.x, a23 = this.ez.y, a33 = this.ez.z;
var det = a11 * (a22 * a33 - a32 * a23) + a21 * (a32 * a13 - a12 * a33) + a31 * (a12 * a23 - a22 * a13);
if (det !== 0) {
det = 1 / det;
}
out.x = det * (b_x * (a22 * a33 - a32 * a23) + b_y * (a32 * a13 - a12 * a33) + b_z * (a12 * a23 - a22 * a13));
out.y = det * (a11 * (b_y * a33 - b_z * a23) + a21 * (b_z * a13 - b_x * a33) + a31 * (b_x * a23 - b_y * a13));
out.z = det * (a11 * (a22 * b_z - a32 * b_y) + a21 * (a32 * b_x - a12 * b_z) + a31 * (a12 * b_y - a22 * b_x));
return out;
};
b2Mat33.prototype.Solve22 = function (b_x, b_y, out) {
var a11 = this.ex.x, a12 = this.ey.x;
var a21 = this.ex.y, a22 = this.ey.y;
var det = a11 * a22 - a12 * a21;
if (det !== 0) {
det = 1 / det;
}
out.x = det * (a22 * b_x - a12 * b_y);
out.y = det * (a11 * b_y - a21 * b_x);
return out;
};
b2Mat33.prototype.GetInverse22 = function (M) {
var a = this.ex.x, b = this.ey.x, c = this.ex.y, d = this.ey.y;
var det = a * d - b * c;
if (det !== 0) {
det = 1 / det;
}
M.ex.x = det * d;
M.ey.x = -det * b;
M.ex.z = 0;
M.ex.y = -det * c;
M.ey.y = det * a;
M.ey.z = 0;
M.ez.x = 0;
M.ez.y = 0;
M.ez.z = 0;
};
b2Mat33.prototype.GetSymInverse33 = function (M) {
var det = b2Vec3.DotV3V3(this.ex, b2Vec3.CrossV3V3(this.ey, this.ez, b2Vec3.s_t0));
if (det !== 0) {
det = 1 / det;
}
var a11 = this.ex.x, a12 = this.ey.x, a13 = this.ez.x;
var a22 = this.ey.y, a23 = this.ez.y;
var a33 = this.ez.z;
M.ex.x = det * (a22 * a33 - a23 * a23);
M.ex.y = det * (a13 * a23 - a12 * a33);
M.ex.z = det * (a12 * a23 - a13 * a22);
M.ey.x = M.ex.y;
M.ey.y = det * (a11 * a33 - a13 * a13);
M.ey.z = det * (a13 * a12 - a11 * a23);
M.ez.x = M.ex.z;
M.ez.y = M.ey.z;
M.ez.z = det * (a11 * a22 - a12 * a12);
};
b2Mat33.MulM33V3 = function (A, v, out) {
var v_x = v.x, v_y = v.y, v_z = v.z;
out.x = A.ex.x * v_x + A.ey.x * v_y + A.ez.x * v_z;
out.y = A.ex.y * v_x + A.ey.y * v_y + A.ez.y * v_z;
out.z = A.ex.z * v_x + A.ey.z * v_y + A.ez.z * v_z;
return out;
};
b2Mat33.MulM33XYZ = function (A, x, y, z, out) {
out.x = A.ex.x * x + A.ey.x * y + A.ez.x * z;
out.y = A.ex.y * x + A.ey.y * y + A.ez.y * z;
out.z = A.ex.z * x + A.ey.z * y + A.ez.z * z;
return out;
};
b2Mat33.MulM33V2 = function (A, v, out) {
var v_x = v.x, v_y = v.y;
out.x = A.ex.x * v_x + A.ey.x * v_y;
out.y = A.ex.y * v_x + A.ey.y * v_y;
return out;
};
b2Mat33.MulM33XY = function (A, x, y, out) {
out.x = A.ex.x * x + A.ey.x * y;
out.y = A.ex.y * x + A.ey.y * y;
return out;
};
b2Mat33.IDENTITY = new b2Mat33();
return b2Mat33;
}());
/// Rotation
var b2Rot = /** @class */ (function () {
function b2Rot(angle) {
if (angle === void 0) { angle = 0; }
this.s = 0;
this.c = 1;
if (angle) {
this.s = Math.sin(angle);
this.c = Math.cos(angle);
}
}
b2Rot.prototype.Clone = function () {
return new b2Rot().Copy(this);
};
b2Rot.prototype.Copy = function (other) {
this.s = other.s;
this.c = other.c;
return this;
};
b2Rot.prototype.SetAngle = function (angle) {
this.s = Math.sin(angle);
this.c = Math.cos(angle);
return this;
};
b2Rot.prototype.SetIdentity = function () {
this.s = 0;
this.c = 1;
return this;
};
b2Rot.prototype.GetAngle = function () {
return Math.atan2(this.s, this.c);
};
b2Rot.prototype.GetXAxis = function (out) {
out.x = this.c;
out.y = this.s;
return out;
};
b2Rot.prototype.GetYAxis = function (out) {
out.x = -this.s;
out.y = this.c;
return out;
};
b2Rot.MulRR = function (q, r, out) {
// [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc]
// [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc]
// s = qs * rc + qc * rs
// c = qc * rc - qs * rs
var q_c = q.c, q_s = q.s;
var r_c = r.c, r_s = r.s;
out.s = q_s * r_c + q_c * r_s;
out.c = q_c * r_c - q_s * r_s;
return out;
};
b2Rot.MulTRR = function (q, r, out) {
// [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc]
// [-qs qc] [rs rc] [-qs*rc+qc*rs qs*rs+qc*rc]
// s = qc * rs - qs * rc
// c = qc * rc + qs * rs
var q_c = q.c, q_s = q.s;
var r_c = r.c, r_s = r.s;
out.s = q_c * r_s - q_s * r_c;
out.c = q_c * r_c + q_s * r_s;
return out;
};
b2Rot.MulRV = function (q, v, out) {
var q_c = q.c, q_s = q.s;
var v_x = v.x, v_y = v.y;
out.x = q_c * v_x - q_s * v_y;
out.y = q_s * v_x + q_c * v_y;
return out;
};
b2Rot.MulTRV = function (q, v, out) {
var q_c = q.c, q_s = q.s;
var v_x = v.x, v_y = v.y;
out.x = q_c * v_x + q_s * v_y;
out.y = -q_s * v_x + q_c * v_y;
return out;
};
b2Rot.IDENTITY = new b2Rot();
return b2Rot;
}());
/// A transform contains translation and rotation. It is used to represent
/// the position and orientation of rigid frames.
var b2Transform = /** @class */ (function () {
function b2Transform() {
this.p = new b2Vec2();
this.q = new b2Rot();
}
b2Transform.prototype.Clone = function () {
return new b2Transform().Copy(this);
};
b2Transform.prototype.Copy = function (other) {
this.p.Copy(other.p);
this.q.Copy(other.q);
return this;
};
b2Transform.prototype.SetIdentity = function () {
this.p.SetZero();
this.q.SetIdentity();
return this;
};
b2Transform.prototype.SetPositionRotation = function (position, q) {
this.p.Copy(position);
this.q.Copy(q);
return this;
};
b2Transform.prototype.SetPositionAngle = function (pos, a) {
this.p.Copy(pos);
this.q.SetAngle(a);
return this;
};
b2Transform.prototype.SetPosition = function (position) {
this.p.Copy(position);
return this;
};
b2Transform.prototype.SetPositionXY = function (x, y) {
this.p.Set(x, y);
return this;
};
b2Transform.prototype.SetRotation = function (rotation) {
this.q.Copy(rotation);
return this;
};
b2Transform.prototype.SetRotationAngle = function (radians) {
this.q.SetAngle(radians);
return this;
};
b2Transform.prototype.GetPosition = function () {
return this.p;
};
b2Transform.prototype.GetRotation = function () {
return this.q;
};
b2Transform.prototype.GetRotationAngle = function () {
return this.q.GetAngle();
};
b2Transform.prototype.GetAngle = function () {
return this.q.GetAngle();
};
b2Transform.MulXV = function (T, v, out) {
// float32 x = (T.q.c * v.x - T.q.s * v.y) + T.p.x;
// float32 y = (T.q.s * v.x + T.q.c * v.y) + T.p.y;
// return b2Vec2(x, y);
var T_q_c = T.q.c, T_q_s = T.q.s;
var v_x = v.x, v_y = v.y;
out.x = (T_q_c * v_x - T_q_s * v_y) + T.p.x;
out.y = (T_q_s * v_x + T_q_c * v_y) + T.p.y;
return out;
};
b2Transform.MulTXV = function (T, v, out) {
// float32 px = v.x - T.p.x;
// float32 py = v.y - T.p.y;
// float32 x = (T.q.c * px + T.q.s * py);
// float32 y = (-T.q.s * px + T.q.c * py);
// return b2Vec2(x, y);
var T_q_c = T.q.c, T_q_s = T.q.s;
var p_x = v.x - T.p.x;
var p_y = v.y - T.p.y;
out.x = (T_q_c * p_x + T_q_s * p_y);
out.y = (-T_q_s * p_x + T_q_c * p_y);
return out;
};
b2Transform.MulXX = function (A, B, out) {
b2Rot.MulRR(A.q, B.q, out.q);
b2Vec2.AddVV(b2Rot.MulRV(A.q, B.p, out.p), A.p, out.p);
return out;
};
b2Transform.MulTXX = function (A, B, out) {
b2Rot.MulTRR(A.q, B.q, out.q);
b2Rot.MulTRV(A.q, b2Vec2.SubVV(B.p, A.p, out.p), out.p);
return out;
};
b2Transform.IDENTITY = new b2Transform();
return b2Transform;
}());
/// This describes the motion of a body/shape for TOI computation.
/// Shapes are defined with respect to the body origin, which may
/// no coincide with the center of mass. However, to support dynamics
/// we must interpolate the center of mass position.
var b2Sweep = /** @class */ (function () {
function b2Sweep() {
this.localCenter = new b2Vec2();
this.c0 = new b2Vec2();
this.c = new b2Vec2();
this.a0 = 0;
this.a = 0;
this.alpha0 = 0;
}
b2Sweep.prototype.Clone = function () {
return new b2Sweep().Copy(this);
};
b2Sweep.prototype.Copy = function (other) {
this.localCenter.Copy(other.localCenter);
this.c0.Copy(other.c0);
this.c.Copy(other.c);
this.a0 = other.a0;
this.a = other.a;
this.alpha0 = other.alpha0;
return this;
};
b2Sweep.prototype.GetTransform = function (xf, beta) {
var one_minus_beta = (1 - beta);
xf.p.x = one_minus_beta * this.c0.x + beta * this.c.x;
xf.p.y = one_minus_beta * this.c0.y + beta * this.c.y;
var angle = one_minus_beta * this.a0 + beta * this.a;
xf.q.SetAngle(angle);
xf.p.SelfSub(b2Rot.MulRV(xf.q, this.localCenter, b2Vec2.s_t0));
return xf;
};
b2Sweep.prototype.Advance = function (alpha) {
// DEBUG: b2Assert(this.alpha0 < 1);
var beta = (alpha - this.alpha0) / (1 - this.alpha0);
var one_minus_beta = (1 - beta);
this.c0.x = one_minus_beta * this.c0.x + beta * this.c.x;
this.c0.y = one_minus_beta * this.c0.y + beta * this.c.y;
this.a0 = one_minus_beta * this.a0 + beta * this.a;
this.alpha0 = alpha;
};
b2Sweep.prototype.Normalize = function () {
var d = b2_two_pi * Math.floor(this.a0 / b2_two_pi);
this.a0 -= d;
this.a -= d;
};
return b2Sweep;
}());
/*
* Copyright (c) 2011 Erin Catto http://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.
*/
/// Color for debug drawing. Each value has the range [0,1].
var b2Color = /** @class */ (function () {
function b2Color(rr, gg, bb, aa) {
if (rr === void 0) { rr = 0.5; }
if (gg === void 0) { gg = 0.5; }
if (bb === void 0) { bb = 0.5; }
if (aa === void 0) { aa = 1.0; }
this.r = rr;
this.g = gg;
this.b = bb;
this.a = aa;
}
b2Color.prototype.Clone = function () {
return new b2Color().Copy(this);
};
b2Color.prototype.Copy = function (other) {
this.r = other.r;
this.g = other.g;
this.b = other.b;
this.a = other.a;
return this;
};
b2Color.prototype.IsEqual = function (color) {
return (this.r === color.r) && (this.g === color.g) && (this.b === color.b) && (this.a === color.a);
};
b2Color.prototype.IsZero = function () {
return (this.r === 0) && (this.g === 0) && (this.b === 0) && (this.a === 0);
};
b2Color.prototype.Set = function (r, g, b, a) {
if (a === void 0) { a = this.a; }
this.SetRGBA(r, g, b, a);
};
b2Color.prototype.SetByteRGB = function (r, g, b) {
this.r = r / 0xff;
this.g = g / 0xff;
this.b = b / 0xff;
return this;
};
b2Color.prototype.SetByteRGBA = function (r, g, b, a) {
this.r = r / 0xff;
this.g = g / 0xff;
this.b = b / 0xff;
this.a = a / 0xff;
return this;
};
b2Color.prototype.SetRGB = function (rr, gg, bb) {
this.r = rr;
this.g = gg;
this.b = bb;
return this;
};
b2Color.prototype.SetRGBA = function (rr, gg, bb, aa) {
this.r = rr;
this.g = gg;
this.b = bb;
this.a = aa;
return this;
};
b2Color.prototype.SelfAdd = function (color) {
this.r += color.r;
this.g += color.g;
this.b += color.b;
this.a += color.a;
return this;
};
b2Color.prototype.Add = function (color, out) {
out.r = this.r + color.r;
out.g = this.g + color.g;
out.b = this.b + color.b;
out.a = this.a + color.a;
return out;
};
b2Color.prototype.SelfSub = function (color) {
this.r -= color.r;
this.g -= color.g;
this.b -= color.b;
this.a -= color.a;
return this;
};
b2Color.prototype.Sub = function (color, out) {
out.r = this.r - color.r;
out.g = this.g - color.g;
out.b = this.b - color.b;
out.a = this.a - color.a;
return out;
};
b2Color.prototype.SelfMul = function (s) {
this.r *= s;
this.g *= s;
this.b *= s;
this.a *= s;
return this;
};
b2Color.prototype.Mul = function (s, out) {
out.r = this.r * s;
out.g = this.g * s;
out.b = this.b * s;
out.a = this.a * s;
return out;
};
b2Color.prototype.Mix = function (mixColor, strength) {
b2Color.MixColors(this, mixColor, strength);
};
b2Color.MixColors = function (colorA, colorB, strength) {
var dr = (strength * (colorB.r - colorA.r));
var dg = (strength * (colorB.g - colorA.g));
var db = (strength * (colorB.b - colorA.b));
var da = (strength * (colorB.a - colorA.a));
colorA.r += dr;
colorA.g += dg;
colorA.b += db;
colorA.a += da;
colorB.r -= dr;
colorB.g -= dg;
colorB.b -= db;
colorB.a -= da;
};
b2Color.prototype.MakeStyleString = function (alpha) {
if (alpha === void 0) { alpha = this.a; }
return b2Color.MakeStyleString(this.r, this.g, this.b, alpha);
};
b2Color.MakeStyleString = function (r, g, b, a) {
if (a === void 0) { a = 1.0; }
// function clamp(x: number, lo: number, hi: number) { return x < lo ? lo : hi < x ? hi : x; }
r *= 255; // r = clamp(r, 0, 255);
g *= 255; // g = clamp(g, 0, 255);
b *= 255; // b = clamp(b, 0, 255);
// a = clamp(a, 0, 1);
if (a < 1) {
return "rgba(" + r + "," + g + "," + b + "," + a + ")";
}
else {
return "rgb(" + r + "," + g + "," + b + ")";
}
};
b2Color.ZERO = new b2Color(0, 0, 0, 0);
b2Color.RED = new b2Color(1, 0, 0);
b2Color.GREEN = new b2Color(0, 1, 0);
b2Color.BLUE = new b2Color(0, 0, 1);
return b2Color;
}());
(function (b2DrawFlags) {
b2DrawFlags[b2DrawFlags["e_none"] = 0] = "e_none";
b2DrawFlags[b2DrawFlags["e_shapeBit"] = 1] = "e_shapeBit";
b2DrawFlags[b2DrawFlags["e_jointBit"] = 2] = "e_jointBit";
b2DrawFlags[b2DrawFlags["e_aabbBit"] = 4] = "e_aabbBit";
b2DrawFlags[b2DrawFlags["e_pairBit"] = 8] = "e_pairBit";
b2DrawFlags[b2DrawFlags["e_centerOfMassBit"] = 16] = "e_centerOfMassBit";
// #if B2_ENABLE_PARTICLE
b2DrawFlags[b2DrawFlags["e_particleBit"] = 32] = "e_particleBit";
// #endif
b2DrawFlags[b2DrawFlags["e_controllerBit"] = 64] = "e_controllerBit";
b2DrawFlags[b2DrawFlags["e_all"] = 63] = "e_all";
})(exports.b2DrawFlags || (exports.b2DrawFlags = {}));
/// Implement and register this class with a b2World to provide debug drawing of physics
/// entities in your game.
var b2Draw = /** @class */ (function () {
function b2Draw() {
this.m_drawFlags = 0;
}
b2Draw.prototype.SetFlags = function (flags) {
this.m_drawFlags = flags;
};
b2Draw.prototype.GetFlags = function () {
return this.m_drawFlags;
};
b2Draw.prototype.AppendFlags = function (flags) {
this.m_drawFlags |= flags;
};
b2Draw.prototype.ClearFlags = function (flags) {
this.m_drawFlags &= ~flags;
};
return b2Draw;
}());
/*
* Copyright (c) 2011 Erin Catto http://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.
*/
/// Timer for profiling. This has platform specific code and may
/// not work on every platform.
var b2Timer = /** @class */ (function () {
function b2Timer() {
this.m_start = Date.now();
}
/// Reset the timer.
b2Timer.prototype.Reset = function () {
this.m_start = Date.now();
return this;
};
/// Get the time since construction or the last reset.
b2Timer.prototype.GetMilliseconds = function () {
return Date.now() - this.m_start;
};
return b2Timer;
}());
var b2Counter = /** @class */ (function () {
function b2Counter() {
this.m_count = 0;
this.m_min_count = 0;
this.m_max_count = 0;
}
b2Counter.prototype.GetCount = function () {
return this.m_count;
};
b2Counter.prototype.GetMinCount = function () {
return this.m_min_count;
};
b2Counter.prototype.GetMaxCount = function () {
return this.m_max_count;
};
b2Counter.prototype.ResetCount = function () {
var count = this.m_count;
this.m_count = 0;
return count;
};
b2Counter.prototype.ResetMinCount = function () {
this.m_min_count = 0;
};
b2Counter.prototype.ResetMaxCount = function () {
this.m_max_count = 0;
};
b2Counter.prototype.Increment = function () {
this.m_count++;
if (this.m_max_count < this.m_count) {
this.m_max_count = this.m_count;
}
};
b2Counter.prototype.Decrement = function () {
this.m_count--;
if (this.m_min_count > this.m_count) {
this.m_min_count = this.m_count;
}
};
return b2Counter;
}());
/*
* Copyright (c) 2010 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.
*/
/// This is a growable LIFO stack with an initial capacity of N.
/// If the stack size exceeds the initial capacity, the heap is used
/// to increase the size of the stack.
var b2GrowableStack = /** @class */ (function () {
function b2GrowableStack(N) {
this.m_stack = [];
this.m_count = 0;
this.m_stack = b2MakeArray(N, function (index) { return null; });
this.m_count = 0;
}
b2GrowableStack.prototype.Reset = function () {
this.m_count = 0;
return this;
};
b2GrowableStack.prototype.Push = function (element) {
this.m_stack[this.m_count] = element;
this.m_count++;
};
b2GrowableStack.prototype.Pop = function () {
// DEBUG: b2Assert(this.m_count > 0);
this.m_count--;
var element = this.m_stack[this.m_count];
this.m_stack[this.m_count] = null;
if (element === null) {
throw new Error();
}
return element;
};
b2GrowableStack.prototype.GetCount = function () {
return this.m_count;
};
return b2GrowableStack;
}());
/*
* Copyright (c) 2006-2009 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.
*/
var b2BlockAllocator = /** @class */ (function () {
function b2BlockAllocator() {
}
return b2BlockAllocator;
}());
/*
* Copyright (c) 2006-2009 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.
*/
var b2StackAllocator = /** @class */ (function () {
function b2StackAllocator() {
}
return b2StackAllocator;
}());
/*
* Copyright (c) 2006-2009 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.
*/
/// A distance proxy is used by the GJK algorithm.
/// It encapsulates any shape.
var b2DistanceProxy = /** @class */ (function () {
function b2DistanceProxy() {
this.m_buffer = b2Vec2.MakeArray(2);
this.m_vertices = this.m_buffer;
this.m_count = 0;
this.m_radius = 0;
}
b2DistanceProxy.prototype.Copy = function (other) {
if (other.m_vertices === other.m_buffer) {
this.m_vertices = this.m_buffer;
this.m_buffer[0].Copy(other.m_buffer[0]);
this.m_buffer[1].Copy(other.m_buffer[1]);
}
else {
this.m_vertices = other.m_vertices;
}
this.m_count = other.m_count;
this.m_radius = other.m_radius;
return this;
};
b2DistanceProxy.prototype.Reset = function () {
this.m_vertices = this.m_buffer;
this.m_count = 0;
this.m_radius = 0;
return this;
};
b2DistanceProxy.prototype.SetShape = function (shape, index) {
shape.SetupDistanceProxy(this, index);
};
b2DistanceProxy.prototype.SetVerticesRadius = function (vertices, count, radius) {
this.m_vertices = vertices;
this.m_count = count;
this.m_radius = radius;
};
b2DistanceProxy.prototype.GetSupport = function (d) {
var bestIndex = 0;
var bestValue = b2Vec2.DotVV(this.m_vertices[0], d);
for (var i = 1; i < this.m_count; ++i) {
var value = b2Vec2.DotVV(this.m_vertices[i], d);
if (value > bestValue) {
bestIndex = i;
bestValue = value;
}
}
return bestIndex;
};
b2DistanceProxy.prototype.GetSupportVertex = function (d) {
var bestIndex = 0;
var bestValue = b2Vec2.DotVV(this.m_vertices[0], d);
for (var i = 1; i < this.m_count; ++i) {
var value = b2Vec2.DotVV(this.m_vertices[i], d);
if (value > bestValue) {
bestIndex = i;
bestValue = value;
}
}
return this.m_vertices[bestIndex];
};
b2DistanceProxy.prototype.GetVertexCount = function () {
return this.m_count;
};
b2DistanceProxy.prototype.GetVertex = function (index) {
// DEBUG: b2Assert(0 <= index && index < this.m_count);
return this.m_vertices[index];
};
return b2DistanceProxy;
}());
var b2SimplexCache = /** @class */ (function () {
function b2SimplexCache() {
this.metric = 0;
this.count = 0;
this.indexA = [0, 0, 0];
this.indexB = [0, 0, 0];
}
b2SimplexCache.prototype.Reset = function () {
this.metric = 0;
this.count = 0;
return this;
};
return b2SimplexCache;
}());
var b2DistanceInput = /** @class */ (function () {
function b2DistanceInput() {
this.proxyA = new b2DistanceProxy();
this.proxyB = new b2DistanceProxy();
this.transformA = new b2Transform();
this.transformB = new b2Transform();
this.useRadii = false;
}
b2DistanceInput.prototype.Reset = function () {
this.proxyA.Reset();
this.proxyB.Reset();
this.transformA.SetIdentity();
this.transformB.SetIdentity();
this.useRadii = false;
return this;
};
return b2DistanceInput;
}());
var b2DistanceOutput = /** @class */ (function () {
function b2DistanceOutput() {
this.pointA = new b2Vec2();
this.pointB = new b2Vec2();
this.distance = 0;
this.iterations = 0; ///< number of GJK iterations used
}
b2DistanceOutput.prototype.Reset = function () {
this.pointA.SetZero();
this.pointB.SetZero();
this.distance = 0;
this.iterations = 0;
return this;
};
return b2DistanceOutput;
}());
/// Input parameters for b2ShapeCast
var b2ShapeCastInput = /** @class */ (function () {
function b2ShapeCastInput() {
this.proxyA = new b2DistanceProxy();
this.proxyB = new b2DistanceProxy();
this.transformA = new b2Transform();
this.transformB = new b2Transform();
this.translationB = new b2Vec2();
}
return b2ShapeCastInput;
}());
/// Output results for b2ShapeCast
var b2ShapeCastOutput = /** @class */ (function () {
function b2ShapeCastOutput() {
this.point = new b2Vec2();
this.normal = new b2Vec2();
this.lambda = 0.0;
this.iterations = 0;
}
return b2ShapeCastOutput;
}());
exports.b2_gjkCalls = 0;
exports.b2_gjkIters = 0;
exports.b2_gjkMaxIters = 0;
function b2_gjk_reset() {
exports.b2_gjkCalls = 0;
exports.b2_gjkIters = 0;
exports.b2_gjkMaxIters = 0;
}
var b2SimplexVertex = /** @class */ (function () {
function b2SimplexVertex() {
this.wA = new b2Vec2(); // support point in proxyA
this.wB = new b2Vec2(); // support point in proxyB
this.w = new b2Vec2(); // wB - wA
this.a = 0; // barycentric coordinate for closest point
this.indexA = 0; // wA index
this.indexB = 0; // wB index
}
b2SimplexVertex.prototype.Copy = function (other) {
this.wA.Copy(other.wA); // support point in proxyA
this.wB.Copy(other.wB); // support point in proxyB
this.w.Copy(other.w); // wB - wA
this.a = other.a; // barycentric coordinate for closest point
this.indexA = other.indexA; // wA index
this.indexB = other.indexB; // wB index
return this;
};
return b2SimplexVertex;
}());
var b2Simplex = /** @class */ (function () {
function b2Simplex() {
this.m_v1 = new b2SimplexVertex();
this.m_v2 = new b2SimplexVertex();
this.m_v3 = new b2SimplexVertex();
this.m_vertices = [ /*3*/];
this.m_count = 0;
this.m_vertices[0] = this.m_v1;
this.m_vertices[1] = this.m_v2;
this.m_vertices[2] = this.m_v3;
}
b2Simplex.prototype.ReadCache = function (cache, proxyA, transformA, proxyB, transformB) {
// DEBUG: b2Assert(0 <= cache.count && cache.count <= 3);
// Copy data from cache.
this.m_count = cache.count;
var vertices = this.m_vertices;
for (var i = 0; i < this.m_count; ++i) {
var v = vertices[i];
v.indexA = cache.indexA[i];
v.indexB = cache.indexB[i];
var wALocal = proxyA.GetVertex(v.indexA);
var wBLocal = proxyB.GetVertex(v.indexB);
b2Transform.MulXV(transformA, wALocal, v.wA);
b2Transform.MulXV(transformB, wBLocal, v.wB);
b2Vec2.SubVV(v.wB, v.wA, v.w);
v.a = 0;
}
// Compute the new simplex metric, if it is substantially different than
// old metric then flush the simplex.
if (this.m_count > 1) {
var metric1 = cache.metric;
var metric2 = this.GetMetric();
if (metric2 < 0.5 * metric1 || 2 * metric1 < metric2 || metric2 < b2_epsilon) {
// Reset the simplex.
this.m_count = 0;
}
}
// If the cache is empty or invalid ...
if (this.m_count === 0) {
var v = vertices[0];
v.indexA = 0;
v.indexB = 0;
var wALocal = proxyA.GetVertex(0);
var wBLocal = proxyB.GetVertex(0);
b2Transform.MulXV(transformA, wALocal, v.wA);
b2Transform.MulXV(transformB, wBLocal, v.wB);
b2Vec2.SubVV(v.wB, v.wA, v.w);
v.a = 1;
this.m_count = 1;
}
};
b2Simplex.prototype.WriteCache = function (cache) {
cache.metric = this.GetMetric();
cache.count = this.m_count;
var vertices = this.m_vertices;
for (var i = 0; i < this.m_count; ++i) {
cache.indexA[i] = vertices[i].indexA;
cache.indexB[i] = vertices[i].indexB;
}
};
b2Simplex.prototype.GetSearchDirection = function (out) {
switch (this.m_count) {
case 1:
return b2Vec2.NegV(this.m_v1.w, out);
case 2: {
var e12 = b2Vec2.SubVV(this.m_v2.w, this.m_v1.w, out);
var sgn = b2Vec2.CrossVV(e12, b2Vec2.NegV(this.m_v1.w, b2Vec2.s_t0));
if (sgn > 0) {
// Origin is left of e12.
return b2Vec2.CrossOneV(e12, out);
}
else {
// Origin is right of e12.
return b2Vec2.CrossVOne(e12, out);
}
}
default:
// DEBUG: b2Assert(false);
return out.SetZero();
}
};
b2Simplex.prototype.GetClosestPoint = function (out) {
switch (this.m_count) {
case 0:
// DEBUG: b2Assert(false);
return out.SetZero();
case 1:
return out.Copy(this.m_v1.w);
case 2:
return out.Set(this.m_v1.a * this.m_v1.w.x + this.m_v2.a * this.m_v2.w.x, this.m_v1.a * this.m_v1.w.y + this.m_v2.a * this.m_v2.w.y);
case 3:
return out.SetZero();
default:
// DEBUG: b2Assert(false);
return out.SetZero();
}
};
b2Simplex.prototype.GetWitnessPoints = function (pA, pB) {
switch (this.m_count) {
case 0:
// DEBUG: b2Assert(false);
break;
case 1:
pA.Copy(this.m_v1.wA);
pB.Copy(this.m_v1.wB);
break;
case 2:
pA.x = this.m_v1.a * this.m_v1.wA.x + this.m_v2.a * this.m_v2.wA.x;
pA.y = this.m_v1.a * this.m_v1.wA.y + this.m_v2.a * this.m_v2.wA.y;
pB.x = this.m_v1.a * this.m_v1.wB.x + this.m_v2.a * this.m_v2.wB.x;
pB.y = this.m_v1.a * this.m_v1.wB.y + this.m_v2.a * this.m_v2.wB.y;
break;
case 3:
pB.x = pA.x = this.m_v1.a * this.m_v1.wA.x + this.m_v2.a * this.m_v2.wA.x + this.m_v3.a * this.m_v3.wA.x;
pB.y = pA.y = this.m_v1.a * this.m_v1.wA.y + this.m_v2.a * this.m_v2.wA.y + this.m_v3.a * this.m_v3.wA.y;
break;
default:
// DEBUG: b2Assert(false);
break;
}
};
b2Simplex.prototype.GetMetric = function () {
switch (this.m_count) {
case 0:
// DEBUG: b2Assert(false);
return 0;
case 1:
return 0;
case 2:
return b2Vec2.DistanceVV(this.m_v1.w, this.m_v2.w);
case 3:
return b2Vec2.CrossVV(b2Vec2.SubVV(this.m_v2.w, this.m_v1.w, b2Vec2.s_t0), b2Vec2.SubVV(this.m_v3.w, this.m_v1.w, b2Vec2.s_t1));
default:
// DEBUG: b2Assert(false);
return 0;
}
};
b2Simplex.prototype.Solve2 = function () {
var w1 = this.m_v1.w;
var w2 = this.m_v2.w;
var e12 = b2Vec2.SubVV(w2, w1, b2Simplex.s_e12);
// w1 region
var d12_2 = (-b2Vec2.DotVV(w1, e12));
if (d12_2 <= 0) {
// a2 <= 0, so we clamp it to 0
this.m_v1.a = 1;
this.m_count = 1;
return;
}
// w2 region
var d12_1 = b2Vec2.DotVV(w2, e12);
if (d12_1 <= 0) {
// a1 <= 0, so we clamp it to 0
this.m_v2.a = 1;
this.m_count = 1;
this.m_v1.Copy(this.m_v2);
return;
}
// Must be in e12 region.
var inv_d12 = 1 / (d12_1 + d12_2);
this.m_v1.a = d12_1 * inv_d12;
this.m_v2.a = d12_2 * inv_d12;
this.m_count = 2;
};
b2Simplex.prototype.Solve3 = function () {
var w1 = this.m_v1.w;
var w2 = this.m_v2.w;
var w3 = this.m_v3.w;
// Edge12
// [1 1 ][a1] = [1]
// [w1.e12 w2.e12][a2] = [0]
// a3 = 0
var e12 = b2Vec2.SubVV(w2, w1, b2Simplex.s_e12);
var w1e12 = b2Vec2.DotVV(w1, e12);
var w2e12 = b2Vec2.DotVV(w2, e12);
var d12_1 = w2e12;
var d12_2 = (-w1e12);
// Edge13
// [1 1 ][a1] = [1]
// [w1.e13 w3.e13][a3] = [0]
// a2 = 0
var e13 = b2Vec2.SubVV(w3, w1, b2Simplex.s_e13);
var w1e13 = b2Vec2.DotVV(w1, e13);
var w3e13 = b2Vec2.DotVV(w3, e13);
var d13_1 = w3e13;
var d13_2 = (-w1e13);
// Edge23
// [1 1 ][a2] = [1]
// [w2.e23 w3.e23][a3] = [0]
// a1 = 0
var e23 = b2Vec2.SubVV(w3, w2, b2Simplex.s_e23);
var w2e23 = b2Vec2.DotVV(w2, e23);
var w3e23 = b2Vec2.DotVV(w3, e23);
var d23_1 = w3e23;
var d23_2 = (-w2e23);
// Triangle123
var n123 = b2Vec2.CrossVV(e12, e13);
var d123_1 = n123 * b2Vec2.CrossVV(w2, w3);
var d123_2 = n123 * b2Vec2.CrossVV(w3, w1);
var d123_3 = n123 * b2Vec2.CrossVV(w1, w2);
// w1 region
if (d12_2 <= 0 && d13_2 <= 0) {
this.m_v1.a = 1;
this.m_count = 1;
return;
}
// e12
if (d12_1 > 0 && d12_2 > 0 && d123_3 <= 0) {
var inv_d12 = 1 / (d12_1 + d12_2);
this.m_v1.a = d12_1 * inv_d12;
this.m_v2.a = d12_2 * inv_d12;
this.m_count = 2;
return;
}
// e13
if (d13_1 > 0 && d13_2 > 0 && d123_2 <= 0) {
var inv_d13 = 1 / (d13_1 + d13_2);
this.m_v1.a = d13_1 * inv_d13;
this.m_v3.a = d13_2 * inv_d13;
this.m_count = 2;
this.m_v2.Copy(this.m_v3);
return;
}
// w2 region
if (d12_1 <= 0 && d23_2 <= 0) {
this.m_v2.a = 1;
this.m_count = 1;
this.m_v1.Copy(this.m_v2);
return;
}
// w3 region
if (d13_1 <= 0 && d23_1 <= 0) {
this.m_v3.a = 1;
this.m_count = 1;
this.m_v1.Copy(this.m_v3);
return;
}
// e23
if (d23_1 > 0 && d23_2 > 0 && d123_1 <= 0) {
var inv_d23 = 1 / (d23_1 + d23_2);
this.m_v2.a = d23_1 * inv_d23;
this.m_v3.a = d23_2 * inv_d23;
this.m_count = 2;
this.m_v1.Copy(this.m_v3);
return;
}
// Must be in triangle123
var inv_d123 = 1 / (d123_1 + d123_2 + d123_3);
this.m_v1.a = d123_1 * inv_d123;
this.m_v2.a = d123_2 * inv_d123;
this.m_v3.a = d123_3 * inv_d123;
this.m_count = 3;
};
b2Simplex.s_e12 = new b2Vec2();
b2Simplex.s_e13 = new b2Vec2();
b2Simplex.s_e23 = new b2Vec2();
return b2Simplex;
}());
var b2Distance_s_simplex = new b2Simplex();
var b2Distance_s_saveA = [0, 0, 0];
var b2Distance_s_saveB = [0, 0, 0];
var b2Distance_s_p = new b2Vec2();
var b2Distance_s_d = new b2Vec2();
var b2Distance_s_normal = new b2Vec2();
var b2Distance_s_supportA = new b2Vec2();
var b2Distance_s_supportB = new b2Vec2();
function b2Distance(output, cache, input) {
++exports.b2_gjkCalls;
var proxyA = input.proxyA;
var proxyB = input.proxyB;
var transformA = input.transformA;
var transformB = input.transformB;
// Initialize the simplex.
var simplex = b2Distance_s_simplex;
simplex.ReadCache(cache, proxyA, transformA, proxyB, transformB);
// Get simplex vertices as an array.
var vertices = simplex.m_vertices;
var k_maxIters = 20;
// These store the vertices of the last simplex so that we
// can check for duplicates and prevent cycling.
var saveA = b2Distance_s_saveA;
var saveB = b2Distance_s_saveB;
var saveCount = 0;
// Main iteration loop.
var iter = 0;
while (iter < k_maxIters) {
// Copy simplex so we can identify duplicates.
saveCount = simplex.m_count;
for (var i = 0; i < saveCount; ++i) {
saveA[i] = vertices[i].indexA;
saveB[i] = vertices[i].indexB;
}
switch (simplex.m_count) {
case 1:
break;
case 2:
simplex.Solve2();
break;
case 3:
simplex.Solve3();
break;
default:
// DEBUG: b2Assert(false);
break;
}
// If we have 3 points, then the origin is in the corresponding triangle.
if (simplex.m_count === 3) {
break;
}
// Get search direction.
var d = simplex.GetSearchDirection(b2Distance_s_d);
// Ensure the search direction is numerically fit.
if (d.LengthSquared() < b2_epsilon_sq) {
// The origin is probably contained by a line segment
// or triangle. Thus the shapes are overlapped.
// We can't return zero here even though there may be overlap.
// In case the simplex is a point, segment, or triangle it is difficult
// to determine if the origin is contained in the CSO or very close to it.
break;
}
// Compute a tentative new simplex vertex using support points.
var vertex = vertices[simplex.m_count];
vertex.indexA = proxyA.GetSupport(b2Rot.MulTRV(transformA.q, b2Vec2.NegV(d, b2Vec2.s_t0), b2Distance_s_supportA));
b2Transform.MulXV(transformA, proxyA.GetVertex(vertex.indexA), vertex.wA);
vertex.indexB = proxyB.GetSupport(b2Rot.MulTRV(transformB.q, d, b2Distance_s_supportB));
b2Transform.MulXV(transformB, proxyB.GetVertex(vertex.indexB), vertex.wB);
b2Vec2.SubVV(vertex.wB, vertex.wA, vertex.w);
// Iteration count is equated to the number of support point calls.
++iter;
++exports.b2_gjkIters;
// Check for duplicate support points. This is the main termination criteria.
var duplicate = false;
for (var i = 0; i < saveCount; ++i) {
if (vertex.indexA === saveA[i] && vertex.indexB === saveB[i]) {
duplicate = true;
break;
}
}
// If we found a duplicate support point we must exit to avoid cycling.
if (duplicate) {
break;
}
// New vertex is ok and needed.
++simplex.m_count;
}
exports.b2_gjkMaxIters = b2Max(exports.b2_gjkMaxIters, iter);
// Prepare output.
simplex.GetWitnessPoints(output.pointA, output.pointB);
output.distance = b2Vec2.DistanceVV(output.pointA, output.pointB);
output.iterations = iter;
// Cache the simplex.
simplex.WriteCache(cache);
// Apply radii if requested.
if (input.useRadii) {
var rA = proxyA.m_radius;
var rB = proxyB.m_radius;
if (output.distance > (rA + rB) && output.distance > b2_epsilon) {
// Shapes are still no overlapped.
// Move the witness points to the outer surface.
output.distance -= rA + rB;
var normal = b2Vec2.SubVV(output.pointB, output.pointA, b2Distance_s_normal);
normal.Normalize();
output.pointA.SelfMulAdd(rA, normal);
output.pointB.SelfMulSub(rB, normal);
}
else {
// Shapes are overlapped when radii are considered.
// Move the witness points to the middle.
var p = b2Vec2.MidVV(output.pointA, output.pointB, b2Distance_s_p);
output.pointA.Copy(p);
output.pointB.Copy(p);
output.distance = 0;
}
}
}
/// Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction.
// GJK-raycast
// Algorithm by Gino van den Bergen.
// "Smooth Mesh Contacts with GJK" in Game Physics Pearls. 2010
// bool b2ShapeCast(b2ShapeCastOutput* output, const b2ShapeCastInput* input);
var b2ShapeCast_s_n = new b2Vec2();
var b2ShapeCast_s_simplex = new b2Simplex();
var b2ShapeCast_s_wA = new b2Vec2();
var b2ShapeCast_s_wB = new b2Vec2();
var b2ShapeCast_s_v = new b2Vec2();
var b2ShapeCast_s_p = new b2Vec2();
var b2ShapeCast_s_pointA = new b2Vec2();
var b2ShapeCast_s_pointB = new b2Vec2();
function b2ShapeCast(output, input) {
output.iterations = 0;
output.lambda = 1.0;
output.normal.SetZero();
output.point.SetZero();
// const b2DistanceProxy* proxyA = &input.proxyA;
var proxyA = input.proxyA;
// const b2DistanceProxy* proxyB = &input.proxyB;
var proxyB = input.proxyB;
// float32 radiusA = b2Max(proxyA.m_radius, b2_polygonRadius);
var radiusA = b2Max(proxyA.m_radius, b2_polygonRadius);
// float32 radiusB = b2Max(proxyB.m_radius, b2_polygonRadius);
var radiusB = b2Max(proxyB.m_radius, b2_polygonRadius);
// float32 radius = radiusA + radiusB;
var radius = radiusA + radiusB;
// b2Transform xfA = input.transformA;
var xfA = input.transformA;
// b2Transform xfB = input.transformB;
var xfB = input.transformB;
// b2Vec2 r = input.translationB;
var r = input.translationB;
// b2Vec2 n(0.0f, 0.0f);
var n = b2ShapeCast_s_n.Set(0.0, 0.0);
// float32 lambda = 0.0f;
var lambda = 0.0;
// Initial simplex
var simplex = b2ShapeCast_s_simplex;
simplex.m_count = 0;
// Get simplex vertices as an array.
// b2SimplexVertex* vertices = &simplex.m_v1;
var vertices = simplex.m_vertices;
// Get support point in -r direction
// int32 indexA = proxyA.GetSupport(b2MulT(xfA.q, -r));
var indexA = proxyA.GetSupport(b2Rot.MulTRV(xfA.q, b2Vec2.NegV(r, b2Vec2.s_t1), b2Vec2.s_t0));
// b2Vec2 wA = b2Mul(xfA, proxyA.GetVertex(indexA));
var wA = b2Transform.MulXV(xfA, proxyA.GetVertex(indexA), b2ShapeCast_s_wA);
// int32 indexB = proxyB.GetSupport(b2MulT(xfB.q, r));
var indexB = proxyB.GetSupport(b2Rot.MulTRV(xfB.q, r, b2Vec2.s_t0));
// b2Vec2 wB = b2Mul(xfB, proxyB.GetVertex(indexB));
var wB = b2Transform.MulXV(xfB, proxyB.GetVertex(indexB), b2ShapeCast_s_wB);
// b2Vec2 v = wA - wB;
var v = b2Vec2.SubVV(wA, wB, b2ShapeCast_s_v);
// Sigma is the target distance between polygons
// float32 sigma = b2Max(b2_polygonRadius, radius - b2_polygonRadius);
var sigma = b2Max(b2_polygonRadius, radius - b2_polygonRadius);
// const float32 tolerance = 0.5f * b2_linearSlop;
var tolerance = 0.5 * b2_linearSlop;
// Main iteration loop.
// const int32 k_maxIters = 20;
var k_maxIters = 20;
// int32 iter = 0;
var iter = 0;
// while (iter < k_maxIters && b2Abs(v.Length() - sigma) > tolerance)
while (iter < k_maxIters && b2Abs(v.Length() - sigma) > tolerance) {
// DEBUG: b2Assert(simplex.m_count < 3);
output.iterations += 1;
// Support in direction -v (A - B)
// indexA = proxyA.GetSupport(b2MulT(xfA.q, -v));
indexA = proxyA.GetSupport(b2Rot.MulTRV(xfA.q, b2Vec2.NegV(v, b2Vec2.s_t1), b2Vec2.s_t0));
// wA = b2Mul(xfA, proxyA.GetVertex(indexA));
wA = b2Transform.MulXV(xfA, proxyA.GetVertex(indexA), b2ShapeCast_s_wA);
// indexB = proxyB.GetSupport(b2MulT(xfB.q, v));
indexB = proxyB.GetSupport(b2Rot.MulTRV(xfB.q, v, b2Vec2.s_t0));
// wB = b2Mul(xfB, proxyB.GetVertex(indexB));
wB = b2Transform.MulXV(xfB, proxyB.GetVertex(indexB), b2ShapeCast_s_wB);
// b2Vec2 p = wA - wB;
var p = b2Vec2.SubVV(wA, wB, b2ShapeCast_s_p);
// -v is a normal at p
v.Normalize();
// Intersect ray with plane
var vp = b2Vec2.DotVV(v, p);
var vr = b2Vec2.DotVV(v, r);
if (vp - sigma > lambda * vr) {
if (vr <= 0.0) {
return false;
}
lambda = (vp - sigma) / vr;
if (lambda > 1.0) {
return false;
}
// n = -v;
n.Copy(v).SelfNeg();
simplex.m_count = 0;
}
// Reverse simplex since it works with B - A.
// Shift by lambda * r because we want the closest point to the current clip point.
// Note that the support point p is not shifted because we want the plane equation
// to be formed in unshifted space.
// b2SimplexVertex* vertex = vertices + simplex.m_count;
var vertex = vertices[simplex.m_count];
vertex.indexA = indexB;
// vertex.wA = wB + lambda * r;
vertex.wA.Copy(wB).SelfMulAdd(lambda, r);
vertex.indexB = indexA;
// vertex.wB = wA;
vertex.wB.Copy(wA);
// vertex.w = vertex.wB - vertex.wA;
vertex.w.Copy(vertex.wB).SelfSub(vertex.wA);
vertex.a = 1.0;
simplex.m_count += 1;
switch (simplex.m_count) {
case 1:
break;
case 2:
simplex.Solve2();
break;
case 3:
simplex.Solve3();
break;
default:
// DEBUG: b2Assert(false);
}
// If we have 3 points, then the origin is in the corresponding triangle.
if (simplex.m_count === 3) {
// Overlap
return false;
}
// Get search direction.
// v = simplex.GetClosestPoint();
simplex.GetClosestPoint(v);
// Iteration count is equated to the number of support point calls.
++iter;
}
// Prepare output.
var pointA = b2ShapeCast_s_pointA;
var pointB = b2ShapeCast_s_pointB;
simplex.GetWitnessPoints(pointA, pointB);
if (v.LengthSquared() > 0.0) {
// n = -v;
n.Copy(v).SelfNeg();
n.Normalize();
}
// output.point = pointA + radiusA * n;
output.normal.Copy(n);
output.lambda = lambda;
output.iterations = iter;
return true;
}
/*
* Copyright (c) 2006-2009 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 (b2ContactFeatureType) {
b2ContactFeatureType[b2ContactFeatureType["e_vertex"] = 0] = "e_vertex";
b2ContactFeatureType[b2ContactFeatureType["e_face"] = 1] = "e_face";
})(exports.b2ContactFeatureType || (exports.b2ContactFeatureType = {}));
/// The features that intersect to form the contact point
/// This must be 4 bytes or less.
var b2ContactFeature = /** @class */ (function () {
function b2ContactFeature() {
this._key = 0;
this._key_invalid = false;
this._indexA = 0;
this._indexB = 0;
this._typeA = 0;
this._typeB = 0;
}
Object.defineProperty(b2ContactFeature.prototype, "key", {
get: function () {
if (this._key_invalid) {
this._key_invalid = false;
this._key = this._indexA | (this._indexB << 8) | (this._typeA << 16) | (this._typeB << 24);
}
return this._key;
},
set: function (value) {
this._key = value;
this._key_invalid = false;
this._indexA = this._key & 0xff;
this._indexB = (this._key >> 8) & 0xff;
this._typeA = (this._key >> 16) & 0xff;
this._typeB = (this._key >> 24) & 0xff;
},
enumerable: true,
configurable: true
});
Object.defineProperty(b2ContactFeature.prototype, "indexA", {
get: function () {
return this._indexA;
},
set: function (value) {
this._indexA = value;
this._key_invalid = true;
},
enumerable: true,
configurable: true
});
Object.defineProperty(b2ContactFeature.prototype, "indexB", {
get: function () {
return this._indexB;
},
set: function (value) {
this._indexB = value;
this._key_invalid = true;
},
enumerable: true,
configurable: true
});
Object.defineProperty(b2ContactFeature.prototype, "typeA", {
get: function () {
return this._typeA;
},
set: function (value) {
this._typeA = value;
this._key_invalid = true;
},
enumerable: true,
configurable: true
});
Object.defineProperty(b2ContactFeature.prototype, "typeB", {
get: function () {
return this._typeB;
},
set: function (value) {
this._typeB = value;
this._key_invalid = true;
},
enumerable: true,
configurable: true
});
return b2ContactFeature;
}());
/// Contact ids to facilitate warm starting.
var b2ContactID = /** @class */ (function () {
function b2ContactID() {
this.cf = new b2ContactFeature();
}
b2ContactID.prototype.Copy = function (o) {
this.key = o.key;
return this;
};
b2ContactID.prototype.Clone = function () {
return new b2ContactID().Copy(this);
};
Object.defineProperty(b2ContactID.prototype, "key", {
get: function () {
return this.cf.key;
},
set: function (value) {
this.cf.key = value;
},
enumerable: true,
configurable: true
});
return b2ContactID;
}());
/// A manifold point is a contact point belonging to a contact
/// manifold. It holds details related to the geometry and dynamics
/// of the contact points.
/// The local point usage depends on the manifold type:
/// -e_circles: the local center of circleB
/// -e_faceA: the local center of cirlceB or the clip point of polygonB
/// -e_faceB: the clip point of polygonA
/// This structure is stored across time steps, so we keep it small.
/// Note: the impulses are used for internal caching and may not
/// provide reliable contact forces, especially for high speed collisions.
var b2ManifoldPoint = /** @class */ (function () {
function b2ManifoldPoint() {
this.localPoint = new b2Vec2(); ///< usage depends on manifold type
this.normalImpulse = 0; ///< the non-penetration impulse
this.tangentImpulse = 0; ///< the friction impulse
this.id = new b2ContactID(); // TODO: readonly ///< uniquely identifies a contact point between two shapes
}
b2ManifoldPoint.MakeArray = function (length) {
return b2MakeArray(length, function (i) { return new b2ManifoldPoint(); });
};
b2ManifoldPoint.prototype.Reset = function () {
this.localPoint.SetZero();
this.normalImpulse = 0;
this.tangentImpulse = 0;
this.id.key = 0;
};
b2ManifoldPoint.prototype.Copy = function (o) {
this.localPoint.Copy(o.localPoint);
this.normalImpulse = o.normalImpulse;
this.tangentImpulse = o.tangentImpulse;
this.id.Copy(o.id);
return this;
};
return b2ManifoldPoint;
}());
(function (b2ManifoldType) {
b2ManifoldType[b2ManifoldType["e_unknown"] = -1] = "e_unknown";
b2ManifoldType[b2ManifoldType["e_circles"] = 0] = "e_circles";
b2ManifoldType[b2ManifoldType["e_faceA"] = 1] = "e_faceA";
b2ManifoldType[b2ManifoldType["e_faceB"] = 2] = "e_faceB";
})(exports.b2ManifoldType || (exports.b2ManifoldType = {}));
/// A manifold for two touching convex shapes.
/// Box2D supports multiple types of contact:
/// - clip point versus plane with radius
/// - point versus point with radius (circles)
/// The local point usage depends on the manifold type:
/// -e_circles: the local center of circleA
/// -e_faceA: the center of faceA
/// -e_faceB: the center of faceB
/// Similarly the local normal usage:
/// -e_circles: not used
/// -e_faceA: the normal on polygonA
/// -e_faceB: the normal on polygonB
/// We store contacts in this way so that position correction can
/// account for movement, which is critical for continuous physics.
/// All contact scenarios must be expressed in one of these types.
/// This structure is stored across time steps, so we keep it small.
var b2Manifold = /** @class */ (function () {
function b2Manifold() {
this.points = b2ManifoldPoint.MakeArray(b2_maxManifoldPoints);
this.localNormal = new b2Vec2();
this.localPoint = new b2Vec2();
this.type = exports.b2ManifoldType.e_unknown;
this.pointCount = 0;
}
b2Manifold.prototype.Reset = function () {
for (var i = 0; i < b2_maxManifoldPoints; ++i) {
// DEBUG: b2Assert(this.points[i] instanceof b2ManifoldPoint);
this.points[i].Reset();
}
this.localNormal.SetZero();
this.localPoint.SetZero();
this.type = exports.b2ManifoldType.e_unknown;
this.pointCount = 0;
};
b2Manifold.prototype.Copy = function (o) {
this.pointCount = o.pointCount;
for (var i = 0; i < b2_maxManifoldPoints; ++i) {
// DEBUG: b2Assert(this.points[i] instanceof b2ManifoldPoint);
this.points[i].Copy(o.points[i]);
}
this.localNormal.Copy(o.localNormal);
this.localPoint.Copy(o.localPoint);
this.type = o.type;
return this;
};
b2Manifold.prototype.Clone = function () {
return new b2Manifold().Copy(this);
};
return b2Manifold;
}());
var b2WorldManifold = /** @class */ (function () {
function b2WorldManifold() {
this.normal = new b2Vec2();
this.points = b2Vec2.MakeArray(b2_maxManifoldPoints);
this.separations = b2MakeNumberArray(b2_maxManifoldPoints);
}
b2WorldManifold.prototype.Initialize = function (manifold, xfA, radiusA, xfB, radiusB) {
if (manifold.pointCount === 0) {
return;
}
switch (manifold.type) {
case exports.b2ManifoldType.e_circles: {
this.normal.Set(1, 0);
var pointA = b2Transform.MulXV(xfA, manifold.localPoint, b2WorldManifold.Initialize_s_pointA);
var pointB = b2Transform.MulXV(xfB, manifold.points[0].localPoint, b2WorldManifold.Initialize_s_pointB);
if (b2Vec2.DistanceSquaredVV(pointA, pointB) > b2_epsilon_sq) {
b2Vec2.SubVV(pointB, pointA, this.normal).SelfNormalize();
}
var cA = b2Vec2.AddVMulSV(pointA, radiusA, this.normal, b2WorldManifold.Initialize_s_cA);
var cB = b2Vec2.SubVMulSV(pointB, radiusB, this.normal, b2WorldManifold.Initialize_s_cB);
b2Vec2.MidVV(cA, cB, this.points[0]);
this.separations[0] = b2Vec2.DotVV(b2Vec2.SubVV(cB, cA, b2Vec2.s_t0), this.normal); // b2Dot(cB - cA, normal);
break;
}
case exports.b2ManifoldType.e_faceA: {
b2Rot.MulRV(xfA.q, manifold.localNormal, this.normal);
var planePoint = b2Transform.MulXV(xfA, manifold.localPoint, b2WorldManifold.Initialize_s_planePoint);
for (var i = 0; i < manifold.pointCount; ++i) {
var clipPoint = b2Transform.MulXV(xfB, manifold.points[i].localPoint, b2WorldManifold.Initialize_s_clipPoint);
var s = radiusA - b2Vec2.DotVV(b2Vec2.SubVV(clipPoint, planePoint, b2Vec2.s_t0), this.normal);
var cA = b2Vec2.AddVMulSV(clipPoint, s, this.normal, b2WorldManifold.Initialize_s_cA);
var cB = b2Vec2.SubVMulSV(clipPoint, radiusB, this.normal, b2WorldManifold.Initialize_s_cB);
b2Vec2.MidVV(cA, cB, this.points[i]);
this.separations[i] = b2Vec2.DotVV(b2Vec2.SubVV(cB, cA, b2Vec2.s_t0), this.normal); // b2Dot(cB - cA, normal);
}
break;
}
case exports.b2ManifoldType.e_faceB: {
b2Rot.MulRV(xfB.q, manifold.localNormal, this.normal);
var planePoint = b2Transform.MulXV(xfB, manifold.localPoint, b2WorldManifold.Initialize_s_planePoint);
for (var i = 0; i < manifold.pointCount; ++i) {
var clipPoint = b2Transform.MulXV(xfA, manifold.points[i].localPoint, b2WorldManifold.Initialize_s_clipPoint);
var s = radiusB - b2Vec2.DotVV(b2Vec2.SubVV(clipPoint, planePoint, b2Vec2.s_t0), this.normal);
var cB = b2Vec2.AddVMulSV(clipPoint, s, this.normal, b2WorldManifold.Initialize_s_cB);
var cA = b2Vec2.SubVMulSV(clipPoint, radiusA, this.normal, b2WorldManifold.Initialize_s_cA);
b2Vec2.MidVV(cA, cB, this.points[i]);
this.separations[i] = b2Vec2.DotVV(b2Vec2.SubVV(cA, cB, b2Vec2.s_t0), this.normal); // b2Dot(cA - cB, normal);
}
// Ensure normal points from A to B.
this.normal.SelfNeg();
break;
}
}
};
b2WorldManifold.Initialize_s_pointA = new b2Vec2();
b2WorldManifold.Initialize_s_pointB = new b2Vec2();
b2WorldManifold.Initialize_s_cA = new b2Vec2();
b2WorldManifold.Initialize_s_cB = new b2Vec2();
b2WorldManifold.Initialize_s_planePoint = new b2Vec2();
b2WorldManifold.Initialize_s_clipPoint = new b2Vec2();
return b2WorldManifold;
}());
(function (b2PointState) {
b2PointState[b2PointState["b2_nullState"] = 0] = "b2_nullState";
b2PointState[b2PointState["b2_addState"] = 1] = "b2_addState";
b2PointState[b2PointState["b2_persistState"] = 2] = "b2_persistState";
b2PointState[b2PointState["b2_removeState"] = 3] = "b2_removeState";
})(exports.b2PointState || (exports.b2PointState = {}));
/// Compute the point states given two manifolds. The states pertain to the transition from manifold1
/// to manifold2. So state1 is either persist or remove while state2 is either add or persist.
function b2GetPointStates(state1, state2, manifold1, manifold2) {
// Detect persists and removes.
var i;
for (i = 0; i < manifold1.pointCount; ++i) {
var id = manifold1.points[i].id;
var key = id.key;
state1[i] = exports.b2PointState.b2_removeState;
for (var j = 0, jct = manifold2.pointCount; j < jct; ++j) {
if (manifold2.points[j].id.key === key) {
state1[i] = exports.b2PointState.b2_persistState;
break;
}
}
}
for (; i < b2_maxManifoldPoints; ++i) {
state1[i] = exports.b2PointState.b2_nullState;
}
// Detect persists and adds.
for (i = 0; i < manifold2.pointCount; ++i) {
var id = manifold2.points[i].id;
var key = id.key;
state2[i] = exports.b2PointState.b2_addState;
for (var j = 0, jct = manifold1.pointCount; j < jct; ++j) {
if (manifold1.points[j].id.key === key) {
state2[i] = exports.b2PointState.b2_persistState;
break;
}
}
}
for (; i < b2_maxManifoldPoints; ++i) {
state2[i] = exports.b2PointState.b2_nullState;
}
}
/// Used for computing contact manifolds.
var b2ClipVertex = /** @class */ (function () {
function b2ClipVertex() {
this.v = new b2Vec2();
this.id = new b2ContactID();
}
b2ClipVertex.MakeArray = function (length) {
return b2MakeArray(length, function (i) { return new b2ClipVertex(); });
};
b2ClipVertex.prototype.Copy = function (other) {
this.v.Copy(other.v);
this.id.Copy(other.id);
return this;
};
return b2ClipVertex;
}());
/// Ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
var b2RayCastInput = /** @class */ (function () {
function b2RayCastInput() {
this.p1 = new b2Vec2();
this.p2 = new b2Vec2();
this.maxFraction = 1;
}
b2RayCastInput.prototype.Copy = function (o) {
this.p1.Copy(o.p1);
this.p2.Copy(o.p2);
this.maxFraction = o.maxFraction;
return this;
};
return b2RayCastInput;
}());
/// Ray-cast output data. The ray hits at p1 + fraction * (p2 - p1), where p1 and p2
/// come from b2RayCastInput.
var b2RayCastOutput = /** @class */ (function () {
function b2RayCastOutput() {
this.normal = new b2Vec2();
this.fraction = 0;
}
b2RayCastOutput.prototype.Copy = function (o) {
this.normal.Copy(o.normal);
this.fraction = o.fraction;
return this;
};
return b2RayCastOutput;
}());
/// An axis aligned bounding box.
var b2AABB = /** @class */ (function () {
function b2AABB() {
this.lowerBound = new b2Vec2(); ///< the lower vertex
this.upperBound = new b2Vec2(); ///< the upper vertex
this.m_cache_center = new b2Vec2(); // access using GetCenter()
this.m_cache_extent = new b2Vec2(); // access using GetExtents()
}
b2AABB.prototype.Copy = function (o) {
this.lowerBound.Copy(o.lowerBound);
this.upperBound.Copy(o.upperBound);
return this;
};
/// Verify that the bounds are sorted.
b2AABB.prototype.IsValid = function () {
var d_x = this.upperBound.x - this.lowerBound.x;
var d_y = this.upperBound.y - this.lowerBound.y;
var valid = d_x >= 0 && d_y >= 0;
valid = valid && this.lowerBound.IsValid() && this.upperBound.IsValid();
return valid;
};
/// Get the center of the AABB.
b2AABB.prototype.GetCenter = function () {
return b2Vec2.MidVV(this.lowerBound, this.upperBound, this.m_cache_center);
};
/// Get the extents of the AABB (half-widths).
b2AABB.prototype.GetExtents = function () {
return b2Vec2.ExtVV(this.lowerBound, this.upperBound, this.m_cache_extent);
};
/// Get the perimeter length
b2AABB.prototype.GetPerimeter = function () {
var wx = this.upperBound.x - this.lowerBound.x;
var wy = this.upperBound.y - this.lowerBound.y;
return 2 * (wx + wy);
};
/// Combine an AABB into this one.
b2AABB.prototype.Combine1 = function (aabb) {
this.lowerBound.x = b2Min(this.lowerBound.x, aabb.lowerBound.x);
this.lowerBound.y = b2Min(this.lowerBound.y, aabb.lowerBound.y);
this.upperBound.x = b2Max(this.upperBound.x, aabb.upperBound.x);
this.upperBound.y = b2Max(this.upperBound.y, aabb.upperBound.y);
return this;
};
/// Combine two AABBs into this one.
b2AABB.prototype.Combine2 = function (aabb1, aabb2) {
this.lowerBound.x = b2Min(aabb1.lowerBound.x, aabb2.lowerBound.x);
this.lowerBound.y = b2Min(aabb1.lowerBound.y, aabb2.lowerBound.y);
this.upperBound.x = b2Max(aabb1.upperBound.x, aabb2.upperBound.x);
this.upperBound.y = b2Max(aabb1.upperBound.y, aabb2.upperBound.y);
return this;
};
b2AABB.Combine = function (aabb1, aabb2, out) {
out.Combine2(aabb1, aabb2);
return out;
};
/// Does this aabb contain the provided AABB.
b2AABB.prototype.Contains = function (aabb) {
var result = true;
result = result && this.lowerBound.x <= aabb.lowerBound.x;
result = result && this.lowerBound.y <= aabb.lowerBound.y;
result = result && aabb.upperBound.x <= this.upperBound.x;
result = result && aabb.upperBound.y <= this.upperBound.y;
return result;
};
// From Real-time Collision Detection, p179.
b2AABB.prototype.RayCast = function (output, input) {
var tmin = (-b2_maxFloat);
var tmax = b2_maxFloat;
var p_x = input.p1.x;
var p_y = input.p1.y;
var d_x = input.p2.x - input.p1.x;
var d_y = input.p2.y - input.p1.y;
var absD_x = b2Abs(d_x);
var absD_y = b2Abs(d_y);
var normal = output.normal;
if (absD_x < b2_epsilon) {
// Parallel.
if (p_x < this.lowerBound.x || this.upperBound.x < p_x) {
return false;
}
}
else {
var inv_d = 1 / d_x;
var t1 = (this.lowerBound.x - p_x) * inv_d;
var t2 = (this.upperBound.x - p_x) * inv_d;
// Sign of the normal vector.
var s = (-1);
if (t1 > t2) {
var t3 = t1;
t1 = t2;
t2 = t3;
s = 1;
}
// Push the min up
if (t1 > tmin) {
normal.x = s;
normal.y = 0;
tmin = t1;
}
// Pull the max down
tmax = b2Min(tmax, t2);
if (tmin > tmax) {
return false;
}
}
if (absD_y < b2_epsilon) {
// Parallel.
if (p_y < this.lowerBound.y || this.upperBound.y < p_y) {
return false;
}
}
else {
var inv_d = 1 / d_y;
var t1 = (this.lowerBound.y - p_y) * inv_d;
var t2 = (this.upperBound.y - p_y) * inv_d;
// Sign of the normal vector.
var s = (-1);
if (t1 > t2) {
var t3 = t1;
t1 = t2;
t2 = t3;
s = 1;
}
// Push the min up
if (t1 > tmin) {
normal.x = 0;
normal.y = s;
tmin = t1;
}
// Pull the max down
tmax = b2Min(tmax, t2);
if (tmin > tmax) {
return false;
}
}
// Does the ray start inside the box?
// Does the ray intersect beyond the max fraction?
if (tmin < 0 || input.maxFraction < tmin) {
return false;
}
// Intersection.
output.fraction = tmin;
return true;
};
b2AABB.prototype.TestContain = function (point) {
if (point.x < this.lowerBound.x || this.upperBound.x < point.x) {
return false;
}
if (point.y < this.lowerBound.y || this.upperBound.y < point.y) {
return false;
}
return true;
};
b2AABB.prototype.TestOverlap = function (other) {
var d1_x = other.lowerBound.x - this.upperBound.x;
var d1_y = other.lowerBound.y - this.upperBound.y;
var d2_x = this.lowerBound.x - other.upperBound.x;
var d2_y = this.lowerBound.y - other.upperBound.y;
if (d1_x > 0 || d1_y > 0) {
return false;
}
if (d2_x > 0 || d2_y > 0) {
return false;
}
return true;
};
return b2AABB;
}());
function b2TestOverlapAABB(a, b) {
var d1_x = b.lowerBound.x - a.upperBound.x;
var d1_y = b.lowerBound.y - a.upperBound.y;
var d2_x = a.lowerBound.x - b.upperBound.x;
var d2_y = a.lowerBound.y - b.upperBound.y;
if (d1_x > 0 || d1_y > 0) {
return false;
}
if (d2_x > 0 || d2_y > 0) {
return false;
}
return true;
}
/// Clipping for contact manifolds.
function b2ClipSegmentToLine(vOut, vIn, normal, offset, vertexIndexA) {
// Start with no output points
var numOut = 0;
var vIn0 = vIn[0];
var vIn1 = vIn[1];
// Calculate the distance of end points to the line
var distance0 = b2Vec2.DotVV(normal, vIn0.v) - offset;
var distance1 = b2Vec2.DotVV(normal, vIn1.v) - offset;
// If the points are behind the plane
if (distance0 <= 0) {
vOut[numOut++].Copy(vIn0);
}
if (distance1 <= 0) {
vOut[numOut++].Copy(vIn1);
}
// If the points are on different sides of the plane
if (distance0 * distance1 < 0) {
// Find intersection point of edge and plane
var interp = distance0 / (distance0 - distance1);
var v = vOut[numOut].v;
v.x = vIn0.v.x + interp * (vIn1.v.x - vIn0.v.x);
v.y = vIn0.v.y + interp * (vIn1.v.y - vIn0.v.y);
// VertexA is hitting edgeB.
var id = vOut[numOut].id;
id.cf.indexA = vertexIndexA;
id.cf.indexB = vIn0.id.cf.indexB;
id.cf.typeA = exports.b2ContactFeatureType.e_vertex;
id.cf.typeB = exports.b2ContactFeatureType.e_face;
++numOut;
}
return numOut;
}
/// Determine if two generic shapes overlap.
var b2TestOverlapShape_s_input = new b2DistanceInput();
var b2TestOverlapShape_s_simplexCache = new b2SimplexCache();
var b2TestOverlapShape_s_output = new b2DistanceOutput();
function b2TestOverlapShape(shapeA, indexA, shapeB, indexB, xfA, xfB) {
var input = b2TestOverlapShape_s_input.Reset();
input.proxyA.SetShape(shapeA, indexA);
input.proxyB.SetShape(shapeB, indexB);
input.transformA.Copy(xfA);
input.transformB.Copy(xfB);
input.useRadii = true;
var simplexCache = b2TestOverlapShape_s_simplexCache.Reset();
simplexCache.count = 0;
var output = b2TestOverlapShape_s_output.Reset();
b2Distance(output, simplexCache, input);
return output.distance < 10 * b2_epsilon;
}
/*
* Copyright (c) 2009 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 verify(value) {
if (value === null) {
throw new Error();
}
return value;
}
/// A node in the dynamic tree. The client does not interact with this directly.
var b2TreeNode = /** @class */ (function () {
function b2TreeNode(id) {
if (id === void 0) { id = 0; }
this.m_id = 0;
this.aabb = new b2AABB();
this.parent = null; // or next
this.child1 = null;
this.child2 = null;
this.height = 0; // leaf = 0, free node = -1
this.m_id = id;
}
b2TreeNode.prototype.IsLeaf = function () {
return this.child1 === null;
};
return b2TreeNode;
}());
var b2DynamicTree = /** @class */ (function () {
function b2DynamicTree() {
this.m_root = null;
// b2TreeNode* public m_nodes;
// int32 public m_nodeCount;
// int32 public m_nodeCapacity;
this.m_freeList = null;
this.m_path = 0;
this.m_insertionCount = 0;
this.m_stack = new b2GrowableStack(256);
}
// public GetUserData(proxy: b2TreeNode<T>): any {
// // DEBUG: b2Assert(proxy !== null);
// return proxy.userData;
// }
// public GetFatAABB(proxy: b2TreeNode<T>): b2AABB {
// // DEBUG: b2Assert(proxy !== null);
// return proxy.aabb;
// }
b2DynamicTree.prototype.Query = function (aabb, callback) {
if (this.m_root === null) {
return;
}
var stack = this.m_stack.Reset();
stack.Push(this.m_root);
while (stack.GetCount() > 0) {
var node = stack.Pop();
// if (node === null) {
// continue;
// }
if (node.aabb.TestOverlap(aabb)) {
if (node.IsLeaf()) {
var proceed = callback(node);
if (!proceed) {
return;
}
}
else {
stack.Push(verify(node.child1));
stack.Push(verify(node.child2));
}
}
}
};
b2DynamicTree.prototype.QueryPoint = function (point, callback) {
if (this.m_root === null) {
return;
}
var stack = this.m_stack.Reset();
stack.Push(this.m_root);
while (stack.GetCount() > 0) {
var node = stack.Pop();
// if (node === null) {
// continue;
// }
if (node.aabb.TestContain(point)) {
if (node.IsLeaf()) {
var proceed = callback(node);
if (!proceed) {
return;
}
}
else {
stack.Push(verify(node.child1));
stack.Push(verify(node.child2));
}
}
}
};
b2DynamicTree.prototype.RayCast = function (input, callback) {
if (this.m_root === null) {
return;
}
var p1 = input.p1;
var p2 = input.p2;
var r = b2Vec2.SubVV(p2, p1, b2DynamicTree.s_r);
// DEBUG: b2Assert(r.LengthSquared() > 0);
r.Normalize();
// v is perpendicular to the segment.
var v = b2Vec2.CrossOneV(r, b2DynamicTree.s_v);
var abs_v = b2Vec2.AbsV(v, b2DynamicTree.s_abs_v);
// Separating axis for segment (Gino, p80).
// |dot(v, p1 - c)| > dot(|v|, h)
var maxFraction = input.maxFraction;
// Build a bounding box for the segment.
var segmentAABB = b2DynamicTree.s_segmentAABB;
var t_x = p1.x + maxFraction * (p2.x - p1.x);
var t_y = p1.y + maxFraction * (p2.y - p1.y);
segmentAABB.lowerBound.x = b2Min(p1.x, t_x);
segmentAABB.lowerBound.y = b2Min(p1.y, t_y);
segmentAABB.upperBound.x = b2Max(p1.x, t_x);
segmentAABB.upperBound.y = b2Max(p1.y, t_y);
var stack = this.m_stack.Reset();
stack.Push(this.m_root);
while (stack.GetCount() > 0) {
var node = stack.Pop();
// if (node === null) {
// continue;
// }
if (!b2TestOverlapAABB(node.aabb, segmentAABB)) {
continue;
}
// Separating axis for segment (Gino, p80).
// |dot(v, p1 - c)| > dot(|v|, h)
var c = node.aabb.GetCenter();
var h = node.aabb.GetExtents();
var separation = b2Abs(b2Vec2.DotVV(v, b2Vec2.SubVV(p1, c, b2Vec2.s_t0))) - b2Vec2.DotVV(abs_v, h);
if (separation > 0) {
continue;
}
if (node.IsLeaf()) {
var subInput = b2DynamicTree.s_subInput;
subInput.p1.Copy(input.p1);
subInput.p2.Copy(input.p2);
subInput.maxFraction = maxFraction;
var value = callback(subInput, node);
if (value === 0) {
// The client has terminated the ray cast.
return;
}
if (value > 0) {
// Update segment bounding box.
maxFraction = value;
t_x = p1.x + maxFraction * (p2.x - p1.x);
t_y = p1.y + maxFraction * (p2.y - p1.y);
segmentAABB.lowerBound.x = b2Min(p1.x, t_x);
segmentAABB.lowerBound.y = b2Min(p1.y, t_y);
segmentAABB.upperBound.x = b2Max(p1.x, t_x);
segmentAABB.upperBound.y = b2Max(p1.y, t_y);
}
}
else {
stack.Push(verify(node.child1));
stack.Push(verify(node.child2));
}
}
};
b2DynamicTree.prototype.AllocateNode = function () {
// Expand the node pool as needed.
if (this.m_freeList) {
var node = this.m_freeList;
this.m_freeList = node.parent; // this.m_freeList = node.next;
node.parent = null;
node.child1 = null;
node.child2 = null;
node.height = 0;
delete node.userData; // = null;
return node;
}
return new b2TreeNode(b2DynamicTree.s_node_id++);
};
b2DynamicTree.prototype.FreeNode = function (node) {
node.parent = this.m_freeList; // node.next = this.m_freeList;
node.child1 = null;
node.child2 = null;
node.height = -1;
delete node.userData; // = null;
this.m_freeList = node;
};
b2DynamicTree.prototype.CreateProxy = function (aabb, userData) {
var node = this.AllocateNode();
// Fatten the aabb.
var r_x = b2_aabbExtension;
var r_y = b2_aabbExtension;
node.aabb.lowerBound.x = aabb.lowerBound.x - r_x;
node.aabb.lowerBound.y = aabb.lowerBound.y - r_y;
node.aabb.upperBound.x = aabb.upperBound.x + r_x;
node.aabb.upperBound.y = aabb.upperBound.y + r_y;
node.userData = userData;
node.height = 0;
this.InsertLeaf(node);
return node;
};
b2DynamicTree.prototype.DestroyProxy = function (proxy) {
// DEBUG: b2Assert(proxy.IsLeaf());
this.RemoveLeaf(proxy);
this.FreeNode(proxy);
};
b2DynamicTree.prototype.MoveProxy = function (proxy, aabb, displacement) {
// DEBUG: b2Assert(proxy.IsLeaf());
if (proxy.aabb.Contains(aabb)) {
return false;
}
this.RemoveLeaf(proxy);
// Extend AABB.
// Predict AABB displacement.
var r_x = b2_aabbExtension + b2_aabbMultiplier * (displacement.x > 0 ? displacement.x : (-displacement.x));
var r_y = b2_aabbExtension + b2_aabbMultiplier * (displacement.y > 0 ? displacement.y : (-displacement.y));
proxy.aabb.lowerBound.x = aabb.lowerBound.x - r_x;
proxy.aabb.lowerBound.y = aabb.lowerBound.y - r_y;
proxy.aabb.upperBound.x = aabb.upperBound.x + r_x;
proxy.aabb.upperBound.y = aabb.upperBound.y + r_y;
this.InsertLeaf(proxy);
return true;
};
b2DynamicTree.prototype.InsertLeaf = function (leaf) {
++this.m_insertionCount;
if (this.m_root === null) {
this.m_root = leaf;
this.m_root.parent = null;
return;
}
// Find the best sibling for this node
var leafAABB = leaf.aabb;
///const center: b2Vec2 = leafAABB.GetCenter();
var index = this.m_root;
while (!index.IsLeaf()) {
var child1 = verify(index.child1);
var child2 = verify(index.child2);
var area = index.aabb.GetPerimeter();
var combinedAABB = b2DynamicTree.s_combinedAABB;
combinedAABB.Combine2(index.aabb, leafAABB);
var combinedArea = combinedAABB.GetPerimeter();
// Cost of creating a new parent for this node and the new leaf
var cost = 2 * combinedArea;
// Minimum cost of pushing the leaf further down the tree
var inheritanceCost = 2 * (combinedArea - area);
// Cost of descending into child1
var cost1 = void 0;
var aabb = b2DynamicTree.s_aabb;
var oldArea = void 0;
var newArea = void 0;
if (child1.IsLeaf()) {
aabb.Combine2(leafAABB, child1.aabb);
cost1 = aabb.GetPerimeter() + inheritanceCost;
}
else {
aabb.Combine2(leafAABB, child1.aabb);
oldArea = child1.aabb.GetPerimeter();
newArea = aabb.GetPerimeter();
cost1 = (newArea - oldArea) + inheritanceCost;
}
// Cost of descending into child2
var cost2 = void 0;
if (child2.IsLeaf()) {
aabb.Combine2(leafAABB, child2.aabb);
cost2 = aabb.GetPerimeter() + inheritanceCost;
}
else {
aabb.Combine2(leafAABB, child2.aabb);
oldArea = child2.aabb.GetPerimeter();
newArea = aabb.GetPerimeter();
cost2 = newArea - oldArea + inheritanceCost;
}
// Descend according to the minimum cost.
if (cost < cost1 && cost < cost2) {
break;
}
// Descend
if (cost1 < cost2) {
index = child1;
}
else {
index = child2;
}
}
var sibling = index;
// Create a parent for the siblings.
var oldParent = sibling.parent;
var newParent = this.AllocateNode();
newParent.parent = oldParent;
delete newParent.userData; // = null;
newParent.aabb.Combine2(leafAABB, sibling.aabb);
newParent.height = sibling.height + 1;
if (oldParent) {
// The sibling was not the root.
if (oldParent.child1 === sibling) {
oldParent.child1 = newParent;
}
else {
oldParent.child2 = newParent;
}
newParent.child1 = sibling;
newParent.child2 = leaf;
sibling.parent = newParent;
leaf.parent = newParent;
}
else {
// The sibling was the root.
newParent.child1 = sibling;
newParent.child2 = leaf;
sibling.parent = newParent;
leaf.parent = newParent;
this.m_root = newParent;
}
// Walk back up the tree fixing heights and AABBs
var index2 = leaf.parent;
while (index2 !== null) {
index2 = this.Balance(index2);
var child1 = verify(index2.child1);
var child2 = verify(index2.child2);
index2.height = 1 + b2Max(child1.height, child2.height);
index2.aabb.Combine2(child1.aabb, child2.aabb);
index2 = index2.parent;
}
// this.Validate();
};
b2DynamicTree.prototype.RemoveLeaf = function (leaf) {
if (leaf === this.m_root) {
this.m_root = null;
return;
}
var parent = verify(leaf.parent);
var grandParent = parent && parent.parent;
var sibling;
if (parent.child1 === leaf) {
sibling = verify(parent.child2);
}
else {
sibling = verify(parent.child1);
}
if (grandParent) {
// Destroy parent and connect sibling to grandParent.
if (grandParent.child1 === parent) {
grandParent.child1 = sibling;
}
else {
grandParent.child2 = sibling;
}
sibling.parent = grandParent;
this.FreeNode(parent);
// Adjust ancestor bounds.
var index = grandParent;
while (index) {
index = this.Balance(index);
var child1 = verify(index.child1);
var child2 = verify(index.child2);
index.aabb.Combine2(child1.aabb, child2.aabb);
index.height = 1 + b2Max(child1.height, child2.height);
index = index.parent;
}
}
else {
this.m_root = sibling;
sibling.parent = null;
this.FreeNode(parent);
}
// this.Validate();
};
b2DynamicTree.prototype.Balance = function (A) {
// DEBUG: b2Assert(A !== null);
if (A.IsLeaf() || A.height < 2) {
return A;
}
var B = verify(A.child1);
var C = verify(A.child2);
var balance = C.height - B.height;
// Rotate C up
if (balance > 1) {
var F = verify(C.child1);
var G = verify(C.child2);
// Swap A and C
C.child1 = A;
C.parent = A.parent;
A.parent = C;
// A's old parent should point to C
if (C.parent !== null) {
if (C.parent.child1 === A) {
C.parent.child1 = C;
}
else {
// DEBUG: b2Assert(C.parent.child2 === A);
C.parent.child2 = C;
}
}
else {
this.m_root = C;
}
// Rotate
if (F.height > G.height) {
C.child2 = F;
A.child2 = G;
G.parent = A;
A.aabb.Combine2(B.aabb, G.aabb);
C.aabb.Combine2(A.aabb, F.aabb);
A.height = 1 + b2Max(B.height, G.height);
C.height = 1 + b2Max(A.height, F.height);
}
else {
C.child2 = G;
A.child2 = F;
F.parent = A;
A.aabb.Combine2(B.aabb, F.aabb);
C.aabb.Combine2(A.aabb, G.aabb);
A.height = 1 + b2Max(B.height, F.height);
C.height = 1 + b2Max(A.height, G.height);
}
return C;
}
// Rotate B up
if (balance < -1) {
var D = verify(B.child1);
var E = verify(B.child2);
// Swap A and B
B.child1 = A;
B.parent = A.parent;
A.parent = B;
// A's old parent should point to B
if (B.parent !== null) {
if (B.parent.child1 === A) {
B.parent.child1 = B;
}
else {
// DEBUG: b2Assert(B.parent.child2 === A);
B.parent.child2 = B;
}
}
else {
this.m_root = B;
}
// Rotate
if (D.height > E.height) {
B.child2 = D;
A.child1 = E;
E.parent = A;
A.aabb.Combine2(C.aabb, E.aabb);
B.aabb.Combine2(A.aabb, D.aabb);
A.height = 1 + b2Max(C.height, E.height);
B.height = 1 + b2Max(A.height, D.height);
}
else {
B.child2 = E;
A.child1 = D;
D.parent = A;
A.aabb.Combine2(C.aabb, D.aabb);
B.aabb.Combine2(A.aabb, E.aabb);
A.height = 1 + b2Max(C.height, D.height);
B.height = 1 + b2Max(A.height, E.height);
}
return B;
}
return A;
};
b2DynamicTree.prototype.GetHeight = function () {
if (this.m_root === null) {
return 0;
}
return this.m_root.height;
};
b2DynamicTree.GetAreaNode = function (node) {
if (node === null) {
return 0;
}
if (node.IsLeaf()) {
return 0;
}
var area = node.aabb.GetPerimeter();
area += b2DynamicTree.GetAreaNode(node.child1);
area += b2DynamicTree.GetAreaNode(node.child2);
return area;
};
b2DynamicTree.prototype.GetAreaRatio = function () {
if (this.m_root === null) {
return 0;
}
var root = this.m_root;
var rootArea = root.aabb.GetPerimeter();
var totalArea = b2DynamicTree.GetAreaNode(this.m_root);
/*
float32 totalArea = 0.0;
for (int32 i = 0; i < m_nodeCapacity; ++i) {
const b2TreeNode<T>* node = m_nodes + i;
if (node.height < 0) {
// Free node in pool
continue;
}
totalArea += node.aabb.GetPerimeter();
}
*/
return totalArea / rootArea;
};
b2DynamicTree.prototype.ComputeHeightNode = function (node) {
if (!node || node.IsLeaf()) {
return 0;
}
var height1 = this.ComputeHeightNode(node.child1);
var height2 = this.ComputeHeightNode(node.child2);
return 1 + b2Max(height1, height2);
};
b2DynamicTree.prototype.ComputeHeight = function () {
var height = this.ComputeHeightNode(this.m_root);
return height;
};
b2DynamicTree.prototype.ValidateStructure = function (index) {
if (index === null) {
return;
}
if (index === this.m_root) ;
var node = index;
if (node.IsLeaf()) {
// DEBUG: b2Assert(node.child1 === null);
// DEBUG: b2Assert(node.child2 === null);
// DEBUG: b2Assert(node.height === 0);
return;
}
var child1 = verify(node.child1);
var child2 = verify(node.child2);
// DEBUG: b2Assert(child1.parent === index);
// DEBUG: b2Assert(child2.parent === index);
this.ValidateStructure(child1);
this.ValidateStructure(child2);
};
b2DynamicTree.prototype.ValidateMetrics = function (index) {
if (index === null) {
return;
}
var node = index;
if (node.IsLeaf()) {
// DEBUG: b2Assert(node.child1 === null);
// DEBUG: b2Assert(node.child2 === null);
// DEBUG: b2Assert(node.height === 0);
return;
}
var child1 = verify(node.child1);
var child2 = verify(node.child2);
// DEBUG: const height1: number = child1.height;
// DEBUG: const height2: number = child2.height;
// DEBUG: const height: number = 1 + b2Max(height1, height2);
// DEBUG: b2Assert(node.height === height);
var aabb = b2DynamicTree.s_aabb;
aabb.Combine2(child1.aabb, child2.aabb);
// DEBUG: b2Assert(aabb.lowerBound === node.aabb.lowerBound);
// DEBUG: b2Assert(aabb.upperBound === node.aabb.upperBound);
this.ValidateMetrics(child1);
this.ValidateMetrics(child2);
};
b2DynamicTree.prototype.Validate = function () {
// DEBUG: this.ValidateStructure(this.m_root);
// DEBUG: this.ValidateMetrics(this.m_root);
// let freeCount: number = 0;
// let freeIndex: b2TreeNode<T> | null = this.m_freeList;
// while (freeIndex !== null) {
// freeIndex = freeIndex.parent; // freeIndex = freeIndex.next;
// ++freeCount;
// }
// DEBUG: b2Assert(this.GetHeight() === this.ComputeHeight());
// b2Assert(this.m_nodeCount + freeCount === this.m_nodeCapacity);
};
b2DynamicTree.GetMaxBalanceNode = function (node, maxBalance) {
if (node === null) {
return maxBalance;
}
if (node.height <= 1) {
return maxBalance;
}
// DEBUG: b2Assert(!node.IsLeaf());
var child1 = verify(node.child1);
var child2 = verify(node.child2);
var balance = b2Abs(child2.height - child1.height);
return b2Max(maxBalance, balance);
};
b2DynamicTree.prototype.GetMaxBalance = function () {
var maxBalance = b2DynamicTree.GetMaxBalanceNode(this.m_root, 0);
/*
int32 maxBalance = 0;
for (int32 i = 0; i < m_nodeCapacity; ++i) {
const b2TreeNode<T>* node = m_nodes + i;
if (node.height <= 1) {
continue;
}
b2Assert(!node.IsLeaf());
int32 child1 = node.child1;
int32 child2 = node.child2;
int32 balance = b2Abs(m_nodes[child2].height - m_nodes[child1].height);
maxBalance = b2Max(maxBalance, balance);
}
*/
return maxBalance;
};
b2DynamicTree.prototype.RebuildBottomUp = function () {
/*
int32* nodes = (int32*)b2Alloc(m_nodeCount * sizeof(int32));
int32 count = 0;
// Build array of leaves. Free the rest.
for (int32 i = 0; i < m_nodeCapacity; ++i) {
if (m_nodes[i].height < 0) {
// free node in pool
continue;
}
if (m_nodes[i].IsLeaf()) {
m_nodes[i].parent = b2_nullNode;
nodes[count] = i;
++count;
} else {
FreeNode(i);
}
}
while (count > 1) {
float32 minCost = b2_maxFloat;
int32 iMin = -1, jMin = -1;
for (int32 i = 0; i < count; ++i) {
b2AABB aabbi = m_nodes[nodes[i]].aabb;
for (int32 j = i + 1; j < count; ++j) {
b2AABB aabbj = m_nodes[nodes[j]].aabb;
b2AABB b;
b.Combine(aabbi, aabbj);
float32 cost = b.GetPerimeter();
if (cost < minCost) {
iMin = i;
jMin = j;
minCost = cost;
}
}
}
int32 index1 = nodes[iMin];
int32 index2 = nodes[jMin];
b2TreeNode<T>* child1 = m_nodes + index1;
b2TreeNode<T>* child2 = m_nodes + index2;
int32 parentIndex = AllocateNode();
b2TreeNode<T>* parent = m_nodes + parentIndex;
parent.child1 = index1;
parent.child2 = index2;
parent.height = 1 + b2Max(child1.height, child2.height);
parent.aabb.Combine(child1.aabb, child2.aabb);
parent.parent = b2_nullNode;
child1.parent = parentIndex;
child2.parent = parentIndex;
nodes[jMin] = nodes[count-1];
nodes[iMin] = parentIndex;
--count;
}
m_root = nodes[0];
b2Free(nodes);
*/
this.Validate();
};
b2DynamicTree.ShiftOriginNode = function (node, newOrigin) {
if (node === null) {
return;
}
if (node.height <= 1) {
return;
}
// DEBUG: b2Assert(!node.IsLeaf());
var child1 = node.child1;
var child2 = node.child2;
b2DynamicTree.ShiftOriginNode(child1, newOrigin);
b2DynamicTree.ShiftOriginNode(child2, newOrigin);
node.aabb.lowerBound.SelfSub(newOrigin);
node.aabb.upperBound.SelfSub(newOrigin);
};
b2DynamicTree.prototype.ShiftOrigin = function (newOrigin) {
b2DynamicTree.ShiftOriginNode(this.m_root, newOrigin);
/*
// Build array of leaves. Free the rest.
for (int32 i = 0; i < m_nodeCapacity; ++i) {
m_nodes[i].aabb.lowerBound -= newOrigin;
m_nodes[i].aabb.upperBound -= newOrigin;
}
*/
};
b2DynamicTree.s_r = new b2Vec2();
b2DynamicTree.s_v = new b2Vec2();
b2DynamicTree.s_abs_v = new b2Vec2();
b2DynamicTree.s_segmentAABB = new b2AABB();
b2DynamicTree.s_subInput = new b2RayCastInput();
b2DynamicTree.s_combinedAABB = new b2AABB();
b2DynamicTree.s_aabb = new b2AABB();
b2DynamicTree.s_node_id = 0;
return b2DynamicTree;
}());
/*
* Copyright (c) 2006-2009 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.
*/
var b2Pair = /** @class */ (function () {
function b2Pair(proxyA, proxyB) {
this.proxyA = proxyA;
this.proxyB = proxyB;
}
return b2Pair;
}());
/// The broad-phase is used for computing pairs and performing volume queries and ray casts.
/// This broad-phase does not persist pairs. Instead, this reports potentially new pairs.
/// It is up to the client to consume the new pairs and to track subsequent overlap.
var b2BroadPhase = /** @class */ (function () {
function b2BroadPhase() {
this.m_tree = new b2DynamicTree();
this.m_proxyCount = 0;
// public m_moveCapacity: number = 16;
this.m_moveCount = 0;
this.m_moveBuffer = [];
// public m_pairCapacity: number = 16;
this.m_pairCount = 0;
this.m_pairBuffer = [];
}
// public m_queryProxyId: number = 0;
/// Create a proxy with an initial AABB. Pairs are not reported until
/// UpdatePairs is called.
b2BroadPhase.prototype.CreateProxy = function (aabb, userData) {
var proxy = this.m_tree.CreateProxy(aabb, userData);
++this.m_proxyCount;
this.BufferMove(proxy);
return proxy;
};
/// Destroy a proxy. It is up to the client to remove any pairs.
b2BroadPhase.prototype.DestroyProxy = function (proxy) {
this.UnBufferMove(proxy);
--this.m_proxyCount;
this.m_tree.DestroyProxy(proxy);
};
/// Call MoveProxy as many times as you like, then when you are done
/// call UpdatePairs to finalized the proxy pairs (for your time step).
b2BroadPhase.prototype.MoveProxy = function (proxy, aabb, displacement) {
var buffer = this.m_tree.MoveProxy(proxy, aabb, displacement);
if (buffer) {
this.BufferMove(proxy);
}
};
/// Call to trigger a re-processing of it's pairs on the next call to UpdatePairs.
b2BroadPhase.prototype.TouchProxy = function (proxy) {
this.BufferMove(proxy);
};
/// Get the fat AABB for a proxy.
// public GetFatAABB(proxy: b2TreeNode<T>): b2AABB {
// return this.m_tree.GetFatAABB(proxy);
// }
/// Get user data from a proxy. Returns NULL if the id is invalid.
// public GetUserData(proxy: b2TreeNode<T>): T {
// return this.m_tree.GetUserData(proxy);
// }
/// Test overlap of fat AABBs.
// public TestOverlap(proxyA: b2TreeNode<T>, proxyB: b2TreeNode<T>): boolean {
// const aabbA: b2AABB = this.m_tree.GetFatAABB(proxyA);
// const aabbB: b2AABB = this.m_tree.GetFatAABB(proxyB);
// return b2TestOverlapAABB(aabbA, aabbB);
// }
/// Get the number of proxies.
b2BroadPhase.prototype.GetProxyCount = function () {
return this.m_proxyCount;
};
/// Update the pairs. This results in pair callbacks. This can only add pairs.
b2BroadPhase.prototype.UpdatePairs = function (callback) {
var _this = this;
// Reset pair buffer
this.m_pairCount = 0;
var _loop_1 = function (i_1) {
var queryProxy = this_1.m_moveBuffer[i_1];
if (queryProxy === null) {
return "continue";
}
// This is called from box2d.b2DynamicTree::Query when we are gathering pairs.
// boolean b2BroadPhase::QueryCallback(int32 proxyId);
// We have to query the tree with the fat AABB so that
// we don't fail to create a pair that may touch later.
var fatAABB = queryProxy.aabb; // this.m_tree.GetFatAABB(queryProxy);
// Query tree, create pairs and add them pair buffer.
this_1.m_tree.Query(fatAABB, function (proxy) {
// A proxy cannot form a pair with itself.
if (proxy.m_id === queryProxy.m_id) {
return true;
}
// const proxyA = proxy < queryProxy ? proxy : queryProxy;
// const proxyB = proxy >= queryProxy ? proxy : queryProxy;
var proxyA;
var proxyB;
if (proxy.m_id < queryProxy.m_id) {
proxyA = proxy;
proxyB = queryProxy;
}
else {
proxyA = queryProxy;
proxyB = proxy;
}
// Grow the pair buffer as needed.
if (_this.m_pairCount === _this.m_pairBuffer.length) {
_this.m_pairBuffer[_this.m_pairCount] = new b2Pair(proxyA, proxyB);
}
else {
var pair = _this.m_pairBuffer[_this.m_pairCount];
pair.proxyA = proxyA;
pair.proxyB = proxyB;
}
++_this.m_pairCount;
return true;
});
};
var this_1 = this;
// Perform tree queries for all moving proxies.
for (var i_1 = 0; i_1 < this.m_moveCount; ++i_1) {
_loop_1(i_1);
}
// Reset move buffer
this.m_moveCount = 0;
// Sort the pair buffer to expose duplicates.
this.m_pairBuffer.length = this.m_pairCount;
this.m_pairBuffer.sort(b2PairLessThan);
// Send the pairs back to the client.
var i = 0;
while (i < this.m_pairCount) {
var primaryPair = this.m_pairBuffer[i];
var userDataA = primaryPair.proxyA.userData; // this.m_tree.GetUserData(primaryPair.proxyA);
var userDataB = primaryPair.proxyB.userData; // this.m_tree.GetUserData(primaryPair.proxyB);
if (userDataA && userDataB) {
callback(userDataA, userDataB);
}
++i;
// Skip any duplicate pairs.
while (i < this.m_pairCount) {
var pair = this.m_pairBuffer[i];
if (pair.proxyA.m_id !== primaryPair.proxyA.m_id || pair.proxyB.m_id !== primaryPair.proxyB.m_id) {
break;
}
++i;
}
}
// Try to keep the tree balanced.
// this.m_tree.Rebalance(4);
};
/// Query an AABB for overlapping proxies. The callback class
/// is called for each proxy that overlaps the supplied AABB.
b2BroadPhase.prototype.Query = function (aabb, callback) {
this.m_tree.Query(aabb, callback);
};
b2BroadPhase.prototype.QueryPoint = function (point, callback) {
this.m_tree.QueryPoint(point, callback);
};
/// Ray-cast against the proxies in the tree. This relies on the callback
/// to perform a exact ray-cast in the case were the proxy contains a shape.
/// The callback also performs the any collision filtering. This has performance
/// roughly equal to k * log(n), where k is the number of collisions and n is the
/// number of proxies in the tree.
/// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1).
/// @param callback a callback class that is called for each proxy that is hit by the ray.
b2BroadPhase.prototype.RayCast = function (input, callback) {
this.m_tree.RayCast(input, callback);
};
/// Get the height of the embedded tree.
b2BroadPhase.prototype.GetTreeHeight = function () {
return this.m_tree.GetHeight();
};
/// Get the balance of the embedded tree.
b2BroadPhase.prototype.GetTreeBalance = function () {
return this.m_tree.GetMaxBalance();
};
/// Get the quality metric of the embedded tree.
b2BroadPhase.prototype.GetTreeQuality = function () {
return this.m_tree.GetAreaRatio();
};
/// Shift the world origin. Useful for large worlds.
/// The shift formula is: position -= newOrigin
/// @param newOrigin the new origin with respect to the old origin
b2BroadPhase.prototype.ShiftOrigin = function (newOrigin) {
this.m_tree.ShiftOrigin(newOrigin);
};
b2BroadPhase.prototype.BufferMove = function (proxy) {
this.m_moveBuffer[this.m_moveCount] = proxy;
++this.m_moveCount;
};
b2BroadPhase.prototype.UnBufferMove = function (proxy) {
var i = this.m_moveBuffer.indexOf(proxy);
this.m_moveBuffer[i] = null;
};
return b2BroadPhase;
}());
/// This is used to sort pairs.
function b2PairLessThan(pair1, pair2) {
if (pair1.proxyA.m_id === pair2.proxyA.m_id) {
return pair1.proxyB.m_id - pair2.proxyB.m_id;
}
return pair1.proxyA.m_id - pair2.proxyA.m_id;
}
/*
* Copyright (c) 2006-2009 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.
*/
exports.b2_toiTime = 0;
exports.b2_toiMaxTime = 0;
exports.b2_toiCalls = 0;
exports.b2_toiIters = 0;
exports.b2_toiMaxIters = 0;
exports.b2_toiRootIters = 0;
exports.b2_toiMaxRootIters = 0;
function b2_toi_reset() {
exports.b2_toiTime = 0;
exports.b2_toiMaxTime = 0;
exports.b2_toiCalls = 0;
exports.b2_toiIters = 0;
exports.b2_toiMaxIters = 0;
exports.b2_toiRootIters = 0;
exports.b2_toiMaxRootIters = 0;
}
var b2TimeOfImpact_s_xfA = new b2Transform();
var b2TimeOfImpact_s_xfB = new b2Transform();
var b2TimeOfImpact_s_pointA = new b2Vec2();
var b2TimeOfImpact_s_pointB = new b2Vec2();
var b2TimeOfImpact_s_normal = new b2Vec2();
var b2TimeOfImpact_s_axisA = new b2Vec2();
var b2TimeOfImpact_s_axisB = new b2Vec2();
/// Input parameters for b2TimeOfImpact
var b2TOIInput = /** @class */ (function () {
function b2TOIInput() {
this.proxyA = new b2DistanceProxy();
this.proxyB = new b2DistanceProxy();
this.sweepA = new b2Sweep();
this.sweepB = new b2Sweep();
this.tMax = 0; // defines sweep interval [0, tMax]
}
return b2TOIInput;
}());
(function (b2TOIOutputState) {
b2TOIOutputState[b2TOIOutputState["e_unknown"] = 0] = "e_unknown";
b2TOIOutputState[b2TOIOutputState["e_failed"] = 1] = "e_failed";
b2TOIOutputState[b2TOIOutputState["e_overlapped"] = 2] = "e_overlapped";
b2TOIOutputState[b2TOIOutputState["e_touching"] = 3] = "e_touching";
b2TOIOutputState[b2TOIOutputState["e_separated"] = 4] = "e_separated";
})(exports.b2TOIOutputState || (exports.b2TOIOutputState = {}));
var b2TOIOutput = /** @class */ (function () {
function b2TOIOutput() {
this.state = exports.b2TOIOutputState.e_unknown;
this.t = 0;
}
return b2TOIOutput;
}());
(function (b2SeparationFunctionType) {
b2SeparationFunctionType[b2SeparationFunctionType["e_unknown"] = -1] = "e_unknown";
b2SeparationFunctionType[b2SeparationFunctionType["e_points"] = 0] = "e_points";
b2SeparationFunctionType[b2SeparationFunctionType["e_faceA"] = 1] = "e_faceA";
b2SeparationFunctionType[b2SeparationFunctionType["e_faceB"] = 2] = "e_faceB";
})(exports.b2SeparationFunctionType || (exports.b2SeparationFunctionType = {}));
var b2SeparationFunction = /** @class */ (function () {
function b2SeparationFunction() {
this.m_sweepA = new b2Sweep();
this.m_sweepB = new b2Sweep();
this.m_type = exports.b2SeparationFunctionType.e_unknown;
this.m_localPoint = new b2Vec2();
this.m_axis = new b2Vec2();
}
b2SeparationFunction.prototype.Initialize = function (cache, proxyA, sweepA, proxyB, sweepB, t1) {
this.m_proxyA = proxyA;
this.m_proxyB = proxyB;
var count = cache.count;
// DEBUG: b2Assert(0 < count && count < 3);
this.m_sweepA.Copy(sweepA);
this.m_sweepB.Copy(sweepB);
var xfA = b2TimeOfImpact_s_xfA;
var xfB = b2TimeOfImpact_s_xfB;
this.m_sweepA.GetTransform(xfA, t1);
this.m_sweepB.GetTransform(xfB, t1);
if (count === 1) {
this.m_type = exports.b2SeparationFunctionType.e_points;
var localPointA = this.m_proxyA.GetVertex(cache.indexA[0]);
var localPointB = this.m_proxyB.GetVertex(cache.indexB[0]);
var pointA = b2Transform.MulXV(xfA, localPointA, b2TimeOfImpact_s_pointA);
var pointB = b2Transform.MulXV(xfB, localPointB, b2TimeOfImpact_s_pointB);
b2Vec2.SubVV(pointB, pointA, this.m_axis);
var s = this.m_axis.Normalize();
// #if B2_ENABLE_PARTICLE
this.m_localPoint.SetZero();
// #endif
return s;
}
else if (cache.indexA[0] === cache.indexA[1]) {
// Two points on B and one on A.
this.m_type = exports.b2SeparationFunctionType.e_faceB;
var localPointB1 = this.m_proxyB.GetVertex(cache.indexB[0]);
var localPointB2 = this.m_proxyB.GetVertex(cache.indexB[1]);
b2Vec2.CrossVOne(b2Vec2.SubVV(localPointB2, localPointB1, b2Vec2.s_t0), this.m_axis).SelfNormalize();
var normal = b2Rot.MulRV(xfB.q, this.m_axis, b2TimeOfImpact_s_normal);
b2Vec2.MidVV(localPointB1, localPointB2, this.m_localPoint);
var pointB = b2Transform.MulXV(xfB, this.m_localPoint, b2TimeOfImpact_s_pointB);
var localPointA = this.m_proxyA.GetVertex(cache.indexA[0]);
var pointA = b2Transform.MulXV(xfA, localPointA, b2TimeOfImpact_s_pointA);
var s = b2Vec2.DotVV(b2Vec2.SubVV(pointA, pointB, b2Vec2.s_t0), normal);
if (s < 0) {
this.m_axis.SelfNeg();
s = -s;
}
return s;
}
else {
// Two points on A and one or two points on B.
this.m_type = exports.b2SeparationFunctionType.e_faceA;
var localPointA1 = this.m_proxyA.GetVertex(cache.indexA[0]);
var localPointA2 = this.m_proxyA.GetVertex(cache.indexA[1]);
b2Vec2.CrossVOne(b2Vec2.SubVV(localPointA2, localPointA1, b2Vec2.s_t0), this.m_axis).SelfNormalize();
var normal = b2Rot.MulRV(xfA.q, this.m_axis, b2TimeOfImpact_s_normal);
b2Vec2.MidVV(localPointA1, localPointA2, this.m_localPoint);
var pointA = b2Transform.MulXV(xfA, this.m_localPoint, b2TimeOfImpact_s_pointA);
var localPointB = this.m_proxyB.GetVertex(cache.indexB[0]);
var pointB = b2Transform.MulXV(xfB, localPointB, b2TimeOfImpact_s_pointB);
var s = b2Vec2.DotVV(b2Vec2.SubVV(pointB, pointA, b2Vec2.s_t0), normal);
if (s < 0) {
this.m_axis.SelfNeg();
s = -s;
}
return s;
}
};
b2SeparationFunction.prototype.FindMinSeparation = function (indexA, indexB, t) {
var xfA = b2TimeOfImpact_s_xfA;
var xfB = b2TimeOfImpact_s_xfB;
this.m_sweepA.GetTransform(xfA, t);
this.m_sweepB.GetTransform(xfB, t);
switch (this.m_type) {
case exports.b2SeparationFunctionType.e_points: {
var axisA = b2Rot.MulTRV(xfA.q, this.m_axis, b2TimeOfImpact_s_axisA);
var axisB = b2Rot.MulTRV(xfB.q, b2Vec2.NegV(this.m_axis, b2Vec2.s_t0), b2TimeOfImpact_s_axisB);
indexA[0] = this.m_proxyA.GetSupport(axisA);
indexB[0] = this.m_proxyB.GetSupport(axisB);
var localPointA = this.m_proxyA.GetVertex(indexA[0]);
var localPointB = this.m_proxyB.GetVertex(indexB[0]);
var pointA = b2Transform.MulXV(xfA, localPointA, b2TimeOfImpact_s_pointA);
var pointB = b2Transform.MulXV(xfB, localPointB, b2TimeOfImpact_s_pointB);
var separation = b2Vec2.DotVV(b2Vec2.SubVV(pointB, pointA, b2Vec2.s_t0), this.m_axis);
return separation;
}
case exports.b2SeparationFunctionType.e_faceA: {
var normal = b2Rot.MulRV(xfA.q, this.m_axis, b2TimeOfImpact_s_normal);
var pointA = b2Transform.MulXV(xfA, this.m_localPoint, b2TimeOfImpact_s_pointA);
var axisB = b2Rot.MulTRV(xfB.q, b2Vec2.NegV(normal, b2Vec2.s_t0), b2TimeOfImpact_s_axisB);
indexA[0] = -1;
indexB[0] = this.m_proxyB.GetSupport(axisB);
var localPointB = this.m_proxyB.GetVertex(indexB[0]);
var pointB = b2Transform.MulXV(xfB, localPointB, b2TimeOfImpact_s_pointB);
var separation = b2Vec2.DotVV(b2Vec2.SubVV(pointB, pointA, b2Vec2.s_t0), normal);
return separation;
}
case exports.b2SeparationFunctionType.e_faceB: {
var normal = b2Rot.MulRV(xfB.q, this.m_axis, b2TimeOfImpact_s_normal);
var pointB = b2Transform.MulXV(xfB, this.m_localPoint, b2TimeOfImpact_s_pointB);
var axisA = b2Rot.MulTRV(xfA.q, b2Vec2.NegV(normal, b2Vec2.s_t0), b2TimeOfImpact_s_axisA);
indexB[0] = -1;
indexA[0] = this.m_proxyA.GetSupport(axisA);
var localPointA = this.m_proxyA.GetVertex(indexA[0]);
var pointA = b2Transform.MulXV(xfA, localPointA, b2TimeOfImpact_s_pointA);
var separation = b2Vec2.DotVV(b2Vec2.SubVV(pointA, pointB, b2Vec2.s_t0), normal);
return separation;
}
default:
// DEBUG: b2Assert(false);
indexA[0] = -1;
indexB[0] = -1;
return 0;
}
};
b2SeparationFunction.prototype.Evaluate = function (indexA, indexB, t) {
var xfA = b2TimeOfImpact_s_xfA;
var xfB = b2TimeOfImpact_s_xfB;
this.m_sweepA.GetTransform(xfA, t);
this.m_sweepB.GetTransform(xfB, t);
switch (this.m_type) {
case exports.b2SeparationFunctionType.e_points: {
var localPointA = this.m_proxyA.GetVertex(indexA);
var localPointB = this.m_proxyB.GetVertex(indexB);
var pointA = b2Transform.MulXV(xfA, localPointA, b2TimeOfImpact_s_pointA);
var pointB = b2Transform.MulXV(xfB, localPointB, b2TimeOfImpact_s_pointB);
var separation = b2Vec2.DotVV(b2Vec2.SubVV(pointB, pointA, b2Vec2.s_t0), this.m_axis);
return separation;
}
case exports.b2SeparationFunctionType.e_faceA: {
var normal = b2Rot.MulRV(xfA.q, this.m_axis, b2TimeOfImpact_s_normal);
var pointA = b2Transform.MulXV(xfA, this.m_localPoint, b2TimeOfImpact_s_pointA);
var localPointB = this.m_proxyB.GetVertex(indexB);
var pointB = b2Transform.MulXV(xfB, localPointB, b2TimeOfImpact_s_pointB);
var separation = b2Vec2.DotVV(b2Vec2.SubVV(pointB, pointA, b2Vec2.s_t0), normal);
return separation;
}
case exports.b2SeparationFunctionType.e_faceB: {
var normal = b2Rot.MulRV(xfB.q, this.m_axis, b2TimeOfImpact_s_normal);
var pointB = b2Transform.MulXV(xfB, this.m_localPoint, b2TimeOfImpact_s_pointB);
var localPointA = this.m_proxyA.GetVertex(indexA);
var pointA = b2Transform.MulXV(xfA, localPointA, b2TimeOfImpact_s_pointA);
var separation = b2Vec2.DotVV(b2Vec2.SubVV(pointA, pointB, b2Vec2.s_t0), normal);
return separation;
}
default:
// DEBUG: b2Assert(false);
return 0;
}
};
return b2SeparationFunction;
}());
var b2TimeOfImpact_s_timer = new b2Timer();
var b2TimeOfImpact_s_cache = new b2SimplexCache();
var b2TimeOfImpact_s_distanceInput = new b2DistanceInput();
var b2TimeOfImpact_s_distanceOutput = new b2DistanceOutput();
var b2TimeOfImpact_s_fcn = new b2SeparationFunction();
var b2TimeOfImpact_s_indexA = [0];
var b2TimeOfImpact_s_indexB = [0];
var b2TimeOfImpact_s_sweepA = new b2Sweep();
var b2TimeOfImpact_s_sweepB = new b2Sweep();
function b2TimeOfImpact(output, input) {
var timer = b2TimeOfImpact_s_timer.Reset();
++exports.b2_toiCalls;
output.state = exports.b2TOIOutputState.e_unknown;
output.t = input.tMax;
var proxyA = input.proxyA;
var proxyB = input.proxyB;
var sweepA = b2TimeOfImpact_s_sweepA.Copy(input.sweepA);
var sweepB = b2TimeOfImpact_s_sweepB.Copy(input.sweepB);
// Large rotations can make the root finder fail, so we normalize the
// sweep angles.
sweepA.Normalize();
sweepB.Normalize();
var tMax = input.tMax;
var totalRadius = proxyA.m_radius + proxyB.m_radius;
var target = b2Max(b2_linearSlop, totalRadius - 3 * b2_linearSlop);
var tolerance = 0.25 * b2_linearSlop;
// DEBUG: b2Assert(target > tolerance);
var t1 = 0;
var k_maxIterations = 20; // TODO_ERIN b2Settings
var iter = 0;
// Prepare input for distance query.
var cache = b2TimeOfImpact_s_cache;
cache.count = 0;
var distanceInput = b2TimeOfImpact_s_distanceInput;
distanceInput.proxyA.Copy(input.proxyA);
distanceInput.proxyB.Copy(input.proxyB);
distanceInput.useRadii = false;
// The outer loop progressively attempts to compute new separating axes.
// This loop terminates when an axis is repeated (no progress is made).
for (;;) {
var xfA = b2TimeOfImpact_s_xfA;
var xfB = b2TimeOfImpact_s_xfB;
sweepA.GetTransform(xfA, t1);
sweepB.GetTransform(xfB, t1);
// Get the distance between shapes. We can also use the results
// to get a separating axis.
distanceInput.transformA.Copy(xfA);
distanceInput.transformB.Copy(xfB);
var distanceOutput = b2TimeOfImpact_s_distanceOutput;
b2Distance(distanceOutput, cache, distanceInput);
// If the shapes are overlapped, we give up on continuous collision.
if (distanceOutput.distance <= 0) {
// Failure!
output.state = exports.b2TOIOutputState.e_overlapped;
output.t = 0;
break;
}
if (distanceOutput.distance < target + tolerance) {
// Victory!
output.state = exports.b2TOIOutputState.e_touching;
output.t = t1;
break;
}
// Initialize the separating axis.
var fcn = b2TimeOfImpact_s_fcn;
fcn.Initialize(cache, proxyA, sweepA, proxyB, sweepB, t1);
/*
#if 0
// Dump the curve seen by the root finder {
const int32 N = 100;
float32 dx = 1.0f / N;
float32 xs[N+1];
float32 fs[N+1];
float32 x = 0.0f;
for (int32 i = 0; i <= N; ++i) {
sweepA.GetTransform(&xfA, x);
sweepB.GetTransform(&xfB, x);
float32 f = fcn.Evaluate(xfA, xfB) - target;
printf("%g %g\n", x, f);
xs[i] = x;
fs[i] = f;
x += dx;
}
}
#endif
*/
// Compute the TOI on the separating axis. We do this by successively
// resolving the deepest point. This loop is bounded by the number of vertices.
var done = false;
var t2 = tMax;
var pushBackIter = 0;
for (;;) {
// Find the deepest point at t2. Store the witness point indices.
var indexA = b2TimeOfImpact_s_indexA;
var indexB = b2TimeOfImpact_s_indexB;
var s2 = fcn.FindMinSeparation(indexA, indexB, t2);
// Is the final configuration separated?
if (s2 > (target + tolerance)) {
// Victory!
output.state = exports.b2TOIOutputState.e_separated;
output.t = tMax;
done = true;
break;
}
// Has the separation reached tolerance?
if (s2 > (target - tolerance)) {
// Advance the sweeps
t1 = t2;
break;
}
// Compute the initial separation of the witness points.
var s1 = fcn.Evaluate(indexA[0], indexB[0], t1);
// Check for initial overlap. This might happen if the root finder
// runs out of iterations.
if (s1 < (target - tolerance)) {
output.state = exports.b2TOIOutputState.e_failed;
output.t = t1;
done = true;
break;
}
// Check for touching
if (s1 <= (target + tolerance)) {
// Victory! t1 should hold the TOI (could be 0.0).
output.state = exports.b2TOIOutputState.e_touching;
output.t = t1;
done = true;
break;
}
// Compute 1D root of: f(x) - target = 0
var rootIterCount = 0;
var a1 = t1;
var a2 = t2;
for (;;) {
// Use a mix of the secant rule and bisection.
var t = 0;
if (rootIterCount & 1) {
// Secant rule to improve convergence.
t = a1 + (target - s1) * (a2 - a1) / (s2 - s1);
}
else {
// Bisection to guarantee progress.
t = 0.5 * (a1 + a2);
}
++rootIterCount;
++exports.b2_toiRootIters;
var s = fcn.Evaluate(indexA[0], indexB[0], t);
if (b2Abs(s - target) < tolerance) {
// t2 holds a tentative value for t1
t2 = t;
break;
}
// Ensure we continue to bracket the root.
if (s > target) {
a1 = t;
s1 = s;
}
else {
a2 = t;
s2 = s;
}
if (rootIterCount === 50) {
break;
}
}
exports.b2_toiMaxRootIters = b2Max(exports.b2_toiMaxRootIters, rootIterCount);
++pushBackIter;
if (pushBackIter === b2_maxPolygonVertices) {
break;
}
}
++iter;
++exports.b2_toiIters;
if (done) {
break;
}
if (iter === k_maxIterations) {
// Root finder got stuck. Semi-victory.
output.state = exports.b2TOIOutputState.e_failed;
output.t = t1;
break;
}
}
exports.b2_toiMaxIters = b2Max(exports.b2_toiMaxIters, iter);
var time = timer.GetMilliseconds();
exports.b2_toiMaxTime = b2Max(exports.b2_toiMaxTime, time);
exports.b2_toiTime += time;
}
var b2CollideCircles_s_pA = new b2Vec2();
var b2CollideCircles_s_pB = new b2Vec2();
function b2CollideCircles(manifold, circleA, xfA, circleB, xfB) {
manifold.pointCount = 0;
var pA = b2Transform.MulXV(xfA, circleA.m_p, b2CollideCircles_s_pA);
var pB = b2Transform.MulXV(xfB, circleB.m_p, b2CollideCircles_s_pB);
var distSqr = b2Vec2.DistanceSquaredVV(pA, pB);
var radius = circleA.m_radius + circleB.m_radius;
if (distSqr > radius * radius) {
return;
}
manifold.type = exports.b2ManifoldType.e_circles;
manifold.localPoint.Copy(circleA.m_p);
manifold.localNormal.SetZero();
manifold.pointCount = 1;
manifold.points[0].localPoint.Copy(circleB.m_p);
manifold.points[0].id.key = 0;
}
var b2CollidePolygonAndCircle_s_c = new b2Vec2();
var b2CollidePolygonAndCircle_s_cLocal = new b2Vec2();
var b2CollidePolygonAndCircle_s_faceCenter = new b2Vec2();
function b2CollidePolygonAndCircle(manifold, polygonA, xfA, circleB, xfB) {
manifold.pointCount = 0;
// Compute circle position in the frame of the polygon.
var c = b2Transform.MulXV(xfB, circleB.m_p, b2CollidePolygonAndCircle_s_c);
var cLocal = b2Transform.MulTXV(xfA, c, b2CollidePolygonAndCircle_s_cLocal);
// Find the min separating edge.
var normalIndex = 0;
var separation = (-b2_maxFloat);
var radius = polygonA.m_radius + circleB.m_radius;
var vertexCount = polygonA.m_count;
var vertices = polygonA.m_vertices;
var normals = polygonA.m_normals;
for (var i = 0; i < vertexCount; ++i) {
var s = b2Vec2.DotVV(normals[i], b2Vec2.SubVV(cLocal, vertices[i], b2Vec2.s_t0));
if (s > radius) {
// Early out.
return;
}
if (s > separation) {
separation = s;
normalIndex = i;
}
}
// Vertices that subtend the incident face.
var vertIndex1 = normalIndex;
var vertIndex2 = (vertIndex1 + 1) % vertexCount;
var v1 = vertices[vertIndex1];
var v2 = vertices[vertIndex2];
// If the center is inside the polygon ...
if (separation < b2_epsilon) {
manifold.pointCount = 1;
manifold.type = exports.b2ManifoldType.e_faceA;
manifold.localNormal.Copy(normals[normalIndex]);
b2Vec2.MidVV(v1, v2, manifold.localPoint);
manifold.points[0].localPoint.Copy(circleB.m_p);
manifold.points[0].id.key = 0;
return;
}
// Compute barycentric coordinates
var u1 = b2Vec2.DotVV(b2Vec2.SubVV(cLocal, v1, b2Vec2.s_t0), b2Vec2.SubVV(v2, v1, b2Vec2.s_t1));
var u2 = b2Vec2.DotVV(b2Vec2.SubVV(cLocal, v2, b2Vec2.s_t0), b2Vec2.SubVV(v1, v2, b2Vec2.s_t1));
if (u1 <= 0) {
if (b2Vec2.DistanceSquaredVV(cLocal, v1) > radius * radius) {
return;
}
manifold.pointCount = 1;
manifold.type = exports.b2ManifoldType.e_faceA;
b2Vec2.SubVV(cLocal, v1, manifold.localNormal).SelfNormalize();
manifold.localPoint.Copy(v1);
manifold.points[0].localPoint.Copy(circleB.m_p);
manifold.points[0].id.key = 0;
}
else if (u2 <= 0) {
if (b2Vec2.DistanceSquaredVV(cLocal, v2) > radius * radius) {
return;
}
manifold.pointCount = 1;
manifold.type = exports.b2ManifoldType.e_faceA;
b2Vec2.SubVV(cLocal, v2, manifold.localNormal).SelfNormalize();
manifold.localPoint.Copy(v2);
manifold.points[0].localPoint.Copy(circleB.m_p);
manifold.points[0].id.key = 0;
}
else {
var faceCenter = b2Vec2.MidVV(v1, v2, b2CollidePolygonAndCircle_s_faceCenter);
var separation_1 = b2Vec2.DotVV(b2Vec2.SubVV(cLocal, faceCenter, b2Vec2.s_t1), normals[vertIndex1]);
if (separation_1 > radius) {
return;
}
manifold.pointCount = 1;
manifold.type = exports.b2ManifoldType.e_faceA;
manifold.localNormal.Copy(normals[vertIndex1]).SelfNormalize();
manifold.localPoint.Copy(faceCenter);
manifold.points[0].localPoint.Copy(circleB.m_p);
manifold.points[0].id.key = 0;
}
}
// DEBUG: import { b2Assert } from "../Common/b2Settings";
var b2EdgeSeparation_s_normal1World = new b2Vec2();
var b2EdgeSeparation_s_normal1 = new b2Vec2();
var b2EdgeSeparation_s_v1 = new b2Vec2();
var b2EdgeSeparation_s_v2 = new b2Vec2();
function b2EdgeSeparation(poly1, xf1, edge1, poly2, xf2) {
// DEBUG: const count1: number = poly1.m_count;
var vertices1 = poly1.m_vertices;
var normals1 = poly1.m_normals;
var count2 = poly2.m_count;
var vertices2 = poly2.m_vertices;
// DEBUG: b2Assert(0 <= edge1 && edge1 < count1);
// Convert normal from poly1's frame into poly2's frame.
var normal1World = b2Rot.MulRV(xf1.q, normals1[edge1], b2EdgeSeparation_s_normal1World);
var normal1 = b2Rot.MulTRV(xf2.q, normal1World, b2EdgeSeparation_s_normal1);
// Find support vertex on poly2 for -normal.
var index = 0;
var minDot = b2_maxFloat;
for (var i = 0; i < count2; ++i) {
var dot = b2Vec2.DotVV(vertices2[i], normal1);
if (dot < minDot) {
minDot = dot;
index = i;
}
}
var v1 = b2Transform.MulXV(xf1, vertices1[edge1], b2EdgeSeparation_s_v1);
var v2 = b2Transform.MulXV(xf2, vertices2[index], b2EdgeSeparation_s_v2);
var separation = b2Vec2.DotVV(b2Vec2.SubVV(v2, v1, b2Vec2.s_t0), normal1World);
return separation;
}
var b2FindMaxSeparation_s_d = new b2Vec2();
var b2FindMaxSeparation_s_dLocal1 = new b2Vec2();
function b2FindMaxSeparation(edgeIndex, poly1, xf1, poly2, xf2) {
var count1 = poly1.m_count;
var normals1 = poly1.m_normals;
// Vector pointing from the centroid of poly1 to the centroid of poly2.
var d = b2Vec2.SubVV(b2Transform.MulXV(xf2, poly2.m_centroid, b2Vec2.s_t0), b2Transform.MulXV(xf1, poly1.m_centroid, b2Vec2.s_t1), b2FindMaxSeparation_s_d);
var dLocal1 = b2Rot.MulTRV(xf1.q, d, b2FindMaxSeparation_s_dLocal1);
// Find edge normal on poly1 that has the largest projection onto d.
var edge = 0;
var maxDot = (-b2_maxFloat);
for (var i = 0; i < count1; ++i) {
var dot = b2Vec2.DotVV(normals1[i], dLocal1);
if (dot > maxDot) {
maxDot = dot;
edge = i;
}
}
// Get the separation for the edge normal.
var s = b2EdgeSeparation(poly1, xf1, edge, poly2, xf2);
// Check the separation for the previous edge normal.
var prevEdge = (edge + count1 - 1) % count1;
var sPrev = b2EdgeSeparation(poly1, xf1, prevEdge, poly2, xf2);
// Check the separation for the next edge normal.
var nextEdge = (edge + 1) % count1;
var sNext = b2EdgeSeparation(poly1, xf1, nextEdge, poly2, xf2);
// Find the best edge and the search direction.
var bestEdge = 0;
var bestSeparation = 0;
var increment = 0;
if (sPrev > s && sPrev > sNext) {
increment = -1;
bestEdge = prevEdge;
bestSeparation = sPrev;
}
else if (sNext > s) {
increment = 1;
bestEdge = nextEdge;
bestSeparation = sNext;
}
else {
edgeIndex[0] = edge;
return s;
}
// Perform a local search for the best edge normal.
while (true) {
if (increment === -1) {
edge = (bestEdge + count1 - 1) % count1;
}
else {
edge = (bestEdge + 1) % count1;
}
s = b2EdgeSeparation(poly1, xf1, edge, poly2, xf2);
if (s > bestSeparation) {
bestEdge = edge;
bestSeparation = s;
}
else {
break;
}
}
edgeIndex[0] = bestEdge;
return bestSeparation;
}
var b2FindIncidentEdge_s_normal1 = new b2Vec2();
function b2FindIncidentEdge(c, poly1, xf1, edge1, poly2, xf2) {
// DEBUG: const count1: number = poly1.m_count;
var normals1 = poly1.m_normals;
var count2 = poly2.m_count;
var vertices2 = poly2.m_vertices;
var normals2 = poly2.m_normals;
// DEBUG: b2Assert(0 <= edge1 && edge1 < count1);
// Get the normal of the reference edge in poly2's frame.
var normal1 = b2Rot.MulTRV(xf2.q, b2Rot.MulRV(xf1.q, normals1[edge1], b2Vec2.s_t0), b2FindIncidentEdge_s_normal1);
// Find the incident edge on poly2.
var index = 0;
var minDot = b2_maxFloat;
for (var i = 0; i < count2; ++i) {
var dot = b2Vec2.DotVV(normal1, normals2[i]);
if (dot < minDot) {
minDot = dot;
index = i;
}
}
// Build the clip vertices for the incident edge.
var i1 = index;
var i2 = (i1 + 1) % count2;
var c0 = c[0];
b2Transform.MulXV(xf2, vertices2[i1], c0.v);
var cf0 = c0.id.cf;
cf0.indexA = edge1;
cf0.indexB = i1;
cf0.typeA = exports.b2ContactFeatureType.e_face;
cf0.typeB = exports.b2ContactFeatureType.e_vertex;
var c1 = c[1];
b2Transform.MulXV(xf2, vertices2[i2], c1.v);
var cf1 = c1.id.cf;
cf1.indexA = edge1;
cf1.indexB = i2;
cf1.typeA = exports.b2ContactFeatureType.e_face;
cf1.typeB = exports.b2ContactFeatureType.e_vertex;
}
var b2CollidePolygons_s_incidentEdge = b2ClipVertex.MakeArray(2);
var b2CollidePolygons_s_clipPoints1 = b2ClipVertex.MakeArray(2);
var b2CollidePolygons_s_clipPoints2 = b2ClipVertex.MakeArray(2);
var b2CollidePolygons_s_edgeA = [0];
var b2CollidePolygons_s_edgeB = [0];
var b2CollidePolygons_s_localTangent = new b2Vec2();
var b2CollidePolygons_s_localNormal = new b2Vec2();
var b2CollidePolygons_s_planePoint = new b2Vec2();
var b2CollidePolygons_s_normal = new b2Vec2();
var b2CollidePolygons_s_tangent = new b2Vec2();
var b2CollidePolygons_s_ntangent = new b2Vec2();
var b2CollidePolygons_s_v11 = new b2Vec2();
var b2CollidePolygons_s_v12 = new b2Vec2();
function b2CollidePolygons(manifold, polyA, xfA, polyB, xfB) {
manifold.pointCount = 0;
var totalRadius = polyA.m_radius + polyB.m_radius;
var edgeA = b2CollidePolygons_s_edgeA;
edgeA[0] = 0;
var separationA = b2FindMaxSeparation(edgeA, polyA, xfA, polyB, xfB);
if (separationA > totalRadius) {
return;
}
var edgeB = b2CollidePolygons_s_edgeB;
edgeB[0] = 0;
var separationB = b2FindMaxSeparation(edgeB, polyB, xfB, polyA, xfA);
if (separationB > totalRadius) {
return;
}
var poly1; // reference polygon
var poly2; // incident polygon
var xf1, xf2;
var edge1 = 0; // reference edge
var flip = 0;
var k_relativeTol = 0.98;
var k_absoluteTol = 0.001;
if (separationB > k_relativeTol * separationA + k_absoluteTol) {
poly1 = polyB;
poly2 = polyA;
xf1 = xfB;
xf2 = xfA;
edge1 = edgeB[0];
manifold.type = exports.b2ManifoldType.e_faceB;
flip = 1;
}
else {
poly1 = polyA;
poly2 = polyB;
xf1 = xfA;
xf2 = xfB;
edge1 = edgeA[0];
manifold.type = exports.b2ManifoldType.e_faceA;
flip = 0;
}
var incidentEdge = b2CollidePolygons_s_incidentEdge;
b2FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2);
var count1 = poly1.m_count;
var vertices1 = poly1.m_vertices;
var iv1 = edge1;
var iv2 = (edge1 + 1) % count1;
var local_v11 = vertices1[iv1];
var local_v12 = vertices1[iv2];
var localTangent = b2Vec2.SubVV(local_v12, local_v11, b2CollidePolygons_s_localTangent);
localTangent.Normalize();
var localNormal = b2Vec2.CrossVOne(localTangent, b2CollidePolygons_s_localNormal);
var planePoint = b2Vec2.MidVV(local_v11, local_v12, b2CollidePolygons_s_planePoint);
var tangent = b2Rot.MulRV(xf1.q, localTangent, b2CollidePolygons_s_tangent);
var normal = b2Vec2.CrossVOne(tangent, b2CollidePolygons_s_normal);
var v11 = b2Transform.MulXV(xf1, local_v11, b2CollidePolygons_s_v11);
var v12 = b2Transform.MulXV(xf1, local_v12, b2CollidePolygons_s_v12);
// Face offset.
var frontOffset = b2Vec2.DotVV(normal, v11);
// Side offsets, extended by polytope skin thickness.
var sideOffset1 = -b2Vec2.DotVV(tangent, v11) + totalRadius;
var sideOffset2 = b2Vec2.DotVV(tangent, v12) + totalRadius;
// Clip incident edge against extruded edge1 side edges.
var clipPoints1 = b2CollidePolygons_s_clipPoints1;
var clipPoints2 = b2CollidePolygons_s_clipPoints2;
var np;
// Clip to box side 1
var ntangent = b2Vec2.NegV(tangent, b2CollidePolygons_s_ntangent);
np = b2ClipSegmentToLine(clipPoints1, incidentEdge, ntangent, sideOffset1, iv1);
if (np < 2) {
return;
}
// Clip to negative box side 1
np = b2ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2, iv2);
if (np < 2) {
return;
}
// Now clipPoints2 contains the clipped points.
manifold.localNormal.Copy(localNormal);
manifold.localPoint.Copy(planePoint);
var pointCount = 0;
for (var i = 0; i < b2_maxManifoldPoints; ++i) {
var cv = clipPoints2[i];
var separation = b2Vec2.DotVV(normal, cv.v) - frontOffset;
if (separation <= totalRadius) {
var cp = manifold.points[pointCount];
b2Transform.MulTXV(xf2, cv.v, cp.localPoint);
cp.id.Copy(cv.id);
if (flip) {
// Swap features
var cf = cp.id.cf;
cp.id.cf.indexA = cf.indexB;
cp.id.cf.indexB = cf.indexA;
cp.id.cf.typeA = cf.typeB;
cp.id.cf.typeB = cf.typeA;
}
++pointCount;
}
}
manifold.pointCount = pointCount;
}
// DEBUG: import { b2Assert } from "../Common/b2Settings";
var b2CollideEdgeAndCircle_s_Q = new b2Vec2();
var b2CollideEdgeAndCircle_s_e = new b2Vec2();
var b2CollideEdgeAndCircle_s_d = new b2Vec2();
var b2CollideEdgeAndCircle_s_e1 = new b2Vec2();
var b2CollideEdgeAndCircle_s_e2 = new b2Vec2();
var b2CollideEdgeAndCircle_s_P = new b2Vec2();
var b2CollideEdgeAndCircle_s_n = new b2Vec2();
var b2CollideEdgeAndCircle_s_id = new b2ContactID();
function b2CollideEdgeAndCircle(manifold, edgeA, xfA, circleB, xfB) {
manifold.pointCount = 0;
// Compute circle in frame of edge
var Q = b2Transform.MulTXV(xfA, b2Transform.MulXV(xfB, circleB.m_p, b2Vec2.s_t0), b2CollideEdgeAndCircle_s_Q);
var A = edgeA.m_vertex1;
var B = edgeA.m_vertex2;
var e = b2Vec2.SubVV(B, A, b2CollideEdgeAndCircle_s_e);
// Barycentric coordinates
var u = b2Vec2.DotVV(e, b2Vec2.SubVV(B, Q, b2Vec2.s_t0));
var v = b2Vec2.DotVV(e, b2Vec2.SubVV(Q, A, b2Vec2.s_t0));
var radius = edgeA.m_radius + circleB.m_radius;
// const cf: b2ContactFeature = new b2ContactFeature();
var id = b2CollideEdgeAndCircle_s_id;
id.cf.indexB = 0;
id.cf.typeB = exports.b2ContactFeatureType.e_vertex;
// Region A
if (v <= 0) {
var P_1 = A;
var d_1 = b2Vec2.SubVV(Q, P_1, b2CollideEdgeAndCircle_s_d);
var dd_1 = b2Vec2.DotVV(d_1, d_1);
if (dd_1 > radius * radius) {
return;
}
// Is there an edge connected to A?
if (edgeA.m_hasVertex0) {
var A1 = edgeA.m_vertex0;
var B1 = A;
var e1 = b2Vec2.SubVV(B1, A1, b2CollideEdgeAndCircle_s_e1);
var u1 = b2Vec2.DotVV(e1, b2Vec2.SubVV(B1, Q, b2Vec2.s_t0));
// Is the circle in Region AB of the previous edge?
if (u1 > 0) {
return;
}
}
id.cf.indexA = 0;
id.cf.typeA = exports.b2ContactFeatureType.e_vertex;
manifold.pointCount = 1;
manifold.type = exports.b2ManifoldType.e_circles;
manifold.localNormal.SetZero();
manifold.localPoint.Copy(P_1);
manifold.points[0].id.Copy(id);
// manifold.points[0].id.key = 0;
// manifold.points[0].id.cf = cf;
manifold.points[0].localPoint.Copy(circleB.m_p);
return;
}
// Region B
if (u <= 0) {
var P_2 = B;
var d_2 = b2Vec2.SubVV(Q, P_2, b2CollideEdgeAndCircle_s_d);
var dd_2 = b2Vec2.DotVV(d_2, d_2);
if (dd_2 > radius * radius) {
return;
}
// Is there an edge connected to B?
if (edgeA.m_hasVertex3) {
var B2 = edgeA.m_vertex3;
var A2 = B;
var e2 = b2Vec2.SubVV(B2, A2, b2CollideEdgeAndCircle_s_e2);
var v2 = b2Vec2.DotVV(e2, b2Vec2.SubVV(Q, A2, b2Vec2.s_t0));
// Is the circle in Region AB of the next edge?
if (v2 > 0) {
return;
}
}
id.cf.indexA = 1;
id.cf.typeA = exports.b2ContactFeatureType.e_vertex;
manifold.pointCount = 1;
manifold.type = exports.b2ManifoldType.e_circles;
manifold.localNormal.SetZero();
manifold.localPoint.Copy(P_2);
manifold.points[0].id.Copy(id);
// manifold.points[0].id.key = 0;
// manifold.points[0].id.cf = cf;
manifold.points[0].localPoint.Copy(circleB.m_p);
return;
}
// Region AB
var den = b2Vec2.DotVV(e, e);
// DEBUG: b2Assert(den > 0);
var P = b2CollideEdgeAndCircle_s_P;
P.x = (1 / den) * (u * A.x + v * B.x);
P.y = (1 / den) * (u * A.y + v * B.y);
var d = b2Vec2.SubVV(Q, P, b2CollideEdgeAndCircle_s_d);
var dd = b2Vec2.DotVV(d, d);
if (dd > radius * radius) {
return;
}
var n = b2CollideEdgeAndCircle_s_n.Set(-e.y, e.x);
if (b2Vec2.DotVV(n, b2Vec2.SubVV(Q, A, b2Vec2.s_t0)) < 0) {
n.Set(-n.x, -n.y);
}
n.Normalize();
id.cf.indexA = 0;
id.cf.typeA = exports.b2ContactFeatureType.e_face;
manifold.pointCount = 1;
manifold.type = exports.b2ManifoldType.e_faceA;
manifold.localNormal.Copy(n);
manifold.localPoint.Copy(A);
manifold.points[0].id.Copy(id);
// manifold.points[0].id.key = 0;
// manifold.points[0].id.cf = cf;
manifold.points[0].localPoint.Copy(circleB.m_p);
}
var b2EPAxis = /** @class */ (function () {
function b2EPAxis() {
this.type = 0 /* e_unknown */;
this.index = 0;
this.separation = 0;
}
return b2EPAxis;
}());
var b2TempPolygon = /** @class */ (function () {
function b2TempPolygon() {
this.vertices = b2Vec2.MakeArray(b2_maxPolygonVertices);
this.normals = b2Vec2.MakeArray(b2_maxPolygonVertices);
this.count = 0;
}
return b2TempPolygon;
}());
var b2ReferenceFace = /** @class */ (function () {
function b2ReferenceFace() {
this.i1 = 0;
this.i2 = 0;
this.v1 = new b2Vec2();
this.v2 = new b2Vec2();
this.normal = new b2Vec2();
this.sideNormal1 = new b2Vec2();
this.sideOffset1 = 0;
this.sideNormal2 = new b2Vec2();
this.sideOffset2 = 0;
}
return b2ReferenceFace;
}());
var b2EPCollider = /** @class */ (function () {
function b2EPCollider() {
this.m_polygonB = new b2TempPolygon();
this.m_xf = new b2Transform();
this.m_centroidB = new b2Vec2();
this.m_v0 = new b2Vec2();
this.m_v1 = new b2Vec2();
this.m_v2 = new b2Vec2();
this.m_v3 = new b2Vec2();
this.m_normal0 = new b2Vec2();
this.m_normal1 = new b2Vec2();
this.m_normal2 = new b2Vec2();
this.m_normal = new b2Vec2();
this.m_type1 = 0 /* e_isolated */;
this.m_type2 = 0 /* e_isolated */;
this.m_lowerLimit = new b2Vec2();
this.m_upperLimit = new b2Vec2();
this.m_radius = 0;
this.m_front = false;
}
b2EPCollider.prototype.Collide = function (manifold, edgeA, xfA, polygonB, xfB) {
b2Transform.MulTXX(xfA, xfB, this.m_xf);
b2Transform.MulXV(this.m_xf, polygonB.m_centroid, this.m_centroidB);
this.m_v0.Copy(edgeA.m_vertex0);
this.m_v1.Copy(edgeA.m_vertex1);
this.m_v2.Copy(edgeA.m_vertex2);
this.m_v3.Copy(edgeA.m_vertex3);
var hasVertex0 = edgeA.m_hasVertex0;
var hasVertex3 = edgeA.m_hasVertex3;
var edge1 = b2Vec2.SubVV(this.m_v2, this.m_v1, b2EPCollider.s_edge1);
edge1.Normalize();
this.m_normal1.Set(edge1.y, -edge1.x);
var offset1 = b2Vec2.DotVV(this.m_normal1, b2Vec2.SubVV(this.m_centroidB, this.m_v1, b2Vec2.s_t0));
var offset0 = 0;
var offset2 = 0;
var convex1 = false;
var convex2 = false;
// Is there a preceding edge?
if (hasVertex0) {
var edge0 = b2Vec2.SubVV(this.m_v1, this.m_v0, b2EPCollider.s_edge0);
edge0.Normalize();
this.m_normal0.Set(edge0.y, -edge0.x);
convex1 = b2Vec2.CrossVV(edge0, edge1) >= 0;
offset0 = b2Vec2.DotVV(this.m_normal0, b2Vec2.SubVV(this.m_centroidB, this.m_v0, b2Vec2.s_t0));
}
// Is there a following edge?
if (hasVertex3) {
var edge2 = b2Vec2.SubVV(this.m_v3, this.m_v2, b2EPCollider.s_edge2);
edge2.Normalize();
this.m_normal2.Set(edge2.y, -edge2.x);
convex2 = b2Vec2.CrossVV(edge1, edge2) > 0;
offset2 = b2Vec2.DotVV(this.m_normal2, b2Vec2.SubVV(this.m_centroidB, this.m_v2, b2Vec2.s_t0));
}
// Determine front or back collision. Determine collision normal limits.
if (hasVertex0 && hasVertex3) {
if (convex1 && convex2) {
this.m_front = offset0 >= 0 || offset1 >= 0 || offset2 >= 0;
if (this.m_front) {
this.m_normal.Copy(this.m_normal1);
this.m_lowerLimit.Copy(this.m_normal0);
this.m_upperLimit.Copy(this.m_normal2);
}
else {
this.m_normal.Copy(this.m_normal1).SelfNeg();
this.m_lowerLimit.Copy(this.m_normal1).SelfNeg();
this.m_upperLimit.Copy(this.m_normal1).SelfNeg();
}
}
else if (convex1) {
this.m_front = offset0 >= 0 || (offset1 >= 0 && offset2 >= 0);
if (this.m_front) {
this.m_normal.Copy(this.m_normal1);
this.m_lowerLimit.Copy(this.m_normal0);
this.m_upperLimit.Copy(this.m_normal1);
}
else {
this.m_normal.Copy(this.m_normal1).SelfNeg();
this.m_lowerLimit.Copy(this.m_normal2).SelfNeg();
this.m_upperLimit.Copy(this.m_normal1).SelfNeg();
}
}
else if (convex2) {
this.m_front = offset2 >= 0 || (offset0 >= 0 && offset1 >= 0);
if (this.m_front) {
this.m_normal.Copy(this.m_normal1);
this.m_lowerLimit.Copy(this.m_normal1);
this.m_upperLimit.Copy(this.m_normal2);
}
else {
this.m_normal.Copy(this.m_normal1).SelfNeg();
this.m_lowerLimit.Copy(this.m_normal1).SelfNeg();
this.m_upperLimit.Copy(this.m_normal0).SelfNeg();
}
}
else {
this.m_front = offset0 >= 0 && offset1 >= 0 && offset2 >= 0;
if (this.m_front) {
this.m_normal.Copy(this.m_normal1);
this.m_lowerLimit.Copy(this.m_normal1);
this.m_upperLimit.Copy(this.m_normal1);
}
else {
this.m_normal.Copy(this.m_normal1).SelfNeg();
this.m_lowerLimit.Copy(this.m_normal2).SelfNeg();
this.m_upperLimit.Copy(this.m_normal0).SelfNeg();
}
}
}
else if (hasVertex0) {
if (convex1) {
this.m_front = offset0 >= 0 || offset1 >= 0;
if (this.m_front) {
this.m_normal.Copy(this.m_normal1);
this.m_lowerLimit.Copy(this.m_normal0);
this.m_upperLimit.Copy(this.m_normal1).SelfNeg();
}
else {
this.m_normal.Copy(this.m_normal1).SelfNeg();
this.m_lowerLimit.Copy(this.m_normal1);
this.m_upperLimit.Copy(this.m_normal1).SelfNeg();
}
}
else {
this.m_front = offset0 >= 0 && offset1 >= 0;
if (this.m_front) {
this.m_normal.Copy(this.m_normal1);
this.m_lowerLimit.Copy(this.m_normal1);
this.m_upperLimit.Copy(this.m_normal1).SelfNeg();
}
else {
this.m_normal.Copy(this.m_normal1).SelfNeg();
this.m_lowerLimit.Copy(this.m_normal1);
this.m_upperLimit.Copy(this.m_normal0).SelfNeg();
}
}
}
else if (hasVertex3) {
if (convex2) {
this.m_front = offset1 >= 0 || offset2 >= 0;
if (this.m_front) {
this.m_normal.Copy(this.m_normal1);
this.m_lowerLimit.Copy(this.m_normal1).SelfNeg();
this.m_upperLimit.Copy(this.m_normal2);
}
else {
this.m_normal.Copy(this.m_normal1).SelfNeg();
this.m_lowerLimit.Copy(this.m_normal1).SelfNeg();
this.m_upperLimit.Copy(this.m_normal1);
}
}
else {
this.m_front = offset1 >= 0 && offset2 >= 0;
if (this.m_front) {
this.m_normal.Copy(this.m_normal1);
this.m_lowerLimit.Copy(this.m_normal1).SelfNeg();
this.m_upperLimit.Copy(this.m_normal1);
}
else {
this.m_normal.Copy(this.m_normal1).SelfNeg();
this.m_lowerLimit.Copy(this.m_normal2).SelfNeg();
this.m_upperLimit.Copy(this.m_normal1);
}
}
}
else {
this.m_front = offset1 >= 0;
if (this.m_front) {
this.m_normal.Copy(this.m_normal1);
this.m_lowerLimit.Copy(this.m_normal1).SelfNeg();
this.m_upperLimit.Copy(this.m_normal1).SelfNeg();
}
else {
this.m_normal.Copy(this.m_normal1).SelfNeg();
this.m_lowerLimit.Copy(this.m_normal1);
this.m_upperLimit.Copy(this.m_normal1);
}
}
// Get polygonB in frameA
this.m_polygonB.count = polygonB.m_count;
for (var i = 0; i < polygonB.m_count; ++i) {
b2Transform.MulXV(this.m_xf, polygonB.m_vertices[i], this.m_polygonB.vertices[i]);
b2Rot.MulRV(this.m_xf.q, polygonB.m_normals[i], this.m_polygonB.normals[i]);
}
this.m_radius = polygonB.m_radius + edgeA.m_radius;
manifold.pointCount = 0;
var edgeAxis = this.ComputeEdgeSeparation(b2EPCollider.s_edgeAxis);
// If no valid normal can be found than this edge should not collide.
if (edgeAxis.type === 0 /* e_unknown */) {
return;
}
if (edgeAxis.separation > this.m_radius) {
return;
}
var polygonAxis = this.ComputePolygonSeparation(b2EPCollider.s_polygonAxis);
if (polygonAxis.type !== 0 /* e_unknown */ && polygonAxis.separation > this.m_radius) {
return;
}
// Use hysteresis for jitter reduction.
var k_relativeTol = 0.98;
var k_absoluteTol = 0.001;
var primaryAxis;
if (polygonAxis.type === 0 /* e_unknown */) {
primaryAxis = edgeAxis;
}
else if (polygonAxis.separation > k_relativeTol * edgeAxis.separation + k_absoluteTol) {
primaryAxis = polygonAxis;
}
else {
primaryAxis = edgeAxis;
}
var ie = b2EPCollider.s_ie;
var rf = b2EPCollider.s_rf;
if (primaryAxis.type === 1 /* e_edgeA */) {
manifold.type = exports.b2ManifoldType.e_faceA;
// Search for the polygon normal that is most anti-parallel to the edge normal.
var bestIndex = 0;
var bestValue = b2Vec2.DotVV(this.m_normal, this.m_polygonB.normals[0]);
for (var i = 1; i < this.m_polygonB.count; ++i) {
var value = b2Vec2.DotVV(this.m_normal, this.m_polygonB.normals[i]);
if (value < bestValue) {
bestValue = value;
bestIndex = i;
}
}
var i1 = bestIndex;
var i2 = (i1 + 1) % this.m_polygonB.count;
var ie0 = ie[0];
ie0.v.Copy(this.m_polygonB.vertices[i1]);
ie0.id.cf.indexA = 0;
ie0.id.cf.indexB = i1;
ie0.id.cf.typeA = exports.b2ContactFeatureType.e_face;
ie0.id.cf.typeB = exports.b2ContactFeatureType.e_vertex;
var ie1 = ie[1];
ie1.v.Copy(this.m_polygonB.vertices[i2]);
ie1.id.cf.indexA = 0;
ie1.id.cf.indexB = i2;
ie1.id.cf.typeA = exports.b2ContactFeatureType.e_face;
ie1.id.cf.typeB = exports.b2ContactFeatureType.e_vertex;
if (this.m_front) {
rf.i1 = 0;
rf.i2 = 1;
rf.v1.Copy(this.m_v1);
rf.v2.Copy(this.m_v2);
rf.normal.Copy(this.m_normal1);
}
else {
rf.i1 = 1;
rf.i2 = 0;
rf.v1.Copy(this.m_v2);
rf.v2.Copy(this.m_v1);
rf.normal.Copy(this.m_normal1).SelfNeg();
}
}
else {
manifold.type = exports.b2ManifoldType.e_faceB;
var ie0 = ie[0];
ie0.v.Copy(this.m_v1);
ie0.id.cf.indexA = 0;
ie0.id.cf.indexB = primaryAxis.index;
ie0.id.cf.typeA = exports.b2ContactFeatureType.e_vertex;
ie0.id.cf.typeB = exports.b2ContactFeatureType.e_face;
var ie1 = ie[1];
ie1.v.Copy(this.m_v2);
ie1.id.cf.indexA = 0;
ie1.id.cf.indexB = primaryAxis.index;
ie1.id.cf.typeA = exports.b2ContactFeatureType.e_vertex;
ie1.id.cf.typeB = exports.b2ContactFeatureType.e_face;
rf.i1 = primaryAxis.index;
rf.i2 = (rf.i1 + 1) % this.m_polygonB.count;
rf.v1.Copy(this.m_polygonB.vertices[rf.i1]);
rf.v2.Copy(this.m_polygonB.vertices[rf.i2]);
rf.normal.Copy(this.m_polygonB.normals[rf.i1]);
}
rf.sideNormal1.Set(rf.normal.y, -rf.normal.x);
rf.sideNormal2.Copy(rf.sideNormal1).SelfNeg();
rf.sideOffset1 = b2Vec2.DotVV(rf.sideNormal1, rf.v1);
rf.sideOffset2 = b2Vec2.DotVV(rf.sideNormal2, rf.v2);
// Clip incident edge against extruded edge1 side edges.
var clipPoints1 = b2EPCollider.s_clipPoints1;
var clipPoints2 = b2EPCollider.s_clipPoints2;
var np = 0;
// Clip to box side 1
np = b2ClipSegmentToLine(clipPoints1, ie, rf.sideNormal1, rf.sideOffset1, rf.i1);
if (np < b2_maxManifoldPoints) {
return;
}
// Clip to negative box side 1
np = b2ClipSegmentToLine(clipPoints2, clipPoints1, rf.sideNormal2, rf.sideOffset2, rf.i2);
if (np < b2_maxManifoldPoints) {
return;
}
// Now clipPoints2 contains the clipped points.
if (primaryAxis.type === 1 /* e_edgeA */) {
manifold.localNormal.Copy(rf.normal);
manifold.localPoint.Copy(rf.v1);
}
else {
manifold.localNormal.Copy(polygonB.m_normals[rf.i1]);
manifold.localPoint.Copy(polygonB.m_vertices[rf.i1]);
}
var pointCount = 0;
for (var i = 0; i < b2_maxManifoldPoints; ++i) {
var separation = void 0;
separation = b2Vec2.DotVV(rf.normal, b2Vec2.SubVV(clipPoints2[i].v, rf.v1, b2Vec2.s_t0));
if (separation <= this.m_radius) {
var cp = manifold.points[pointCount];
if (primaryAxis.type === 1 /* e_edgeA */) {
b2Transform.MulTXV(this.m_xf, clipPoints2[i].v, cp.localPoint);
cp.id = clipPoints2[i].id;
}
else {
cp.localPoint.Copy(clipPoints2[i].v);
cp.id.cf.typeA = clipPoints2[i].id.cf.typeB;
cp.id.cf.typeB = clipPoints2[i].id.cf.typeA;
cp.id.cf.indexA = clipPoints2[i].id.cf.indexB;
cp.id.cf.indexB = clipPoints2[i].id.cf.indexA;
}
++pointCount;
}
}
manifold.pointCount = pointCount;
};
b2EPCollider.prototype.ComputeEdgeSeparation = function (out) {
var axis = out;
axis.type = 1 /* e_edgeA */;
axis.index = this.m_front ? 0 : 1;
axis.separation = b2_maxFloat;
for (var i = 0; i < this.m_polygonB.count; ++i) {
var s = b2Vec2.DotVV(this.m_normal, b2Vec2.SubVV(this.m_polygonB.vertices[i], this.m_v1, b2Vec2.s_t0));
if (s < axis.separation) {
axis.separation = s;
}
}
return axis;
};
b2EPCollider.prototype.ComputePolygonSeparation = function (out) {
var axis = out;
axis.type = 0 /* e_unknown */;
axis.index = -1;
axis.separation = -b2_maxFloat;
var perp = b2EPCollider.s_perp.Set(-this.m_normal.y, this.m_normal.x);
for (var i = 0; i < this.m_polygonB.count; ++i) {
var n = b2Vec2.NegV(this.m_polygonB.normals[i], b2EPCollider.s_n);
var s1 = b2Vec2.DotVV(n, b2Vec2.SubVV(this.m_polygonB.vertices[i], this.m_v1, b2Vec2.s_t0));
var s2 = b2Vec2.DotVV(n, b2Vec2.SubVV(this.m_polygonB.vertices[i], this.m_v2, b2Vec2.s_t0));
var s = b2Min(s1, s2);
if (s > this.m_radius) {
// No collision
axis.type = 2 /* e_edgeB */;
axis.index = i;
axis.separation = s;
return axis;
}
// Adjacency
if (b2Vec2.DotVV(n, perp) >= 0) {
if (b2Vec2.DotVV(b2Vec2.SubVV(n, this.m_upperLimit, b2Vec2.s_t0), this.m_normal) < -b2_angularSlop) {
continue;
}
}
else {
if (b2Vec2.DotVV(b2Vec2.SubVV(n, this.m_lowerLimit, b2Vec2.s_t0), this.m_normal) < -b2_angularSlop) {
continue;
}
}
if (s > axis.separation) {
axis.type = 2 /* e_edgeB */;
axis.index = i;
axis.separation = s;
}
}
return axis;
};
b2EPCollider.s_edge1 = new b2Vec2();
b2EPCollider.s_edge0 = new b2Vec2();
b2EPCollider.s_edge2 = new b2Vec2();
b2EPCollider.s_ie = b2ClipVertex.MakeArray(2);
b2EPCollider.s_rf = new b2ReferenceFace();
b2EPCollider.s_clipPoints1 = b2ClipVertex.MakeArray(2);
b2EPCollider.s_clipPoints2 = b2ClipVertex.MakeArray(2);
b2EPCollider.s_edgeAxis = new b2EPAxis();
b2EPCollider.s_polygonAxis = new b2EPAxis();
b2EPCollider.s_n = new b2Vec2();
b2EPCollider.s_perp = new b2Vec2();
return b2EPCollider;
}());
var b2CollideEdgeAndPolygon_s_collider = new b2EPCollider();
function b2CollideEdgeAndPolygon(manifold, edgeA, xfA, polygonB, xfB) {
var collider = b2CollideEdgeAndPolygon_s_collider;
collider.Collide(manifold, edgeA, xfA, polygonB, xfB);
}
/*
* Copyright (c) 2006-2009 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.
*/
/// This holds the mass data computed for a shape.
var b2MassData = /** @class */ (function () {
function b2MassData() {
/// The mass of the shape, usually in kilograms.
this.mass = 0;
/// The position of the shape's centroid relative to the shape's origin.
this.center = new b2Vec2(0, 0);
/// The rotational inertia of the shape about the local origin.
this.I = 0;
}
return b2MassData;
}());
(function (b2ShapeType) {
b2ShapeType[b2ShapeType["e_unknown"] = -1] = "e_unknown";
b2ShapeType[b2ShapeType["e_circleShape"] = 0] = "e_circleShape";
b2ShapeType[b2ShapeType["e_edgeShape"] = 1] = "e_edgeShape";
b2ShapeType[b2ShapeType["e_polygonShape"] = 2] = "e_polygonShape";
b2ShapeType[b2ShapeType["e_chainShape"] = 3] = "e_chainShape";
b2ShapeType[b2ShapeType["e_shapeTypeCount"] = 4] = "e_shapeTypeCount";
})(exports.b2ShapeType || (exports.b2ShapeType = {}));
/// A shape is used for collision detection. You can create a shape however you like.
/// Shapes used for simulation in b2World are created automatically when a b2Fixture
/// is created. Shapes may encapsulate a one or more child shapes.
var b2Shape = /** @class */ (function () {
function b2Shape(type, radius) {
this.m_type = exports.b2ShapeType.e_unknown;
/// Radius of a shape. For polygonal shapes this must be b2_polygonRadius. There is no support for
/// making rounded polygons.
this.m_radius = 0;
this.m_type = type;
this.m_radius = radius;
}
b2Shape.prototype.Copy = function (other) {
// DEBUG: b2Assert(this.m_type === other.m_type);
this.m_radius = other.m_radius;
return this;
};
/// Get the type of this shape. You can use this to down cast to the concrete shape.
/// @return the shape type.
b2Shape.prototype.GetType = function () {
return this.m_type;
};
return b2Shape;
}());
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/* global Reflect, Promise */
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
function __extends(d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
/*
* Copyright (c) 2006-2009 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.
*/
/// A circle shape.
var b2CircleShape = /** @class */ (function (_super) {
__extends(b2CircleShape, _super);
function b2CircleShape(radius) {
if (radius === void 0) { radius = 0; }
var _this = _super.call(this, exports.b2ShapeType.e_circleShape, radius) || this;
_this.m_p = new b2Vec2();
return _this;
}
b2CircleShape.prototype.Set = function (position, radius) {
if (radius === void 0) { radius = this.m_radius; }
this.m_p.Copy(position);
this.m_radius = radius;
return this;
};
/// Implement b2Shape.
b2CircleShape.prototype.Clone = function () {
return new b2CircleShape().Copy(this);
};
b2CircleShape.prototype.Copy = function (other) {
_super.prototype.Copy.call(this, other);
// DEBUG: b2Assert(other instanceof b2CircleShape);
this.m_p.Copy(other.m_p);
return this;
};
/// @see b2Shape::GetChildCount
b2CircleShape.prototype.GetChildCount = function () {
return 1;
};
b2CircleShape.prototype.TestPoint = function (transform, p) {
var center = b2Transform.MulXV(transform, this.m_p, b2CircleShape.TestPoint_s_center);
var d = b2Vec2.SubVV(p, center, b2CircleShape.TestPoint_s_d);
return b2Vec2.DotVV(d, d) <= b2Sq(this.m_radius);
};
b2CircleShape.prototype.ComputeDistance = function (xf, p, normal, childIndex) {
var center = b2Transform.MulXV(xf, this.m_p, b2CircleShape.ComputeDistance_s_center);
b2Vec2.SubVV(p, center, normal);
return normal.Normalize() - this.m_radius;
};
b2CircleShape.prototype.RayCast = function (output, input, transform, childIndex) {
var position = b2Transform.MulXV(transform, this.m_p, b2CircleShape.RayCast_s_position);
var s = b2Vec2.SubVV(input.p1, position, b2CircleShape.RayCast_s_s);
var b = b2Vec2.DotVV(s, s) - b2Sq(this.m_radius);
// Solve quadratic equation.
var r = b2Vec2.SubVV(input.p2, input.p1, b2CircleShape.RayCast_s_r);
var c = b2Vec2.DotVV(s, r);
var rr = b2Vec2.DotVV(r, r);
var sigma = c * c - rr * b;
// Check for negative discriminant and short segment.
if (sigma < 0 || rr < b2_epsilon) {
return false;
}
// Find the point of intersection of the line with the circle.
var a = (-(c + b2Sqrt(sigma)));
// Is the intersection point on the segment?
if (0 <= a && a <= input.maxFraction * rr) {
a /= rr;
output.fraction = a;
b2Vec2.AddVMulSV(s, a, r, output.normal).SelfNormalize();
return true;
}
return false;
};
b2CircleShape.prototype.ComputeAABB = function (aabb, transform, childIndex) {
var p = b2Transform.MulXV(transform, this.m_p, b2CircleShape.ComputeAABB_s_p);
aabb.lowerBound.Set(p.x - this.m_radius, p.y - this.m_radius);
aabb.upperBound.Set(p.x + this.m_radius, p.y + this.m_radius);
};
/// @see b2Shape::ComputeMass
b2CircleShape.prototype.ComputeMass = function (massData, density) {
var radius_sq = b2Sq(this.m_radius);
massData.mass = density * b2_pi * radius_sq;
massData.center.Copy(this.m_p);
// inertia about the local origin
massData.I = massData.mass * (0.5 * radius_sq + b2Vec2.DotVV(this.m_p, this.m_p));
};
b2CircleShape.prototype.SetupDistanceProxy = function (proxy, index) {
proxy.m_vertices = proxy.m_buffer;
proxy.m_vertices[0].Copy(this.m_p);
proxy.m_count = 1;
proxy.m_radius = this.m_radius;
};
b2CircleShape.prototype.ComputeSubmergedArea = function (normal, offset, xf, c) {
var p = b2Transform.MulXV(xf, this.m_p, new b2Vec2());
var l = (-(b2Vec2.DotVV(normal, p) - offset));
if (l < (-this.m_radius) + b2_epsilon) {
// Completely dry
return 0;
}
if (l > this.m_radius) {
// Completely wet
c.Copy(p);
return b2_pi * this.m_radius * this.m_radius;
}
// Magic
var r2 = this.m_radius * this.m_radius;
var l2 = l * l;
var area = r2 * (b2Asin(l / this.m_radius) + b2_pi / 2) + l * b2Sqrt(r2 - l2);
var com = (-2 / 3 * b2Pow(r2 - l2, 1.5) / area);
c.x = p.x + normal.x * com;
c.y = p.y + normal.y * com;
return area;
};
b2CircleShape.prototype.Dump = function (log) {
log(" const shape: b2CircleShape = new b2CircleShape();\n");
log(" shape.m_radius = %.15f;\n", this.m_radius);
log(" shape.m_p.Set(%.15f, %.15f);\n", this.m_p.x, this.m_p.y);
};
/// Implement b2Shape.
b2CircleShape.TestPoint_s_center = new b2Vec2();
b2CircleShape.TestPoint_s_d = new b2Vec2();
// #if B2_ENABLE_PARTICLE
/// @see b2Shape::ComputeDistance
b2CircleShape.ComputeDistance_s_center = new b2Vec2();
// #endif
/// Implement b2Shape.
// Collision Detection in Interactive 3D Environments by Gino van den Bergen
// From Section 3.1.2
// x = s + a * r
// norm(x) = radius
b2CircleShape.RayCast_s_position = new b2Vec2();
b2CircleShape.RayCast_s_s = new b2Vec2();
b2CircleShape.RayCast_s_r = new b2Vec2();
/// @see b2Shape::ComputeAABB
b2CircleShape.ComputeAABB_s_p = new b2Vec2();
return b2CircleShape;
}(b2Shape));
/*
* Copyright (c) 2006-2009 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.
*/
/// A convex polygon. It is assumed that the interior of the polygon is to
/// the left of each edge.
/// Polygons have a maximum number of vertices equal to b2_maxPolygonVertices.
/// In most cases you should not need many vertices for a convex polygon.
var b2PolygonShape = /** @class */ (function (_super) {
__extends(b2PolygonShape, _super);
function b2PolygonShape() {
var _this = _super.call(this, exports.b2ShapeType.e_polygonShape, b2_polygonRadius) || this;
_this.m_centroid = new b2Vec2(0, 0);
_this.m_vertices = []; // b2Vec2.MakeArray(b2_maxPolygonVertices);
_this.m_normals = []; // b2Vec2.MakeArray(b2_maxPolygonVertices);
_this.m_count = 0;
return _this;
}
/// Implement b2Shape.
b2PolygonShape.prototype.Clone = function () {
return new b2PolygonShape().Copy(this);
};
b2PolygonShape.prototype.Copy = function (other) {
_super.prototype.Copy.call(this, other);
// DEBUG: b2Assert(other instanceof b2PolygonShape);
this.m_centroid.Copy(other.m_centroid);
this.m_count = other.m_count;
this.m_vertices = b2Vec2.MakeArray(this.m_count);
this.m_normals = b2Vec2.MakeArray(this.m_count);
for (var i = 0; i < this.m_count; ++i) {
this.m_vertices[i].Copy(other.m_vertices[i]);
this.m_normals[i].Copy(other.m_normals[i]);
}
return this;
};
/// @see b2Shape::GetChildCount
b2PolygonShape.prototype.GetChildCount = function () {
return 1;
};
b2PolygonShape.prototype.Set = function (vertices, count, start) {
if (count === void 0) { count = vertices.length; }
if (start === void 0) { start = 0; }
// DEBUG: b2Assert(3 <= count && count <= b2_maxPolygonVertices);
if (count < 3) {
return this.SetAsBox(1, 1);
}
var n = b2Min(count, b2_maxPolygonVertices);
// Perform welding and copy vertices into local buffer.
var ps = b2PolygonShape.Set_s_ps;
var tempCount = 0;
for (var i = 0; i < n; ++i) {
var /*b2Vec2*/ v = vertices[start + i];
var /*bool*/ unique = true;
for (var /*int32*/ j = 0; j < tempCount; ++j) {
if (b2Vec2.DistanceSquaredVV(v, ps[j]) < ((0.5 * b2_linearSlop) * (0.5 * b2_linearSlop))) {
unique = false;
break;
}
}
if (unique) {
ps[tempCount++].Copy(v); // ps[tempCount++] = v;
}
}
n = tempCount;
if (n < 3) {
// Polygon is degenerate.
// DEBUG: b2Assert(false);
return this.SetAsBox(1.0, 1.0);
}
// Create the convex hull using the Gift wrapping algorithm
// http://en.wikipedia.org/wiki/Gift_wrapping_algorithm
// Find the right most point on the hull
var i0 = 0;
var x0 = ps[0].x;
for (var i = 1; i < n; ++i) {
var x = ps[i].x;
if (x > x0 || (x === x0 && ps[i].y < ps[i0].y)) {
i0 = i;
x0 = x;
}
}
var hull = b2PolygonShape.Set_s_hull;
var m = 0;
var ih = i0;
for (;;) {
// DEBUG: b2Assert(m < b2_maxPolygonVertices);
hull[m] = ih;
var ie = 0;
for (var j = 1; j < n; ++j) {
if (ie === ih) {
ie = j;
continue;
}
var r = b2Vec2.SubVV(ps[ie], ps[hull[m]], b2PolygonShape.Set_s_r);
var v = b2Vec2.SubVV(ps[j], ps[hull[m]], b2PolygonShape.Set_s_v);
var c = b2Vec2.CrossVV(r, v);
if (c < 0) {
ie = j;
}
// Collinearity check
if (c === 0 && v.LengthSquared() > r.LengthSquared()) {
ie = j;
}
}
++m;
ih = ie;
if (ie === i0) {
break;
}
}
this.m_count = m;
this.m_vertices = b2Vec2.MakeArray(this.m_count);
this.m_normals = b2Vec2.MakeArray(this.m_count);
// Copy vertices.
for (var i = 0; i < m; ++i) {
this.m_vertices[i].Copy(ps[hull[i]]);
}
// Compute normals. Ensure the edges have non-zero length.
for (var i = 0; i < m; ++i) {
var vertexi1 = this.m_vertices[i];
var vertexi2 = this.m_vertices[(i + 1) % m];
var edge = b2Vec2.SubVV(vertexi2, vertexi1, b2Vec2.s_t0); // edge uses s_t0
// DEBUG: b2Assert(edge.LengthSquared() > b2_epsilon_sq);
b2Vec2.CrossVOne(edge, this.m_normals[i]).SelfNormalize();
}
// Compute the polygon centroid.
b2PolygonShape.ComputeCentroid(this.m_vertices, m, this.m_centroid);
return this;
};
b2PolygonShape.prototype.SetAsArray = function (vertices, count) {
if (count === void 0) { count = vertices.length; }
return this.Set(vertices, count);
};
/// Build vertices to represent an axis-aligned box or an oriented box.
/// @param hx the half-width.
/// @param hy the half-height.
/// @param center the center of the box in local coordinates.
/// @param angle the rotation of the box in local coordinates.
b2PolygonShape.prototype.SetAsBox = function (hx, hy, center, angle) {
if (angle === void 0) { angle = 0; }
this.m_count = 4;
this.m_vertices = b2Vec2.MakeArray(this.m_count);
this.m_normals = b2Vec2.MakeArray(this.m_count);
this.m_vertices[0].Set((-hx), (-hy));
this.m_vertices[1].Set(hx, (-hy));
this.m_vertices[2].Set(hx, hy);
this.m_vertices[3].Set((-hx), hy);
this.m_normals[0].Set(0, (-1));
this.m_normals[1].Set(1, 0);
this.m_normals[2].Set(0, 1);
this.m_normals[3].Set((-1), 0);
this.m_centroid.SetZero();
if (center) {
this.m_centroid.Copy(center);
var xf = new b2Transform();
xf.SetPosition(center);
xf.SetRotationAngle(angle);
// Transform vertices and normals.
for (var i = 0; i < this.m_count; ++i) {
b2Transform.MulXV(xf, this.m_vertices[i], this.m_vertices[i]);
b2Rot.MulRV(xf.q, this.m_normals[i], this.m_normals[i]);
}
}
return this;
};
b2PolygonShape.prototype.TestPoint = function (xf, p) {
var pLocal = b2Transform.MulTXV(xf, p, b2PolygonShape.TestPoint_s_pLocal);
for (var i = 0; i < this.m_count; ++i) {
var dot = b2Vec2.DotVV(this.m_normals[i], b2Vec2.SubVV(pLocal, this.m_vertices[i], b2Vec2.s_t0));
if (dot > 0) {
return false;
}
}
return true;
};
b2PolygonShape.prototype.ComputeDistance = function (xf, p, normal, childIndex) {
var pLocal = b2Transform.MulTXV(xf, p, b2PolygonShape.ComputeDistance_s_pLocal);
var maxDistance = -b2_maxFloat;
var normalForMaxDistance = b2PolygonShape.ComputeDistance_s_normalForMaxDistance.Copy(pLocal);
for (var i = 0; i < this.m_count; ++i) {
var dot = b2Vec2.DotVV(this.m_normals[i], b2Vec2.SubVV(pLocal, this.m_vertices[i], b2Vec2.s_t0));
if (dot > maxDistance) {
maxDistance = dot;
normalForMaxDistance.Copy(this.m_normals[i]);
}
}
if (maxDistance > 0) {
var minDistance = b2PolygonShape.ComputeDistance_s_minDistance.Copy(normalForMaxDistance);
var minDistance2 = maxDistance * maxDistance;
for (var i = 0; i < this.m_count; ++i) {
var distance = b2Vec2.SubVV(pLocal, this.m_vertices[i], b2PolygonShape.ComputeDistance_s_distance);
var distance2 = distance.LengthSquared();
if (minDistance2 > distance2) {
minDistance.Copy(distance);
minDistance2 = distance2;
}
}
b2Rot.MulRV(xf.q, minDistance, normal);
normal.Normalize();
return Math.sqrt(minDistance2);
}
else {
b2Rot.MulRV(xf.q, normalForMaxDistance, normal);
return maxDistance;
}
};
b2PolygonShape.prototype.RayCast = function (output, input, xf, childIndex) {
// Put the ray into the polygon's frame of reference.
var p1 = b2Transform.MulTXV(xf, input.p1, b2PolygonShape.RayCast_s_p1);
var p2 = b2Transform.MulTXV(xf, input.p2, b2PolygonShape.RayCast_s_p2);
var d = b2Vec2.SubVV(p2, p1, b2PolygonShape.RayCast_s_d);
var lower = 0, upper = input.maxFraction;
var index = -1;
for (var i = 0; i < this.m_count; ++i) {
// p = p1 + a * d
// dot(normal, p - v) = 0
// dot(normal, p1 - v) + a * dot(normal, d) = 0
var numerator = b2Vec2.DotVV(this.m_normals[i], b2Vec2.SubVV(this.m_vertices[i], p1, b2Vec2.s_t0));
var denominator = b2Vec2.DotVV(this.m_normals[i], d);
if (denominator === 0) {
if (numerator < 0) {
return false;
}
}
else {
// Note: we want this predicate without division:
// lower < numerator / denominator, where denominator < 0
// Since denominator < 0, we have to flip the inequality:
// lower < numerator / denominator <==> denominator * lower > numerator.
if (denominator < 0 && numerator < lower * denominator) {
// Increase lower.
// The segment enters this half-space.
lower = numerator / denominator;
index = i;
}
else if (denominator > 0 && numerator < upper * denominator) {
// Decrease upper.
// The segment exits this half-space.
upper = numerator / denominator;
}
}
// The use of epsilon here causes the assert on lower to trip
// in some cases. Apparently the use of epsilon was to make edge
// shapes work, but now those are handled separately.
// if (upper < lower - b2_epsilon)
if (upper < lower) {
return false;
}
}
// DEBUG: b2Assert(0 <= lower && lower <= input.maxFraction);
if (index >= 0) {
output.fraction = lower;
b2Rot.MulRV(xf.q, this.m_normals[index], output.normal);
return true;
}
return false;
};
b2PolygonShape.prototype.ComputeAABB = function (aabb, xf, childIndex) {
var lower = b2Transform.MulXV(xf, this.m_vertices[0], aabb.lowerBound);
var upper = aabb.upperBound.Copy(lower);
for (var i = 0; i < this.m_count; ++i) {
var v = b2Transform.MulXV(xf, this.m_vertices[i], b2PolygonShape.ComputeAABB_s_v);
b2Vec2.MinV(v, lower, lower);
b2Vec2.MaxV(v, upper, upper);
}
var r = this.m_radius;
lower.SelfSubXY(r, r);
upper.SelfAddXY(r, r);
};
b2PolygonShape.prototype.ComputeMass = function (massData, density) {
// Polygon mass, centroid, and inertia.
// Let rho be the polygon density in mass per unit area.
// Then:
// mass = rho * int(dA)
// centroid.x = (1/mass) * rho * int(x * dA)
// centroid.y = (1/mass) * rho * int(y * dA)
// I = rho * int((x*x + y*y) * dA)
//
// We can compute these integrals by summing all the integrals
// for each triangle of the polygon. To evaluate the integral
// for a single triangle, we make a change of variables to
// the (u,v) coordinates of the triangle:
// x = x0 + e1x * u + e2x * v
// y = y0 + e1y * u + e2y * v
// where 0 <= u && 0 <= v && u + v <= 1.
//
// We integrate u from [0,1-v] and then v from [0,1].
// We also need to use the Jacobian of the transformation:
// D = cross(e1, e2)
//
// Simplification: triangle centroid = (1/3) * (p1 + p2 + p3)
//
// The rest of the derivation is handled by computer algebra.
// DEBUG: b2Assert(this.m_count >= 3);
var center = b2PolygonShape.ComputeMass_s_center.SetZero();
var area = 0;
var I = 0;
// s is the reference point for forming triangles.
// It's location doesn't change the result (except for rounding error).
var s = b2PolygonShape.ComputeMass_s_s.SetZero();
// This code would put the reference point inside the polygon.
for (var i = 0; i < this.m_count; ++i) {
s.SelfAdd(this.m_vertices[i]);
}
s.SelfMul(1 / this.m_count);
var k_inv3 = 1 / 3;
for (var i = 0; i < this.m_count; ++i) {
// Triangle vertices.
var e1 = b2Vec2.SubVV(this.m_vertices[i], s, b2PolygonShape.ComputeMass_s_e1);
var e2 = b2Vec2.SubVV(this.m_vertices[(i + 1) % this.m_count], s, b2PolygonShape.ComputeMass_s_e2);
var D = b2Vec2.CrossVV(e1, e2);
var triangleArea = 0.5 * D;
area += triangleArea;
// Area weighted centroid
center.SelfAdd(b2Vec2.MulSV(triangleArea * k_inv3, b2Vec2.AddVV(e1, e2, b2Vec2.s_t0), b2Vec2.s_t1));
var ex1 = e1.x;
var ey1 = e1.y;
var ex2 = e2.x;
var ey2 = e2.y;
var intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2;
var inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2;
I += (0.25 * k_inv3 * D) * (intx2 + inty2);
}
// Total mass
massData.mass = density * area;
// Center of mass
// DEBUG: b2Assert(area > b2_epsilon);
center.SelfMul(1 / area);
b2Vec2.AddVV(center, s, massData.center);
// Inertia tensor relative to the local origin (point s).
massData.I = density * I;
// Shift to center of mass then to original body origin.
massData.I += massData.mass * (b2Vec2.DotVV(massData.center, massData.center) - b2Vec2.DotVV(center, center));
};
b2PolygonShape.prototype.Validate = function () {
for (var i = 0; i < this.m_count; ++i) {
var i1 = i;
var i2 = (i + 1) % this.m_count;
var p = this.m_vertices[i1];
var e = b2Vec2.SubVV(this.m_vertices[i2], p, b2PolygonShape.Validate_s_e);
for (var j = 0; j < this.m_count; ++j) {
if (j === i1 || j === i2) {
continue;
}
var v = b2Vec2.SubVV(this.m_vertices[j], p, b2PolygonShape.Validate_s_v);
var c = b2Vec2.CrossVV(e, v);
if (c < 0) {
return false;
}
}
}
return true;
};
b2PolygonShape.prototype.SetupDistanceProxy = function (proxy, index) {
proxy.m_vertices = this.m_vertices;
proxy.m_count = this.m_count;
proxy.m_radius = this.m_radius;
};
b2PolygonShape.prototype.ComputeSubmergedArea = function (normal, offset, xf, c) {
// Transform plane into shape co-ordinates
var normalL = b2Rot.MulTRV(xf.q, normal, b2PolygonShape.ComputeSubmergedArea_s_normalL);
var offsetL = offset - b2Vec2.DotVV(normal, xf.p);
var depths = b2PolygonShape.ComputeSubmergedArea_s_depths;
var diveCount = 0;
var intoIndex = -1;
var outoIndex = -1;
var lastSubmerged = false;
for (var i_1 = 0; i_1 < this.m_count; ++i_1) {
depths[i_1] = b2Vec2.DotVV(normalL, this.m_vertices[i_1]) - offsetL;
var isSubmerged = depths[i_1] < (-b2_epsilon);
if (i_1 > 0) {
if (isSubmerged) {
if (!lastSubmerged) {
intoIndex = i_1 - 1;
diveCount++;
}
}
else {
if (lastSubmerged) {
outoIndex = i_1 - 1;
diveCount++;
}
}
}
lastSubmerged = isSubmerged;
}
switch (diveCount) {
case 0:
if (lastSubmerged) {
// Completely submerged
var md = b2PolygonShape.ComputeSubmergedArea_s_md;
this.ComputeMass(md, 1);
b2Transform.MulXV(xf, md.center, c);
return md.mass;
}
else {
// Completely dry
return 0;
}
case 1:
if (intoIndex === (-1)) {
intoIndex = this.m_count - 1;
}
else {
outoIndex = this.m_count - 1;
}
break;
}
var intoIndex2 = ((intoIndex + 1) % this.m_count);
var outoIndex2 = ((outoIndex + 1) % this.m_count);
var intoLamdda = (0 - depths[intoIndex]) / (depths[intoIndex2] - depths[intoIndex]);
var outoLamdda = (0 - depths[outoIndex]) / (depths[outoIndex2] - depths[outoIndex]);
var intoVec = b2PolygonShape.ComputeSubmergedArea_s_intoVec.Set(this.m_vertices[intoIndex].x * (1 - intoLamdda) + this.m_vertices[intoIndex2].x * intoLamdda, this.m_vertices[intoIndex].y * (1 - intoLamdda) + this.m_vertices[intoIndex2].y * intoLamdda);
var outoVec = b2PolygonShape.ComputeSubmergedArea_s_outoVec.Set(this.m_vertices[outoIndex].x * (1 - outoLamdda) + this.m_vertices[outoIndex2].x * outoLamdda, this.m_vertices[outoIndex].y * (1 - outoLamdda) + this.m_vertices[outoIndex2].y * outoLamdda);
// Initialize accumulator
var area = 0;
var center = b2PolygonShape.ComputeSubmergedArea_s_center.SetZero();
var p2 = this.m_vertices[intoIndex2];
var p3;
// An awkward loop from intoIndex2+1 to outIndex2
var i = intoIndex2;
while (i !== outoIndex2) {
i = (i + 1) % this.m_count;
if (i === outoIndex2) {
p3 = outoVec;
}
else {
p3 = this.m_vertices[i];
}
var triangleArea = 0.5 * ((p2.x - intoVec.x) * (p3.y - intoVec.y) - (p2.y - intoVec.y) * (p3.x - intoVec.x));
area += triangleArea;
// Area weighted centroid
center.x += triangleArea * (intoVec.x + p2.x + p3.x) / 3;
center.y += triangleArea * (intoVec.y + p2.y + p3.y) / 3;
p2 = p3;
}
// Normalize and transform centroid
center.SelfMul(1 / area);
b2Transform.MulXV(xf, center, c);
return area;
};
b2PolygonShape.prototype.Dump = function (log) {
log(" const shape: b2PolygonShape = new b2PolygonShape();\n");
log(" const vs: b2Vec2[] = b2Vec2.MakeArray(%d);\n", b2_maxPolygonVertices);
for (var i = 0; i < this.m_count; ++i) {
log(" vs[%d].Set(%.15f, %.15f);\n", i, this.m_vertices[i].x, this.m_vertices[i].y);
}
log(" shape.Set(vs, %d);\n", this.m_count);
};
b2PolygonShape.ComputeCentroid = function (vs, count, out) {
// DEBUG: b2Assert(count >= 3);
var c = out;
c.SetZero();
var area = 0;
// s is the reference point for forming triangles.
// It's location doesn't change the result (except for rounding error).
var pRef = b2PolygonShape.ComputeCentroid_s_pRef.SetZero();
/*
#if 0
// This code would put the reference point inside the polygon.
for (let i: number = 0; i < count; ++i) {
pRef.SelfAdd(vs[i]);
}
pRef.SelfMul(1 / count);
#endif
*/
var inv3 = 1 / 3;
for (var i = 0; i < count; ++i) {
// Triangle vertices.
var p1 = pRef;
var p2 = vs[i];
var p3 = vs[(i + 1) % count];
var e1 = b2Vec2.SubVV(p2, p1, b2PolygonShape.ComputeCentroid_s_e1);
var e2 = b2Vec2.SubVV(p3, p1, b2PolygonShape.ComputeCentroid_s_e2);
var D = b2Vec2.CrossVV(e1, e2);
var triangleArea = 0.5 * D;
area += triangleArea;
// Area weighted centroid
c.x += triangleArea * inv3 * (p1.x + p2.x + p3.x);
c.y += triangleArea * inv3 * (p1.y + p2.y + p3.y);
}
// Centroid
// DEBUG: b2Assert(area > b2_epsilon);
c.SelfMul(1 / area);
return c;
};
/// Create a convex hull from the given array of points.
/// The count must be in the range [3, b2_maxPolygonVertices].
/// @warning the points may be re-ordered, even if they form a convex polygon
/// @warning collinear points are handled but not removed. Collinear points
/// may lead to poor stacking behavior.
b2PolygonShape.Set_s_ps = b2Vec2.MakeArray(b2_maxPolygonVertices);
b2PolygonShape.Set_s_hull = b2MakeNumberArray(b2_maxPolygonVertices);
b2PolygonShape.Set_s_r = new b2Vec2();
b2PolygonShape.Set_s_v = new b2Vec2();
/// @see b2Shape::TestPoint
b2PolygonShape.TestPoint_s_pLocal = new b2Vec2();
// #if B2_ENABLE_PARTICLE
/// @see b2Shape::ComputeDistance
b2PolygonShape.ComputeDistance_s_pLocal = new b2Vec2();
b2PolygonShape.ComputeDistance_s_normalForMaxDistance = new b2Vec2();
b2PolygonShape.ComputeDistance_s_minDistance = new b2Vec2();
b2PolygonShape.ComputeDistance_s_distance = new b2Vec2();
// #endif
/// Implement b2Shape.
b2PolygonShape.RayCast_s_p1 = new b2Vec2();
b2PolygonShape.RayCast_s_p2 = new b2Vec2();
b2PolygonShape.RayCast_s_d = new b2Vec2();
/// @see b2Shape::ComputeAABB
b2PolygonShape.ComputeAABB_s_v = new b2Vec2();
/// @see b2Shape::ComputeMass
b2PolygonShape.ComputeMass_s_center = new b2Vec2();
b2PolygonShape.ComputeMass_s_s = new b2Vec2();
b2PolygonShape.ComputeMass_s_e1 = new b2Vec2();
b2PolygonShape.ComputeMass_s_e2 = new b2Vec2();
b2PolygonShape.Validate_s_e = new b2Vec2();
b2PolygonShape.Validate_s_v = new b2Vec2();
b2PolygonShape.ComputeSubmergedArea_s_normalL = new b2Vec2();
b2PolygonShape.ComputeSubmergedArea_s_depths = b2MakeNumberArray(b2_maxPolygonVertices);
b2PolygonShape.ComputeSubmergedArea_s_md = new b2MassData();
b2PolygonShape.ComputeSubmergedArea_s_intoVec = new b2Vec2();
b2PolygonShape.ComputeSubmergedArea_s_outoVec = new b2Vec2();
b2PolygonShape.ComputeSubmergedArea_s_center = new b2Vec2();
b2PolygonShape.ComputeCentroid_s_pRef = new b2Vec2();
b2PolygonShape.ComputeCentroid_s_e1 = new b2Vec2();
b2PolygonShape.ComputeCentroid_s_e2 = new b2Vec2();
return b2PolygonShape;
}(b2Shape));
/*
* Copyright (c) 2006-2010 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.
*/
/// A line segment (edge) shape. These can be connected in chains or loops
/// to other edge shapes. The connectivity information is used to ensure
/// correct contact normals.
var b2EdgeShape = /** @class */ (function (_super) {
__extends(b2EdgeShape, _super);
function b2EdgeShape() {
var _this = _super.call(this, exports.b2ShapeType.e_edgeShape, b2_polygonRadius) || this;
_this.m_vertex1 = new b2Vec2();
_this.m_vertex2 = new b2Vec2();
_this.m_vertex0 = new b2Vec2();
_this.m_vertex3 = new b2Vec2();
_this.m_hasVertex0 = false;
_this.m_hasVertex3 = false;
return _this;
}
/// Set this as an isolated edge.
b2EdgeShape.prototype.Set = function (v1, v2) {
this.m_vertex1.Copy(v1);
this.m_vertex2.Copy(v2);
this.m_hasVertex0 = false;
this.m_hasVertex3 = false;
return this;
};
/// Implement b2Shape.
b2EdgeShape.prototype.Clone = function () {
return new b2EdgeShape().Copy(this);
};
b2EdgeShape.prototype.Copy = function (other) {
_super.prototype.Copy.call(this, other);
// DEBUG: b2Assert(other instanceof b2EdgeShape);
this.m_vertex1.Copy(other.m_vertex1);
this.m_vertex2.Copy(other.m_vertex2);
this.m_vertex0.Copy(other.m_vertex0);
this.m_vertex3.Copy(other.m_vertex3);
this.m_hasVertex0 = other.m_hasVertex0;
this.m_hasVertex3 = other.m_hasVertex3;
return this;
};
/// @see b2Shape::GetChildCount
b2EdgeShape.prototype.GetChildCount = function () {
return 1;
};
/// @see b2Shape::TestPoint
b2EdgeShape.prototype.TestPoint = function (xf, p) {
return false;
};
b2EdgeShape.prototype.ComputeDistance = function (xf, p, normal, childIndex) {
var v1 = b2Transform.MulXV(xf, this.m_vertex1, b2EdgeShape.ComputeDistance_s_v1);
var v2 = b2Transform.MulXV(xf, this.m_vertex2, b2EdgeShape.ComputeDistance_s_v2);
var d = b2Vec2.SubVV(p, v1, b2EdgeShape.ComputeDistance_s_d);
var s = b2Vec2.SubVV(v2, v1, b2EdgeShape.ComputeDistance_s_s);
var ds = b2Vec2.DotVV(d, s);
if (ds > 0) {
var s2 = b2Vec2.DotVV(s, s);
if (ds > s2) {
b2Vec2.SubVV(p, v2, d);
}
else {
d.SelfMulSub(ds / s2, s);
}
}
normal.Copy(d);
return normal.Normalize();
};
b2EdgeShape.prototype.RayCast = function (output, input, xf, childIndex) {
// Put the ray into the edge's frame of reference.
var p1 = b2Transform.MulTXV(xf, input.p1, b2EdgeShape.RayCast_s_p1);
var p2 = b2Transform.MulTXV(xf, input.p2, b2EdgeShape.RayCast_s_p2);
var d = b2Vec2.SubVV(p2, p1, b2EdgeShape.RayCast_s_d);
var v1 = this.m_vertex1;
var v2 = this.m_vertex2;
var e = b2Vec2.SubVV(v2, v1, b2EdgeShape.RayCast_s_e);
var normal = output.normal.Set(e.y, -e.x).SelfNormalize();
// q = p1 + t * d
// dot(normal, q - v1) = 0
// dot(normal, p1 - v1) + t * dot(normal, d) = 0
var numerator = b2Vec2.DotVV(normal, b2Vec2.SubVV(v1, p1, b2Vec2.s_t0));
var denominator = b2Vec2.DotVV(normal, d);
if (denominator === 0) {
return false;
}
var t = numerator / denominator;
if (t < 0 || input.maxFraction < t) {
return false;
}
var q = b2Vec2.AddVMulSV(p1, t, d, b2EdgeShape.RayCast_s_q);
// q = v1 + s * r
// s = dot(q - v1, r) / dot(r, r)
var r = b2Vec2.SubVV(v2, v1, b2EdgeShape.RayCast_s_r);
var rr = b2Vec2.DotVV(r, r);
if (rr === 0) {
return false;
}
var s = b2Vec2.DotVV(b2Vec2.SubVV(q, v1, b2Vec2.s_t0), r) / rr;
if (s < 0 || 1 < s) {
return false;
}
output.fraction = t;
b2Rot.MulRV(xf.q, output.normal, output.normal);
if (numerator > 0) {
output.normal.SelfNeg();
}
return true;
};
b2EdgeShape.prototype.ComputeAABB = function (aabb, xf, childIndex) {
var v1 = b2Transform.MulXV(xf, this.m_vertex1, b2EdgeShape.ComputeAABB_s_v1);
var v2 = b2Transform.MulXV(xf, this.m_vertex2, b2EdgeShape.ComputeAABB_s_v2);
b2Vec2.MinV(v1, v2, aabb.lowerBound);
b2Vec2.MaxV(v1, v2, aabb.upperBound);
var r = this.m_radius;
aabb.lowerBound.SelfSubXY(r, r);
aabb.upperBound.SelfAddXY(r, r);
};
/// @see b2Shape::ComputeMass
b2EdgeShape.prototype.ComputeMass = function (massData, density) {
massData.mass = 0;
b2Vec2.MidVV(this.m_vertex1, this.m_vertex2, massData.center);
massData.I = 0;
};
b2EdgeShape.prototype.SetupDistanceProxy = function (proxy, index) {
proxy.m_vertices = proxy.m_buffer;
proxy.m_vertices[0].Copy(this.m_vertex1);
proxy.m_vertices[1].Copy(this.m_vertex2);
proxy.m_count = 2;
proxy.m_radius = this.m_radius;
};
b2EdgeShape.prototype.ComputeSubmergedArea = function (normal, offset, xf, c) {
c.SetZero();
return 0;
};
b2EdgeShape.prototype.Dump = function (log) {
log(" const shape: b2EdgeShape = new b2EdgeShape();\n");
log(" shape.m_radius = %.15f;\n", this.m_radius);
log(" shape.m_vertex0.Set(%.15f, %.15f);\n", this.m_vertex0.x, this.m_vertex0.y);
log(" shape.m_vertex1.Set(%.15f, %.15f);\n", this.m_vertex1.x, this.m_vertex1.y);
log(" shape.m_vertex2.Set(%.15f, %.15f);\n", this.m_vertex2.x, this.m_vertex2.y);
log(" shape.m_vertex3.Set(%.15f, %.15f);\n", this.m_vertex3.x, this.m_vertex3.y);
log(" shape.m_hasVertex0 = %s;\n", this.m_hasVertex0);
log(" shape.m_hasVertex3 = %s;\n", this.m_hasVertex3);
};
// #if B2_ENABLE_PARTICLE
/// @see b2Shape::ComputeDistance
b2EdgeShape.ComputeDistance_s_v1 = new b2Vec2();
b2EdgeShape.ComputeDistance_s_v2 = new b2Vec2();
b2EdgeShape.ComputeDistance_s_d = new b2Vec2();
b2EdgeShape.ComputeDistance_s_s = new b2Vec2();
// #endif
/// Implement b2Shape.
// p = p1 + t * d
// v = v1 + s * e
// p1 + t * d = v1 + s * e
// s * e - t * d = p1 - v1
b2EdgeShape.RayCast_s_p1 = new b2Vec2();
b2EdgeShape.RayCast_s_p2 = new b2Vec2();
b2EdgeShape.RayCast_s_d = new b2Vec2();
b2EdgeShape.RayCast_s_e = new b2Vec2();
b2EdgeShape.RayCast_s_q = new b2Vec2();
b2EdgeShape.RayCast_s_r = new b2Vec2();
/// @see b2Shape::ComputeAABB
b2EdgeShape.ComputeAABB_s_v1 = new b2Vec2();
b2EdgeShape.ComputeAABB_s_v2 = new b2Vec2();
return b2EdgeShape;
}(b2Shape));
/*
* Copyright (c) 2006-2010 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.
*/
/// A chain shape is a free form sequence of line segments.
/// The chain has two-sided collision, so you can use inside and outside collision.
/// Therefore, you may use any winding order.
/// Since there may be many vertices, they are allocated using b2Alloc.
/// Connectivity information is used to create smooth collisions.
/// WARNING: The chain will not collide properly if there are self-intersections.
var b2ChainShape = /** @class */ (function (_super) {
__extends(b2ChainShape, _super);
function b2ChainShape() {
var _this = _super.call(this, exports.b2ShapeType.e_chainShape, b2_polygonRadius) || this;
_this.m_vertices = [];
_this.m_count = 0;
_this.m_prevVertex = new b2Vec2();
_this.m_nextVertex = new b2Vec2();
_this.m_hasPrevVertex = false;
_this.m_hasNextVertex = false;
return _this;
}
/// Create a loop. This automatically adjusts connectivity.
/// @param vertices an array of vertices, these are copied
/// @param count the vertex count
b2ChainShape.prototype.CreateLoop = function (vertices, count, start) {
if (count === void 0) { count = vertices.length; }
if (start === void 0) { start = 0; }
// DEBUG: b2Assert(count >= 3);
if (count < 3) {
return this;
}
// DEBUG: for (let i: number = 1; i < count; ++i) {
// DEBUG: const v1 = vertices[start + i - 1];
// DEBUG: const v2 = vertices[start + i];
// DEBUG: // If the code crashes here, it means your vertices are too close together.
// DEBUG: b2Assert(b2Vec2.DistanceSquaredVV(v1, v2) > b2_linearSlop * b2_linearSlop);
// DEBUG: }
this.m_count = count + 1;
this.m_vertices = b2Vec2.MakeArray(this.m_count);
for (var i = 0; i < count; ++i) {
this.m_vertices[i].Copy(vertices[start + i]);
}
this.m_vertices[count].Copy(this.m_vertices[0]);
this.m_prevVertex.Copy(this.m_vertices[this.m_count - 2]);
this.m_nextVertex.Copy(this.m_vertices[1]);
this.m_hasPrevVertex = true;
this.m_hasNextVertex = true;
return this;
};
/// Create a chain with isolated end vertices.
/// @param vertices an array of vertices, these are copied
/// @param count the vertex count
b2ChainShape.prototype.CreateChain = function (vertices, count, start) {
// DEBUG: b2Assert(count >= 2);
// DEBUG: for (let i: number = 1; i < count; ++i) {
// DEBUG: const v1 = vertices[start + i - 1];
// DEBUG: const v2 = vertices[start + i];
// DEBUG: // If the code crashes here, it means your vertices are too close together.
// DEBUG: b2Assert(b2Vec2.DistanceSquaredVV(v1, v2) > b2_linearSlop * b2_linearSlop);
// DEBUG: }
if (count === void 0) { count = vertices.length; }
if (start === void 0) { start = 0; }
this.m_count = count;
this.m_vertices = b2Vec2.MakeArray(count);
for (var i = 0; i < count; ++i) {
this.m_vertices[i].Copy(vertices[start + i]);
}
this.m_hasPrevVertex = false;
this.m_hasNextVertex = false;
this.m_prevVertex.SetZero();
this.m_nextVertex.SetZero();
return this;
};
/// Establish connectivity to a vertex that precedes the first vertex.
/// Don't call this for loops.
b2ChainShape.prototype.SetPrevVertex = function (prevVertex) {
this.m_prevVertex.Copy(prevVertex);
this.m_hasPrevVertex = true;
return this;
};
/// Establish connectivity to a vertex that follows the last vertex.
/// Don't call this for loops.
b2ChainShape.prototype.SetNextVertex = function (nextVertex) {
this.m_nextVertex.Copy(nextVertex);
this.m_hasNextVertex = true;
return this;
};
/// Implement b2Shape. Vertices are cloned using b2Alloc.
b2ChainShape.prototype.Clone = function () {
return new b2ChainShape().Copy(this);
};
b2ChainShape.prototype.Copy = function (other) {
_super.prototype.Copy.call(this, other);
// DEBUG: b2Assert(other instanceof b2ChainShape);
this.CreateChain(other.m_vertices, other.m_count);
this.m_prevVertex.Copy(other.m_prevVertex);
this.m_nextVertex.Copy(other.m_nextVertex);
this.m_hasPrevVertex = other.m_hasPrevVertex;
this.m_hasNextVertex = other.m_hasNextVertex;
return this;
};
/// @see b2Shape::GetChildCount
b2ChainShape.prototype.GetChildCount = function () {
// edge count = vertex count - 1
return this.m_count - 1;
};
/// Get a child edge.
b2ChainShape.prototype.GetChildEdge = function (edge, index) {
// DEBUG: b2Assert(0 <= index && index < this.m_count - 1);
edge.m_type = exports.b2ShapeType.e_edgeShape;
edge.m_radius = this.m_radius;
edge.m_vertex1.Copy(this.m_vertices[index]);
edge.m_vertex2.Copy(this.m_vertices[index + 1]);
if (index > 0) {
edge.m_vertex0.Copy(this.m_vertices[index - 1]);
edge.m_hasVertex0 = true;
}
else {
edge.m_vertex0.Copy(this.m_prevVertex);
edge.m_hasVertex0 = this.m_hasPrevVertex;
}
if (index < this.m_count - 2) {
edge.m_vertex3.Copy(this.m_vertices[index + 2]);
edge.m_hasVertex3 = true;
}
else {
edge.m_vertex3.Copy(this.m_nextVertex);
edge.m_hasVertex3 = this.m_hasNextVertex;
}
};
/// This always return false.
/// @see b2Shape::TestPoint
b2ChainShape.prototype.TestPoint = function (xf, p) {
return false;
};
b2ChainShape.prototype.ComputeDistance = function (xf, p, normal, childIndex) {
var edge = b2ChainShape.ComputeDistance_s_edgeShape;
this.GetChildEdge(edge, childIndex);
return edge.ComputeDistance(xf, p, normal, 0);
};
b2ChainShape.prototype.RayCast = function (output, input, xf, childIndex) {
// DEBUG: b2Assert(childIndex < this.m_count);
var edgeShape = b2ChainShape.RayCast_s_edgeShape;
edgeShape.m_vertex1.Copy(this.m_vertices[childIndex]);
edgeShape.m_vertex2.Copy(this.m_vertices[(childIndex + 1) % this.m_count]);
return edgeShape.RayCast(output, input, xf, 0);
};
b2ChainShape.prototype.ComputeAABB = function (aabb, xf, childIndex) {
// DEBUG: b2Assert(childIndex < this.m_count);
var vertexi1 = this.m_vertices[childIndex];
var vertexi2 = this.m_vertices[(childIndex + 1) % this.m_count];
var v1 = b2Transform.MulXV(xf, vertexi1, b2ChainShape.ComputeAABB_s_v1);
var v2 = b2Transform.MulXV(xf, vertexi2, b2ChainShape.ComputeAABB_s_v2);
b2Vec2.MinV(v1, v2, aabb.lowerBound);
b2Vec2.MaxV(v1, v2, aabb.upperBound);
};
/// Chains have zero mass.
/// @see b2Shape::ComputeMass
b2ChainShape.prototype.ComputeMass = function (massData, density) {
massData.mass = 0;
massData.center.SetZero();
massData.I = 0;
};
b2ChainShape.prototype.SetupDistanceProxy = function (proxy, index) {
// DEBUG: b2Assert(0 <= index && index < this.m_count);
proxy.m_vertices = proxy.m_buffer;
proxy.m_vertices[0].Copy(this.m_vertices[index]);
if (index + 1 < this.m_count) {
proxy.m_vertices[1].Copy(this.m_vertices[index + 1]);
}
else {
proxy.m_vertices[1].Copy(this.m_vertices[0]);
}
proxy.m_count = 2;
proxy.m_radius = this.m_radius;
};
b2ChainShape.prototype.ComputeSubmergedArea = function (normal, offset, xf, c) {
c.SetZero();
return 0;
};
b2ChainShape.prototype.Dump = function (log) {
log(" const shape: b2ChainShape = new b2ChainShape();\n");
log(" const vs: b2Vec2[] = b2Vec2.MakeArray(%d);\n", b2_maxPolygonVertices);
for (var i = 0; i < this.m_count; ++i) {
log(" vs[%d].Set(%.15f, %.15f);\n", i, this.m_vertices[i].x, this.m_vertices[i].y);
}
log(" shape.CreateChain(vs, %d);\n", this.m_count);
log(" shape.m_prevVertex.Set(%.15f, %.15f);\n", this.m_prevVertex.x, this.m_prevVertex.y);
log(" shape.m_nextVertex.Set(%.15f, %.15f);\n", this.m_nextVertex.x, this.m_nextVertex.y);
log(" shape.m_hasPrevVertex = %s;\n", (this.m_hasPrevVertex) ? ("true") : ("false"));
log(" shape.m_hasNextVertex = %s;\n", (this.m_hasNextVertex) ? ("true") : ("false"));
};
// #if B2_ENABLE_PARTICLE
/// @see b2Shape::ComputeDistance
b2ChainShape.ComputeDistance_s_edgeShape = new b2EdgeShape();
// #endif
/// Implement b2Shape.
b2ChainShape.RayCast_s_edgeShape = new b2EdgeShape();
/// @see b2Shape::ComputeAABB
b2ChainShape.ComputeAABB_s_v1 = new b2Vec2();
b2ChainShape.ComputeAABB_s_v2 = new b2Vec2();
return b2ChainShape;
}(b2Shape));
/*
* Copyright (c) 2006-2009 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.
*/
/// This holds contact filtering data.
var b2Filter = /** @class */ (function () {
function b2Filter() {
/// The collision category bits. Normally you would just set one bit.
this.categoryBits = 0x0001;
/// The collision mask bits. This states the categories that this
/// shape would accept for collision.
this.maskBits = 0xFFFF;
/// Collision groups allow a certain group of objects to never collide (negative)
/// or always collide (positive). Zero means no collision group. Non-zero group
/// filtering always wins against the mask bits.
this.groupIndex = 0;
}
b2Filter.prototype.Clone = function () {
return new b2Filter().Copy(this);
};
b2Filter.prototype.Copy = function (other) {
// DEBUG: b2Assert(this !== other);
this.categoryBits = other.categoryBits;
this.maskBits = other.maskBits;
this.groupIndex = other.groupIndex || 0;
return this;
};
b2Filter.DEFAULT = new b2Filter();
return b2Filter;
}());
/// A fixture definition is used to create a fixture. This class defines an
/// abstract fixture definition. You can reuse fixture definitions safely.
var b2FixtureDef = /** @class */ (function () {
function b2FixtureDef() {
/// Use this to store application specific fixture data.
this.userData = null;
/// The friction coefficient, usually in the range [0,1].
this.friction = 0.2;
/// The restitution (elasticity) usually in the range [0,1].
this.restitution = 0;
/// The density, usually in kg/m^2.
this.density = 0;
/// A sensor shape collects contact information but never generates a collision
/// response.
this.isSensor = false;
/// Contact filtering data.
this.filter = new b2Filter();
}
return b2FixtureDef;
}());
/// This proxy is used internally to connect fixtures to the broad-phase.
var b2FixtureProxy = /** @class */ (function () {
function b2FixtureProxy(fixture) {
this.aabb = new b2AABB();
this.childIndex = 0;
this.fixture = fixture;
}
return b2FixtureProxy;
}());
/// A fixture is used to attach a shape to a body for collision detection. A fixture
/// inherits its transform from its parent. Fixtures hold additional non-geometric data
/// such as friction, collision filters, etc.
/// Fixtures are created via b2Body::CreateFixture.
/// @warning you cannot reuse fixtures.
var b2Fixture = /** @class */ (function () {
function b2Fixture(def, body) {
this.m_density = 0;
this.m_next = null;
this.m_friction = 0;
this.m_restitution = 0;
this.m_proxies = [];
this.m_proxyCount = 0;
this.m_filter = new b2Filter();
this.m_isSensor = false;
this.m_userData = null;
this.m_body = body;
this.m_shape = def.shape.Clone();
}
/// Get the type of the child shape. You can use this to down cast to the concrete shape.
/// @return the shape type.
b2Fixture.prototype.GetType = function () {
return this.m_shape.GetType();
};
/// Get the child shape. You can modify the child shape, however you should not change the
/// number of vertices because this will crash some collision caching mechanisms.
/// Manipulating the shape may lead to non-physical behavior.
b2Fixture.prototype.GetShape = function () {
return this.m_shape;
};
/// Set if this fixture is a sensor.
b2Fixture.prototype.SetSensor = function (sensor) {
if (sensor !== this.m_isSensor) {
this.m_body.SetAwake(true);
this.m_isSensor = sensor;
}
};
/// Is this fixture a sensor (non-solid)?
/// @return the true if the shape is a sensor.
b2Fixture.prototype.IsSensor = function () {
return this.m_isSensor;
};
/// Set the contact filtering data. This will not update contacts until the next time
/// step when either parent body is active and awake.
/// This automatically calls Refilter.
b2Fixture.prototype.SetFilterData = function (filter) {
this.m_filter.Copy(filter);
this.Refilter();
};
/// Get the contact filtering data.
b2Fixture.prototype.GetFilterData = function () {
return this.m_filter;
};
/// Call this if you want to establish collision that was previously disabled by b2ContactFilter::ShouldCollide.
b2Fixture.prototype.Refilter = function () {
// Flag associated contacts for filtering.
var edge = this.m_body.GetContactList();
while (edge) {
var contact = edge.contact;
var fixtureA = contact.GetFixtureA();
var fixtureB = contact.GetFixtureB();
if (fixtureA === this || fixtureB === this) {
contact.FlagForFiltering();
}
edge = edge.next;
}
var world = this.m_body.GetWorld();
if (world === null) {
return;
}
// Touch each proxy so that new pairs may be created
var broadPhase = world.m_contactManager.m_broadPhase;
for (var i = 0; i < this.m_proxyCount; ++i) {
broadPhase.TouchProxy(this.m_proxies[i].treeNode);
}
};
/// Get the parent body of this fixture. This is NULL if the fixture is not attached.
/// @return the parent body.
b2Fixture.prototype.GetBody = function () {
return this.m_body;
};
/// Get the next fixture in the parent body's fixture list.
/// @return the next shape.
b2Fixture.prototype.GetNext = function () {
return this.m_next;
};
/// Get the user data that was assigned in the fixture definition. Use this to
/// store your application specific data.
b2Fixture.prototype.GetUserData = function () {
return this.m_userData;
};
/// Set the user data. Use this to store your application specific data.
b2Fixture.prototype.SetUserData = function (data) {
this.m_userData = data;
};
/// Test a point for containment in this fixture.
/// @param p a point in world coordinates.
b2Fixture.prototype.TestPoint = function (p) {
return this.m_shape.TestPoint(this.m_body.GetTransform(), p);
};
// #if B2_ENABLE_PARTICLE
b2Fixture.prototype.ComputeDistance = function (p, normal, childIndex) {
return this.m_shape.ComputeDistance(this.m_body.GetTransform(), p, normal, childIndex);
};
// #endif
/// Cast a ray against this shape.
/// @param output the ray-cast results.
/// @param input the ray-cast input parameters.
b2Fixture.prototype.RayCast = function (output, input, childIndex) {
return this.m_shape.RayCast(output, input, this.m_body.GetTransform(), childIndex);
};
/// Get the mass data for this fixture. The mass data is based on the density and
/// the shape. The rotational inertia is about the shape's origin. This operation
/// may be expensive.
b2Fixture.prototype.GetMassData = function (massData) {
if (massData === void 0) { massData = new b2MassData(); }
this.m_shape.ComputeMass(massData, this.m_density);
return massData;
};
/// Set the density of this fixture. This will _not_ automatically adjust the mass
/// of the body. You must call b2Body::ResetMassData to update the body's mass.
b2Fixture.prototype.SetDensity = function (density) {
this.m_density = density;
};
/// Get the density of this fixture.
b2Fixture.prototype.GetDensity = function () {
return this.m_density;
};
/// Get the coefficient of friction.
b2Fixture.prototype.GetFriction = function () {
return this.m_friction;
};
/// Set the coefficient of friction. This will _not_ change the friction of
/// existing contacts.
b2Fixture.prototype.SetFriction = function (friction) {
this.m_friction = friction;
};
/// Get the coefficient of restitution.
b2Fixture.prototype.GetRestitution = function () {
return this.m_restitution;
};
/// Set the coefficient of restitution. This will _not_ change the restitution of
/// existing contacts.
b2Fixture.prototype.SetRestitution = function (restitution) {
this.m_restitution = restitution;
};
/// Get the fixture's AABB. This AABB may be enlarge and/or stale.
/// If you need a more accurate AABB, compute it using the shape and
/// the body transform.
b2Fixture.prototype.GetAABB = function (childIndex) {
// DEBUG: b2Assert(0 <= childIndex && childIndex < this.m_proxyCount);
return this.m_proxies[childIndex].aabb;
};
/// Dump this fixture to the log file.
b2Fixture.prototype.Dump = function (log, bodyIndex) {
log(" const fd: b2FixtureDef = new b2FixtureDef();\n");
log(" fd.friction = %.15f;\n", this.m_friction);
log(" fd.restitution = %.15f;\n", this.m_restitution);
log(" fd.density = %.15f;\n", this.m_density);
log(" fd.isSensor = %s;\n", (this.m_isSensor) ? ("true") : ("false"));
log(" fd.filter.categoryBits = %d;\n", this.m_filter.categoryBits);
log(" fd.filter.maskBits = %d;\n", this.m_filter.maskBits);
log(" fd.filter.groupIndex = %d;\n", this.m_filter.groupIndex);
this.m_shape.Dump(log);
log("\n");
log(" fd.shape = shape;\n");
log("\n");
log(" bodies[%d].CreateFixture(fd);\n", bodyIndex);
};
// We need separation create/destroy functions from the constructor/destructor because
// the destructor cannot access the allocator (no destructor arguments allowed by C++).
b2Fixture.prototype.Create = function (def) {
var _this = this;
this.m_userData = def.userData;
this.m_friction = b2Maybe(def.friction, 0.2);
this.m_restitution = b2Maybe(def.restitution, 0);
// this.m_body = body;
this.m_next = null;
this.m_filter.Copy(b2Maybe(def.filter, b2Filter.DEFAULT));
this.m_isSensor = b2Maybe(def.isSensor, false);
// Reserve proxy space
// const childCount = m_shape->GetChildCount();
// m_proxies = (b2FixtureProxy*)allocator->Allocate(childCount * sizeof(b2FixtureProxy));
// for (int32 i = 0; i < childCount; ++i)
// {
// m_proxies[i].fixture = NULL;
// m_proxies[i].proxyId = b2BroadPhase::e_nullProxy;
// }
// this.m_proxies = b2FixtureProxy.MakeArray(this.m_shape.GetChildCount());
this.m_proxies = b2MakeArray(this.m_shape.GetChildCount(), function (i) { return new b2FixtureProxy(_this); });
this.m_proxyCount = 0;
this.m_density = b2Maybe(def.density, 0);
};
b2Fixture.prototype.Destroy = function () {
// The proxies must be destroyed before calling this.
// DEBUG: b2Assert(this.m_proxyCount === 0);
// Free the proxy array.
// int32 childCount = m_shape->GetChildCount();
// allocator->Free(m_proxies, childCount * sizeof(b2FixtureProxy));
// m_proxies = NULL;
// this.m_shape = null;
};
// These support body activation/deactivation.
b2Fixture.prototype.CreateProxies = function (xf) {
var broadPhase = this.m_body.m_world.m_contactManager.m_broadPhase;
// DEBUG: b2Assert(this.m_proxyCount === 0);
// Create proxies in the broad-phase.
this.m_proxyCount = this.m_shape.GetChildCount();
for (var i = 0; i < this.m_proxyCount; ++i) {
var proxy = this.m_proxies[i] = new b2FixtureProxy(this);
this.m_shape.ComputeAABB(proxy.aabb, xf, i);
proxy.treeNode = broadPhase.CreateProxy(proxy.aabb, proxy);
proxy.childIndex = i;
}
};
b2Fixture.prototype.DestroyProxies = function () {
var broadPhase = this.m_body.m_world.m_contactManager.m_broadPhase;
// Destroy proxies in the broad-phase.
for (var i = 0; i < this.m_proxyCount; ++i) {
var proxy = this.m_proxies[i];
delete proxy.treeNode.userData;
broadPhase.DestroyProxy(proxy.treeNode);
delete proxy.treeNode;
}
this.m_proxyCount = 0;
};
b2Fixture.prototype.TouchProxies = function () {
var broadPhase = this.m_body.m_world.m_contactManager.m_broadPhase;
var proxyCount = this.m_proxyCount;
for (var i = 0; i < proxyCount; ++i) {
broadPhase.TouchProxy(this.m_proxies[i].treeNode);
}
};
b2Fixture.prototype.Synchronize = function (transform1, transform2) {
if (this.m_proxyCount === 0) {
return;
}
var broadPhase = this.m_body.m_world.m_contactManager.m_broadPhase;
for (var i = 0; i < this.m_proxyCount; ++i) {
var proxy = this.m_proxies[i];
// Compute an AABB that covers the swept shape (may miss some rotation effect).
var aabb1 = b2Fixture.Synchronize_s_aabb1;
var aabb2 = b2Fixture.Synchronize_s_aabb2;
this.m_shape.ComputeAABB(aabb1, transform1, i);
this.m_shape.ComputeAABB(aabb2, transform2, i);
proxy.aabb.Combine2(aabb1, aabb2);
var displacement = b2Vec2.SubVV(transform2.p, transform1.p, b2Fixture.Synchronize_s_displacement);
broadPhase.MoveProxy(proxy.treeNode, proxy.aabb, displacement);
}
};
b2Fixture.Synchronize_s_aabb1 = new b2AABB();
b2Fixture.Synchronize_s_aabb2 = new b2AABB();
b2Fixture.Synchronize_s_displacement = new b2Vec2();
return b2Fixture;
}());
/*
* 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 (b2BodyType) {
b2BodyType[b2BodyType["b2_unknown"] = -1] = "b2_unknown";
b2BodyType[b2BodyType["b2_staticBody"] = 0] = "b2_staticBody";
b2BodyType[b2BodyType["b2_kinematicBody"] = 1] = "b2_kinematicBody";
b2BodyType[b2BodyType["b2_dynamicBody"] = 2] = "b2_dynamicBody";
// TODO_ERIN
// b2_bulletBody = 3
})(exports.b2BodyType || (exports.b2BodyType = {}));
/// A body definition holds all the data needed to construct a rigid body.
/// You can safely re-use body definitions. Shapes are added to a body after construction.
var b2BodyDef = /** @class */ (function () {
function b2BodyDef() {
/// The body type: static, kinematic, or dynamic.
/// Note: if a dynamic body would have zero mass, the mass is set to one.
this.type = exports.b2BodyType.b2_staticBody;
/// The world position of the body. Avoid creating bodies at the origin
/// since this can lead to many overlapping shapes.
this.position = new b2Vec2(0, 0);
/// The world angle of the body in radians.
this.angle = 0;
/// The linear velocity of the body's origin in world co-ordinates.
this.linearVelocity = new b2Vec2(0, 0);
/// The angular velocity of the body.
this.angularVelocity = 0;
/// Linear damping is use to reduce the linear velocity. The damping parameter
/// can be larger than 1.0f but the damping effect becomes sensitive to the
/// time step when the damping parameter is large.
this.linearDamping = 0;
/// Angular damping is use to reduce the angular velocity. The damping parameter
/// can be larger than 1.0f but the damping effect becomes sensitive to the
/// time step when the damping parameter is large.
this.angularDamping = 0;
/// Set this flag to false if this body should never fall asleep. Note that
/// this increases CPU usage.
this.allowSleep = true;
/// Is this body initially awake or sleeping?
this.awake = true;
/// Should this body be prevented from rotating? Useful for characters.
this.fixedRotation = false;
/// Is this a fast moving body that should be prevented from tunneling through
/// other moving bodies? Note that all bodies are prevented from tunneling through
/// kinematic and static bodies. This setting is only considered on dynamic bodies.
/// @warning You should use this flag sparingly since it increases processing time.
this.bullet = false;
/// Does this body start out active?
this.active = true;
/// Use this to store application specific body data.
this.userData = null;
/// Scale the gravity applied to this body.
this.gravityScale = 1;
}
return b2BodyDef;
}());
/// A rigid body. These are created via b2World::CreateBody.
var b2Body = /** @class */ (function () {
// #endif
function b2Body(bd, world) {
this.m_type = exports.b2BodyType.b2_staticBody;
this.m_islandFlag = false;
this.m_awakeFlag = false;
this.m_autoSleepFlag = false;
this.m_bulletFlag = false;
this.m_fixedRotationFlag = false;
this.m_activeFlag = false;
this.m_toiFlag = false;
this.m_islandIndex = 0;
this.m_xf = new b2Transform(); // the body origin transform
// #if B2_ENABLE_PARTICLE
this.m_xf0 = new b2Transform();
// #endif
this.m_sweep = new b2Sweep(); // the swept motion for CCD
this.m_linearVelocity = new b2Vec2();
this.m_angularVelocity = 0;
this.m_force = new b2Vec2();
this.m_torque = 0;
this.m_prev = null;
this.m_next = null;
this.m_fixtureList = null;
this.m_fixtureCount = 0;
this.m_jointList = null;
this.m_contactList = null;
this.m_mass = 1;
this.m_invMass = 1;
// Rotational inertia about the center of mass.
this.m_I = 0;
this.m_invI = 0;
this.m_linearDamping = 0;
this.m_angularDamping = 0;
this.m_gravityScale = 1;
this.m_sleepTime = 0;
this.m_userData = null;
// #if B2_ENABLE_CONTROLLER
this.m_controllerList = null;
this.m_controllerCount = 0;
this.m_bulletFlag = b2Maybe(bd.bullet, false);
this.m_fixedRotationFlag = b2Maybe(bd.fixedRotation, false);
this.m_autoSleepFlag = b2Maybe(bd.allowSleep, true);
this.m_awakeFlag = b2Maybe(bd.awake, true);
this.m_activeFlag = b2Maybe(bd.active, true);
this.m_world = world;
this.m_xf.p.Copy(b2Maybe(bd.position, b2Vec2.ZERO));
// DEBUG: b2Assert(this.m_xf.p.IsValid());
this.m_xf.q.SetAngle(b2Maybe(bd.angle, 0));
// DEBUG: b2Assert(b2IsValid(this.m_xf.q.GetAngle()));
// #if B2_ENABLE_PARTICLE
this.m_xf0.Copy(this.m_xf);
// #endif
this.m_sweep.localCenter.SetZero();
this.m_sweep.c0.Copy(this.m_xf.p);
this.m_sweep.c.Copy(this.m_xf.p);
this.m_sweep.a0 = this.m_sweep.a = this.m_xf.q.GetAngle();
this.m_sweep.alpha0 = 0;
this.m_linearVelocity.Copy(b2Maybe(bd.linearVelocity, b2Vec2.ZERO));
// DEBUG: b2Assert(this.m_linearVelocity.IsValid());
this.m_angularVelocity = b2Maybe(bd.angularVelocity, 0);
// DEBUG: b2Assert(b2IsValid(this.m_angularVelocity));
this.m_linearDamping = b2Maybe(bd.linearDamping, 0);
this.m_angularDamping = b2Maybe(bd.angularDamping, 0);
this.m_gravityScale = b2Maybe(bd.gravityScale, 1);
// DEBUG: b2Assert(b2IsValid(this.m_gravityScale) && this.m_gravityScale >= 0);
// DEBUG: b2Assert(b2IsValid(this.m_angularDamping) && this.m_angularDamping >= 0);
// DEBUG: b2Assert(b2IsValid(this.m_linearDamping) && this.m_linearDamping >= 0);
this.m_force.SetZero();
this.m_torque = 0;
this.m_sleepTime = 0;
this.m_type = b2Maybe(bd.type, exports.b2BodyType.b2_staticBody);
if (bd.type === exports.b2BodyType.b2_dynamicBody) {
this.m_mass = 1;
this.m_invMass = 1;
}
else {
this.m_mass = 0;
this.m_invMass = 0;
}
this.m_I = 0;
this.m_invI = 0;
this.m_userData = bd.userData;
this.m_fixtureList = null;
this.m_fixtureCount = 0;
// #if B2_ENABLE_CONTROLLER
this.m_controllerList = null;
this.m_controllerCount = 0;
// #endif
}
b2Body.prototype.CreateFixture = function (a, b) {
if (b === void 0) { b = 0; }
if (a instanceof b2Shape) {
return this.CreateFixtureShapeDensity(a, b);
}
else {
return this.CreateFixtureDef(a);
}
};
/// Creates a fixture and attach it to this body. Use this function if you need
/// to set some fixture parameters, like friction. Otherwise you can create the
/// fixture directly from a shape.
/// If the density is non-zero, this function automatically updates the mass of the body.
/// Contacts are not created until the next time step.
/// @param def the fixture definition.
/// @warning This function is locked during callbacks.
b2Body.prototype.CreateFixtureDef = function (def) {
if (this.m_world.IsLocked()) {
throw new Error();
}
var fixture = new b2Fixture(def, this);
fixture.Create(def);
if (this.m_activeFlag) {
fixture.CreateProxies(this.m_xf);
}
fixture.m_next = this.m_fixtureList;
this.m_fixtureList = fixture;
++this.m_fixtureCount;
// fixture.m_body = this;
// Adjust mass properties if needed.
if (fixture.m_density > 0) {
this.ResetMassData();
}
// Let the world know we have a new fixture. This will cause new contacts
// to be created at the beginning of the next time step.
this.m_world.m_newFixture = true;
return fixture;
};
b2Body.prototype.CreateFixtureShapeDensity = function (shape, density) {
if (density === void 0) { density = 0; }
var def = b2Body.CreateFixtureShapeDensity_s_def;
def.shape = shape;
def.density = density;
return this.CreateFixtureDef(def);
};
/// Destroy a fixture. This removes the fixture from the broad-phase and
/// destroys all contacts associated with this fixture. This will
/// automatically adjust the mass of the body if the body is dynamic and the
/// fixture has positive density.
/// All fixtures attached to a body are implicitly destroyed when the body is destroyed.
/// @param fixture the fixture to be removed.
/// @warning This function is locked during callbacks.
b2Body.prototype.DestroyFixture = function (fixture) {
if (this.m_world.IsLocked()) {
throw new Error();
}
// DEBUG: b2Assert(fixture.m_body === this);
// Remove the fixture from this body's singly linked list.
// DEBUG: b2Assert(this.m_fixtureCount > 0);
var node = this.m_fixtureList;
var ppF = null;
// DEBUG: let found: boolean = false;
while (node !== null) {
if (node === fixture) {
if (ppF) {
ppF.m_next = fixture.m_next;
}
else {
this.m_fixtureList = fixture.m_next;
}
// DEBUG: found = true;
break;
}
ppF = node;
node = node.m_next;
}
// You tried to remove a shape that is not attached to this body.
// DEBUG: b2Assert(found);
// Destroy any contacts associated with the fixture.
var edge = this.m_contactList;
while (edge) {
var c = edge.contact;
edge = edge.next;
var fixtureA = c.GetFixtureA();
var fixtureB = c.GetFixtureB();
if (fixture === fixtureA || fixture === fixtureB) {
// This destroys the contact and removes it from
// this body's contact list.
this.m_world.m_contactManager.Destroy(c);
}
}
if (this.m_activeFlag) {
fixture.DestroyProxies();
}
// fixture.m_body = null;
fixture.m_next = null;
fixture.Destroy();
--this.m_fixtureCount;
// Reset the mass data.
this.ResetMassData();
};
/// Set the position of the body's origin and rotation.
/// This breaks any contacts and wakes the other bodies.
/// Manipulating a body's transform may cause non-physical behavior.
/// @param position the world position of the body's local origin.
/// @param angle the world rotation in radians.
b2Body.prototype.SetTransformVec = function (position, angle) {
this.SetTransformXY(position.x, position.y, angle);
};
b2Body.prototype.SetTransformXY = function (x, y, angle) {
if (this.m_world.IsLocked()) {
throw new Error();
}
this.m_xf.q.SetAngle(angle);
this.m_xf.p.Set(x, y);
// #if B2_ENABLE_PARTICLE
this.m_xf0.Copy(this.m_xf);
// #endif
b2Transform.MulXV(this.m_xf, this.m_sweep.localCenter, this.m_sweep.c);
this.m_sweep.a = angle;
this.m_sweep.c0.Copy(this.m_sweep.c);
this.m_sweep.a0 = angle;
for (var f = this.m_fixtureList; f; f = f.m_next) {
f.Synchronize(this.m_xf, this.m_xf);
}
this.m_world.m_contactManager.FindNewContacts();
};
b2Body.prototype.SetTransform = function (xf) {
this.SetTransformVec(xf.p, xf.GetAngle());
};
/// Get the body transform for the body's origin.
/// @return the world transform of the body's origin.
b2Body.prototype.GetTransform = function () {
return this.m_xf;
};
/// Get the world body origin position.
/// @return the world position of the body's origin.
b2Body.prototype.GetPosition = function () {
return this.m_xf.p;
};
b2Body.prototype.SetPosition = function (position) {
this.SetTransformVec(position, this.GetAngle());
};
b2Body.prototype.SetPositionXY = function (x, y) {
this.SetTransformXY(x, y, this.GetAngle());
};
/// Get the angle in radians.
/// @return the current world rotation angle in radians.
b2Body.prototype.GetAngle = function () {
return this.m_sweep.a;
};
b2Body.prototype.SetAngle = function (angle) {
this.SetTransformVec(this.GetPosition(), angle);
};
/// Get the world position of the center of mass.
b2Body.prototype.GetWorldCenter = function () {
return this.m_sweep.c;
};
/// Get the local position of the center of mass.
b2Body.prototype.GetLocalCenter = function () {
return this.m_sweep.localCenter;
};
/// Set the linear velocity of the center of mass.
/// @param v the new linear velocity of the center of mass.
b2Body.prototype.SetLinearVelocity = function (v) {
if (this.m_type === exports.b2BodyType.b2_staticBody) {
return;
}
if (b2Vec2.DotVV(v, v) > 0) {
this.SetAwake(true);
}
this.m_linearVelocity.Copy(v);
};
/// Get the linear velocity of the center of mass.
/// @return the linear velocity of the center of mass.
b2Body.prototype.GetLinearVelocity = function () {
return this.m_linearVelocity;
};
/// Set the angular velocity.
/// @param omega the new angular velocity in radians/second.
b2Body.prototype.SetAngularVelocity = function (w) {
if (this.m_type === exports.b2BodyType.b2_staticBody) {
return;
}
if (w * w > 0) {
this.SetAwake(true);
}
this.m_angularVelocity = w;
};
/// Get the angular velocity.
/// @return the angular velocity in radians/second.
b2Body.prototype.GetAngularVelocity = function () {
return this.m_angularVelocity;
};
b2Body.prototype.GetDefinition = function (bd) {
bd.type = this.GetType();
bd.allowSleep = this.m_autoSleepFlag;
bd.angle = this.GetAngle();
bd.angularDamping = this.m_angularDamping;
bd.gravityScale = this.m_gravityScale;
bd.angularVelocity = this.m_angularVelocity;
bd.fixedRotation = this.m_fixedRotationFlag;
bd.bullet = this.m_bulletFlag;
bd.awake = this.m_awakeFlag;
bd.linearDamping = this.m_linearDamping;
bd.linearVelocity.Copy(this.GetLinearVelocity());
bd.position.Copy(this.GetPosition());
bd.userData = this.GetUserData();
return bd;
};
/// Apply a force at a world point. If the force is not
/// applied at the center of mass, it will generate a torque and
/// affect the angular velocity. This wakes up the body.
/// @param force the world force vector, usually in Newtons (N).
/// @param point the world position of the point of application.
/// @param wake also wake up the body
b2Body.prototype.ApplyForce = function (force, point, wake) {
if (wake === void 0) { wake = true; }
if (this.m_type !== exports.b2BodyType.b2_dynamicBody) {
return;
}
if (wake && !this.m_awakeFlag) {
this.SetAwake(true);
}
// Don't accumulate a force if the body is sleeping.
if (this.m_awakeFlag) {
this.m_force.x += force.x;
this.m_force.y += force.y;
this.m_torque += ((point.x - this.m_sweep.c.x) * force.y - (point.y - this.m_sweep.c.y) * force.x);
}
};
/// Apply a force to the center of mass. This wakes up the body.
/// @param force the world force vector, usually in Newtons (N).
/// @param wake also wake up the body
b2Body.prototype.ApplyForceToCenter = function (force, wake) {
if (wake === void 0) { wake = true; }
if (this.m_type !== exports.b2BodyType.b2_dynamicBody) {
return;
}
if (wake && !this.m_awakeFlag) {
this.SetAwake(true);
}
// Don't accumulate a force if the body is sleeping.
if (this.m_awakeFlag) {
this.m_force.x += force.x;
this.m_force.y += force.y;
}
};
/// Apply a torque. This affects the angular velocity
/// without affecting the linear velocity of the center of mass.
/// @param torque about the z-axis (out of the screen), usually in N-m.
/// @param wake also wake up the body
b2Body.prototype.ApplyTorque = function (torque, wake) {
if (wake === void 0) { wake = true; }
if (this.m_type !== exports.b2BodyType.b2_dynamicBody) {
return;
}
if (wake && !this.m_awakeFlag) {
this.SetAwake(true);
}
// Don't accumulate a force if the body is sleeping.
if (this.m_awakeFlag) {
this.m_torque += torque;
}
};
/// Apply an impulse at a point. This immediately modifies the velocity.
/// It also modifies the angular velocity if the point of application
/// is not at the center of mass. This wakes up the body.
/// @param impulse the world impulse vector, usually in N-seconds or kg-m/s.
/// @param point the world position of the point of application.
/// @param wake also wake up the body
b2Body.prototype.ApplyLinearImpulse = function (impulse, point, wake) {
if (wake === void 0) { wake = true; }
if (this.m_type !== exports.b2BodyType.b2_dynamicBody) {
return;
}
if (wake && !this.m_awakeFlag) {
this.SetAwake(true);
}
// Don't accumulate a force if the body is sleeping.
if (this.m_awakeFlag) {
this.m_linearVelocity.x += this.m_invMass * impulse.x;
this.m_linearVelocity.y += this.m_invMass * impulse.y;
this.m_angularVelocity += this.m_invI * ((point.x - this.m_sweep.c.x) * impulse.y - (point.y - this.m_sweep.c.y) * impulse.x);
}
};
/// Apply an impulse at the center of gravity. This immediately modifies the velocity.
/// @param impulse the world impulse vector, usually in N-seconds or kg-m/s.
/// @param wake also wake up the body
b2Body.prototype.ApplyLinearImpulseToCenter = function (impulse, wake) {
if (wake === void 0) { wake = true; }
if (this.m_type !== exports.b2BodyType.b2_dynamicBody) {
return;
}
if (wake && !this.m_awakeFlag) {
this.SetAwake(true);
}
// Don't accumulate a force if the body is sleeping.
if (this.m_awakeFlag) {
this.m_linearVelocity.x += this.m_invMass * impulse.x;
this.m_linearVelocity.y += this.m_invMass * impulse.y;
}
};
/// Apply an angular impulse.
/// @param impulse the angular impulse in units of kg*m*m/s
/// @param wake also wake up the body
b2Body.prototype.ApplyAngularImpulse = function (impulse, wake) {
if (wake === void 0) { wake = true; }
if (this.m_type !== exports.b2BodyType.b2_dynamicBody) {
return;
}
if (wake && !this.m_awakeFlag) {
this.SetAwake(true);
}
// Don't accumulate a force if the body is sleeping.
if (this.m_awakeFlag) {
this.m_angularVelocity += this.m_invI * impulse;
}
};
/// Get the total mass of the body.
/// @return the mass, usually in kilograms (kg).
b2Body.prototype.GetMass = function () {
return this.m_mass;
};
/// Get the rotational inertia of the body about the local origin.
/// @return the rotational inertia, usually in kg-m^2.
b2Body.prototype.GetInertia = function () {
return this.m_I + this.m_mass * b2Vec2.DotVV(this.m_sweep.localCenter, this.m_sweep.localCenter);
};
/// Get the mass data of the body.
/// @return a struct containing the mass, inertia and center of the body.
b2Body.prototype.GetMassData = function (data) {
data.mass = this.m_mass;
data.I = this.m_I + this.m_mass * b2Vec2.DotVV(this.m_sweep.localCenter, this.m_sweep.localCenter);
data.center.Copy(this.m_sweep.localCenter);
return data;
};
b2Body.prototype.SetMassData = function (massData) {
if (this.m_world.IsLocked()) {
throw new Error();
}
if (this.m_type !== exports.b2BodyType.b2_dynamicBody) {
return;
}
this.m_invMass = 0;
this.m_I = 0;
this.m_invI = 0;
this.m_mass = massData.mass;
if (this.m_mass <= 0) {
this.m_mass = 1;
}
this.m_invMass = 1 / this.m_mass;
if (massData.I > 0 && !this.m_fixedRotationFlag) {
this.m_I = massData.I - this.m_mass * b2Vec2.DotVV(massData.center, massData.center);
// DEBUG: b2Assert(this.m_I > 0);
this.m_invI = 1 / this.m_I;
}
// Move center of mass.
var oldCenter = b2Body.SetMassData_s_oldCenter.Copy(this.m_sweep.c);
this.m_sweep.localCenter.Copy(massData.center);
b2Transform.MulXV(this.m_xf, this.m_sweep.localCenter, this.m_sweep.c);
this.m_sweep.c0.Copy(this.m_sweep.c);
// Update center of mass velocity.
b2Vec2.AddVCrossSV(this.m_linearVelocity, this.m_angularVelocity, b2Vec2.SubVV(this.m_sweep.c, oldCenter, b2Vec2.s_t0), this.m_linearVelocity);
};
b2Body.prototype.ResetMassData = function () {
// Compute mass data from shapes. Each shape has its own density.
this.m_mass = 0;
this.m_invMass = 0;
this.m_I = 0;
this.m_invI = 0;
this.m_sweep.localCenter.SetZero();
// Static and kinematic bodies have zero mass.
if (this.m_type === exports.b2BodyType.b2_staticBody || this.m_type === exports.b2BodyType.b2_kinematicBody) {
this.m_sweep.c0.Copy(this.m_xf.p);
this.m_sweep.c.Copy(this.m_xf.p);
this.m_sweep.a0 = this.m_sweep.a;
return;
}
// DEBUG: b2Assert(this.m_type === b2BodyType.b2_dynamicBody);
// Accumulate mass over all fixtures.
var localCenter = b2Body.ResetMassData_s_localCenter.SetZero();
for (var f = this.m_fixtureList; f; f = f.m_next) {
if (f.m_density === 0) {
continue;
}
var massData = f.GetMassData(b2Body.ResetMassData_s_massData);
this.m_mass += massData.mass;
localCenter.x += massData.center.x * massData.mass;
localCenter.y += massData.center.y * massData.mass;
this.m_I += massData.I;
}
// Compute center of mass.
if (this.m_mass > 0) {
this.m_invMass = 1 / this.m_mass;
localCenter.x *= this.m_invMass;
localCenter.y *= this.m_invMass;
}
else {
// Force all dynamic bodies to have a positive mass.
this.m_mass = 1;
this.m_invMass = 1;
}
if (this.m_I > 0 && !this.m_fixedRotationFlag) {
// Center the inertia about the center of mass.
this.m_I -= this.m_mass * b2Vec2.DotVV(localCenter, localCenter);
// DEBUG: b2Assert(this.m_I > 0);
this.m_invI = 1 / this.m_I;
}
else {
this.m_I = 0;
this.m_invI = 0;
}
// Move center of mass.
var oldCenter = b2Body.ResetMassData_s_oldCenter.Copy(this.m_sweep.c);
this.m_sweep.localCenter.Copy(localCenter);
b2Transform.MulXV(this.m_xf, this.m_sweep.localCenter, this.m_sweep.c);
this.m_sweep.c0.Copy(this.m_sweep.c);
// Update center of mass velocity.
b2Vec2.AddVCrossSV(this.m_linearVelocity, this.m_angularVelocity, b2Vec2.SubVV(this.m_sweep.c, oldCenter, b2Vec2.s_t0), this.m_linearVelocity);
};
/// Get the world coordinates of a point given the local coordinates.
/// @param localPoint a point on the body measured relative the the body's origin.
/// @return the same point expressed in world coordinates.
b2Body.prototype.GetWorldPoint = function (localPoint, out) {
return b2Transform.MulXV(this.m_xf, localPoint, out);
};
/// Get the world coordinates of a vector given the local coordinates.
/// @param localVector a vector fixed in the body.
/// @return the same vector expressed in world coordinates.
b2Body.prototype.GetWorldVector = function (localVector, out) {
return b2Rot.MulRV(this.m_xf.q, localVector, out);
};
/// Gets a local point relative to the body's origin given a world point.
/// @param a point in world coordinates.
/// @return the corresponding local point relative to the body's origin.
b2Body.prototype.GetLocalPoint = function (worldPoint, out) {
return b2Transform.MulTXV(this.m_xf, worldPoint, out);
};
/// Gets a local vector given a world vector.
/// @param a vector in world coordinates.
/// @return the corresponding local vector.
b2Body.prototype.GetLocalVector = function (worldVector, out) {
return b2Rot.MulTRV(this.m_xf.q, worldVector, out);
};
/// Get the world linear velocity of a world point attached to this body.
/// @param a point in world coordinates.
/// @return the world velocity of a point.
b2Body.prototype.GetLinearVelocityFromWorldPoint = function (worldPoint, out) {
return b2Vec2.AddVCrossSV(this.m_linearVelocity, this.m_angularVelocity, b2Vec2.SubVV(worldPoint, this.m_sweep.c, b2Vec2.s_t0), out);
};
/// Get the world velocity of a local point.
/// @param a point in local coordinates.
/// @return the world velocity of a point.
b2Body.prototype.GetLinearVelocityFromLocalPoint = function (localPoint, out) {
return this.GetLinearVelocityFromWorldPoint(this.GetWorldPoint(localPoint, out), out);
};
/// Get the linear damping of the body.
b2Body.prototype.GetLinearDamping = function () {
return this.m_linearDamping;
};
/// Set the linear damping of the body.
b2Body.prototype.SetLinearDamping = function (linearDamping) {
this.m_linearDamping = linearDamping;
};
/// Get the angular damping of the body.
b2Body.prototype.GetAngularDamping = function () {
return this.m_angularDamping;
};
/// Set the angular damping of the body.
b2Body.prototype.SetAngularDamping = function (angularDamping) {
this.m_angularDamping = angularDamping;
};
/// Get the gravity scale of the body.
b2Body.prototype.GetGravityScale = function () {
return this.m_gravityScale;
};
/// Set the gravity scale of the body.
b2Body.prototype.SetGravityScale = function (scale) {
this.m_gravityScale = scale;
};
/// Set the type of this body. This may alter the mass and velocity.
b2Body.prototype.SetType = function (type) {
if (this.m_world.IsLocked()) {
throw new Error();
}
if (this.m_type === type) {
return;
}
this.m_type = type;
this.ResetMassData();
if (this.m_type === exports.b2BodyType.b2_staticBody) {
this.m_linearVelocity.SetZero();
this.m_angularVelocity = 0;
this.m_sweep.a0 = this.m_sweep.a;
this.m_sweep.c0.Copy(this.m_sweep.c);
this.SynchronizeFixtures();
}
this.SetAwake(true);
this.m_force.SetZero();
this.m_torque = 0;
// Delete the attached contacts.
var ce = this.m_contactList;
while (ce) {
var ce0 = ce;
ce = ce.next;
this.m_world.m_contactManager.Destroy(ce0.contact);
}
this.m_contactList = null;
// Touch the proxies so that new contacts will be created (when appropriate)
for (var f = this.m_fixtureList; f; f = f.m_next) {
f.TouchProxies();
}
};
/// Get the type of this body.
b2Body.prototype.GetType = function () {
return this.m_type;
};
/// Should this body be treated like a bullet for continuous collision detection?
b2Body.prototype.SetBullet = function (flag) {
this.m_bulletFlag = flag;
};
/// Is this body treated like a bullet for continuous collision detection?
b2Body.prototype.IsBullet = function () {
return this.m_bulletFlag;
};
/// You can disable sleeping on this body. If you disable sleeping, the
/// body will be woken.
b2Body.prototype.SetSleepingAllowed = function (flag) {
this.m_autoSleepFlag = flag;
if (!flag) {
this.SetAwake(true);
}
};
/// Is this body allowed to sleep
b2Body.prototype.IsSleepingAllowed = function () {
return this.m_autoSleepFlag;
};
/// Set the sleep state of the body. A sleeping body has very
/// low CPU cost.
/// @param flag set to true to wake the body, false to put it to sleep.
b2Body.prototype.SetAwake = function (flag) {
if (flag) {
this.m_awakeFlag = true;
this.m_sleepTime = 0;
}
else {
this.m_awakeFlag = false;
this.m_sleepTime = 0;
this.m_linearVelocity.SetZero();
this.m_angularVelocity = 0;
this.m_force.SetZero();
this.m_torque = 0;
}
};
/// Get the sleeping state of this body.
/// @return true if the body is sleeping.
b2Body.prototype.IsAwake = function () {
return this.m_awakeFlag;
};
/// Set the active state of the body. An inactive body is not
/// simulated and cannot be collided with or woken up.
/// If you pass a flag of true, all fixtures will be added to the
/// broad-phase.
/// If you pass a flag of false, all fixtures will be removed from
/// the broad-phase and all contacts will be destroyed.
/// Fixtures and joints are otherwise unaffected. You may continue
/// to create/destroy fixtures and joints on inactive bodies.
/// Fixtures on an inactive body are implicitly inactive and will
/// not participate in collisions, ray-casts, or queries.
/// Joints connected to an inactive body are implicitly inactive.
/// An inactive body is still owned by a b2World object and remains
/// in the body list.
b2Body.prototype.SetActive = function (flag) {
if (this.m_world.IsLocked()) {
throw new Error();
}
if (flag === this.IsActive()) {
return;
}
this.m_activeFlag = flag;
if (flag) {
// Create all proxies.
for (var f = this.m_fixtureList; f; f = f.m_next) {
f.CreateProxies(this.m_xf);
}
// Contacts are created the next time step.
}
else {
// Destroy all proxies.
for (var f = this.m_fixtureList; f; f = f.m_next) {
f.DestroyProxies();
}
// Destroy the attached contacts.
var ce = this.m_contactList;
while (ce) {
var ce0 = ce;
ce = ce.next;
this.m_world.m_contactManager.Destroy(ce0.contact);
}
this.m_contactList = null;
}
};
/// Get the active state of the body.
b2Body.prototype.IsActive = function () {
return this.m_activeFlag;
};
/// Set this body to have fixed rotation. This causes the mass
/// to be reset.
b2Body.prototype.SetFixedRotation = function (flag) {
if (this.m_fixedRotationFlag === flag) {
return;
}
this.m_fixedRotationFlag = flag;
this.m_angularVelocity = 0;
this.ResetMassData();
};
/// Does this body have fixed rotation?
b2Body.prototype.IsFixedRotation = function () {
return this.m_fixedRotationFlag;
};
/// Get the list of all fixtures attached to this body.
b2Body.prototype.GetFixtureList = function () {
return this.m_fixtureList;
};
/// Get the list of all joints attached to this body.
b2Body.prototype.GetJointList = function () {
return this.m_jointList;
};
/// Get the list of all contacts attached to this body.
/// @warning this list changes during the time step and you may
/// miss some collisions if you don't use b2ContactListener.
b2Body.prototype.GetContactList = function () {
return this.m_contactList;
};
/// Get the next body in the world's body list.
b2Body.prototype.GetNext = function () {
return this.m_next;
};
/// Get the user data pointer that was provided in the body definition.
b2Body.prototype.GetUserData = function () {
return this.m_userData;
};
/// Set the user data. Use this to store your application specific data.
b2Body.prototype.SetUserData = function (data) {
this.m_userData = data;
};
/// Get the parent world of this body.
b2Body.prototype.GetWorld = function () {
return this.m_world;
};
/// Dump this body to a log file
b2Body.prototype.Dump = function (log) {
var bodyIndex = this.m_islandIndex;
log("{\n");
log(" const bd: b2BodyDef = new b2BodyDef();\n");
var type_str = "";
switch (this.m_type) {
case exports.b2BodyType.b2_staticBody:
type_str = "b2BodyType.b2_staticBody";
break;
case exports.b2BodyType.b2_kinematicBody:
type_str = "b2BodyType.b2_kinematicBody";
break;
case exports.b2BodyType.b2_dynamicBody:
type_str = "b2BodyType.b2_dynamicBody";
break;
default:
// DEBUG: b2Assert(false);
break;
}
log(" bd.type = %s;\n", type_str);
log(" bd.position.Set(%.15f, %.15f);\n", this.m_xf.p.x, this.m_xf.p.y);
log(" bd.angle = %.15f;\n", this.m_sweep.a);
log(" bd.linearVelocity.Set(%.15f, %.15f);\n", this.m_linearVelocity.x, this.m_linearVelocity.y);
log(" bd.angularVelocity = %.15f;\n", this.m_angularVelocity);
log(" bd.linearDamping = %.15f;\n", this.m_linearDamping);
log(" bd.angularDamping = %.15f;\n", this.m_angularDamping);
log(" bd.allowSleep = %s;\n", (this.m_autoSleepFlag) ? ("true") : ("false"));
log(" bd.awake = %s;\n", (this.m_awakeFlag) ? ("true") : ("false"));
log(" bd.fixedRotation = %s;\n", (this.m_fixedRotationFlag) ? ("true") : ("false"));
log(" bd.bullet = %s;\n", (this.m_bulletFlag) ? ("true") : ("false"));
log(" bd.active = %s;\n", (this.m_activeFlag) ? ("true") : ("false"));
log(" bd.gravityScale = %.15f;\n", this.m_gravityScale);
log("\n");
log(" bodies[%d] = this.m_world.CreateBody(bd);\n", this.m_islandIndex);
log("\n");
for (var f = this.m_fixtureList; f; f = f.m_next) {
log(" {\n");
f.Dump(log, bodyIndex);
log(" }\n");
}
log("}\n");
};
b2Body.prototype.SynchronizeFixtures = function () {
var xf1 = b2Body.SynchronizeFixtures_s_xf1;
xf1.q.SetAngle(this.m_sweep.a0);
b2Rot.MulRV(xf1.q, this.m_sweep.localCenter, xf1.p);
b2Vec2.SubVV(this.m_sweep.c0, xf1.p, xf1.p);
for (var f = this.m_fixtureList; f; f = f.m_next) {
f.Synchronize(xf1, this.m_xf);
}
};
b2Body.prototype.SynchronizeTransform = function () {
this.m_xf.q.SetAngle(this.m_sweep.a);
b2Rot.MulRV(this.m_xf.q, this.m_sweep.localCenter, this.m_xf.p);
b2Vec2.SubVV(this.m_sweep.c, this.m_xf.p, this.m_xf.p);
};
// This is used to prevent connected bodies from colliding.
// It may lie, depending on the collideConnected flag.
b2Body.prototype.ShouldCollide = function (other) {
// At least one body should be dynamic or kinematic.
if (this.m_type === exports.b2BodyType.b2_staticBody && other.m_type === exports.b2BodyType.b2_staticBody) {
return false;
}
return this.ShouldCollideConnected(other);
};
b2Body.prototype.ShouldCollideConnected = function (other) {
// Does a joint prevent collision?
for (var jn = this.m_jointList; jn; jn = jn.next) {
if (jn.other === other) {
if (!jn.joint.m_collideConnected) {
return false;
}
}
}
return true;
};
b2Body.prototype.Advance = function (alpha) {
// Advance to the new safe time. This doesn't sync the broad-phase.
this.m_sweep.Advance(alpha);
this.m_sweep.c.Copy(this.m_sweep.c0);
this.m_sweep.a = this.m_sweep.a0;
this.m_xf.q.SetAngle(this.m_sweep.a);
b2Rot.MulRV(this.m_xf.q, this.m_sweep.localCenter, this.m_xf.p);
b2Vec2.SubVV(this.m_sweep.c, this.m_xf.p, this.m_xf.p);
};
// #if B2_ENABLE_CONTROLLER
b2Body.prototype.GetControllerList = function () {
return this.m_controllerList;
};
b2Body.prototype.GetControllerCount = function () {
return this.m_controllerCount;
};
/// Creates a fixture from a shape and attach it to this body.
/// This is a convenience function. Use b2FixtureDef if you need to set parameters
/// like friction, restitution, user data, or filtering.
/// If the density is non-zero, this function automatically updates the mass of the body.
/// @param shape the shape to be cloned.
/// @param density the shape density (set to zero for static bodies).
/// @warning This function is locked during callbacks.
b2Body.CreateFixtureShapeDensity_s_def = new b2FixtureDef();
/// Set the mass properties to override the mass properties of the fixtures.
/// Note that this changes the center of mass position.
/// Note that creating or destroying fixtures can also alter the mass.
/// This function has no effect if the body isn't dynamic.
/// @param massData the mass properties.
b2Body.SetMassData_s_oldCenter = new b2Vec2();
/// This resets the mass properties to the sum of the mass properties of the fixtures.
/// This normally does not need to be called unless you called SetMassData to override
/// the mass and you later want to reset the mass.
b2Body.ResetMassData_s_localCenter = new b2Vec2();
b2Body.ResetMassData_s_oldCenter = new b2Vec2();
b2Body.ResetMassData_s_massData = new b2MassData();
b2Body.SynchronizeFixtures_s_xf1 = new b2Transform();
return b2Body;
}());
/*
* Copyright (c) 2006-2007 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 (b2JointType) {
b2JointType[b2JointType["e_unknownJoint"] = 0] = "e_unknownJoint";
b2JointType[b2JointType["e_revoluteJoint"] = 1] = "e_revoluteJoint";
b2JointType[b2JointType["e_prismaticJoint"] = 2] = "e_prismaticJoint";
b2JointType[b2JointType["e_distanceJoint"] = 3] = "e_distanceJoint";
b2JointType[b2JointType["e_pulleyJoint"] = 4] = "e_pulleyJoint";
b2JointType[b2JointType["e_mouseJoint"] = 5] = "e_mouseJoint";
b2JointType[b2JointType["e_gearJoint"] = 6] = "e_gearJoint";
b2JointType[b2JointType["e_wheelJoint"] = 7] = "e_wheelJoint";
b2JointType[b2JointType["e_weldJoint"] = 8] = "e_weldJoint";
b2JointType[b2JointType["e_frictionJoint"] = 9] = "e_frictionJoint";
b2JointType[b2JointType["e_ropeJoint"] = 10] = "e_ropeJoint";
b2JointType[b2JointType["e_motorJoint"] = 11] = "e_motorJoint";
b2JointType[b2JointType["e_areaJoint"] = 12] = "e_areaJoint";
})(exports.b2JointType || (exports.b2JointType = {}));
(function (b2LimitState) {
b2LimitState[b2LimitState["e_inactiveLimit"] = 0] = "e_inactiveLimit";
b2LimitState[b2LimitState["e_atLowerLimit"] = 1] = "e_atLowerLimit";
b2LimitState[b2LimitState["e_atUpperLimit"] = 2] = "e_atUpperLimit";
b2LimitState[b2LimitState["e_equalLimits"] = 3] = "e_equalLimits";
})(exports.b2LimitState || (exports.b2LimitState = {}));
var b2Jacobian = /** @class */ (function () {
function b2Jacobian() {
this.linear = new b2Vec2();
this.angularA = 0;
this.angularB = 0;
}
b2Jacobian.prototype.SetZero = function () {
this.linear.SetZero();
this.angularA = 0;
this.angularB = 0;
return this;
};
b2Jacobian.prototype.Set = function (x, a1, a2) {
this.linear.Copy(x);
this.angularA = a1;
this.angularB = a2;
return this;
};
return b2Jacobian;
}());
/// A joint edge is used to connect bodies and joints together
/// in a joint graph where each body is a node and each joint
/// is an edge. A joint edge belongs to a doubly linked list
/// maintained in each attached body. Each joint has two joint
/// nodes, one for each attached body.
var b2JointEdge = /** @class */ (function () {
function b2JointEdge(joint, other) {
this.prev = null; ///< the previous joint edge in the body's joint list
this.next = null; ///< the next joint edge in the body's joint list
this.joint = joint;
this.other = other;
}
return b2JointEdge;
}());
/// Joint definitions are used to construct joints.
var b2JointDef = /** @class */ (function () {
function b2JointDef(type) {
/// The joint type is set automatically for concrete joint types.
this.type = exports.b2JointType.e_unknownJoint;
/// Use this to attach application specific data to your joints.
this.userData = null;
/// Set this flag to true if the attached bodies should collide.
this.collideConnected = false;
this.type = type;
}
return b2JointDef;
}());
/// The base joint class. Joints are used to constraint two bodies together in
/// various fashions. Some joints also feature limits and motors.
var b2Joint = /** @class */ (function () {
function b2Joint(def) {
// DEBUG: b2Assert(def.bodyA !== def.bodyB);
this.m_type = exports.b2JointType.e_unknownJoint;
this.m_prev = null;
this.m_next = null;
this.m_index = 0;
this.m_islandFlag = false;
this.m_collideConnected = false;
this.m_userData = null;
this.m_type = def.type;
this.m_edgeA = new b2JointEdge(this, def.bodyB);
this.m_edgeB = new b2JointEdge(this, def.bodyA);
this.m_bodyA = def.bodyA;
this.m_bodyB = def.bodyB;
this.m_collideConnected = b2Maybe(def.collideConnected, false);
this.m_userData = def.userData;
}
/// Get the type of the concrete joint.
b2Joint.prototype.GetType = function () {
return this.m_type;
};
/// Get the first body attached to this joint.
b2Joint.prototype.GetBodyA = function () {
return this.m_bodyA;
};
/// Get the second body attached to this joint.
b2Joint.prototype.GetBodyB = function () {
return this.m_bodyB;
};
/// Get the next joint the world joint list.
b2Joint.prototype.GetNext = function () {
return this.m_next;
};
/// Get the user data pointer.
b2Joint.prototype.GetUserData = function () {
return this.m_userData;
};
/// Set the user data pointer.
b2Joint.prototype.SetUserData = function (data) {
this.m_userData = data;
};
/// Short-cut function to determine if either body is inactive.
b2Joint.prototype.IsActive = function () {
return this.m_bodyA.IsActive() && this.m_bodyB.IsActive();
};
/// Get collide connected.
/// Note: modifying the collide connect flag won't work correctly because
/// the flag is only checked when fixture AABBs begin to overlap.
b2Joint.prototype.GetCollideConnected = function () {
return this.m_collideConnected;
};
/// Dump this joint to the log file.
b2Joint.prototype.Dump = function (log) {
log("// Dump is not supported for this joint type.\n");
};
/// Shift the origin for any points stored in world coordinates.
b2Joint.prototype.ShiftOrigin = function (newOrigin) {
};
return b2Joint;
}());
/*
* Copyright (c) 2006-2007 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.
*/
/// Distance joint definition. This requires defining an
/// anchor point on both bodies and the non-zero length of the
/// distance joint. The definition uses local anchor points
/// so that the initial configuration can violate the constraint
/// slightly. This helps when saving and loading a game.
/// @warning Do not use a zero or short length.
var b2DistanceJointDef = /** @class */ (function (_super) {
__extends(b2DistanceJointDef, _super);
function b2DistanceJointDef() {
var _this = _super.call(this, exports.b2JointType.e_distanceJoint) || this;
_this.localAnchorA = new b2Vec2();
_this.localAnchorB = new b2Vec2();
_this.length = 1;
_this.frequencyHz = 0;
_this.dampingRatio = 0;
return _this;
}
b2DistanceJointDef.prototype.Initialize = function (b1, b2, anchor1, anchor2) {
this.bodyA = b1;
this.bodyB = b2;
this.bodyA.GetLocalPoint(anchor1, this.localAnchorA);
this.bodyB.GetLocalPoint(anchor2, this.localAnchorB);
this.length = b2Vec2.DistanceVV(anchor1, anchor2);
this.frequencyHz = 0;
this.dampingRatio = 0;
};
return b2DistanceJointDef;
}(b2JointDef));
var b2DistanceJoint = /** @class */ (function (_super) {
__extends(b2DistanceJoint, _super);
function b2DistanceJoint(def) {
var _this = _super.call(this, def) || this;
_this.m_frequencyHz = 0;
_this.m_dampingRatio = 0;
_this.m_bias = 0;
// Solver shared
_this.m_localAnchorA = new b2Vec2();
_this.m_localAnchorB = new b2Vec2();
_this.m_gamma = 0;
_this.m_impulse = 0;
_this.m_length = 0;
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_u = new b2Vec2();
_this.m_rA = new b2Vec2();
_this.m_rB = new b2Vec2();
_this.m_localCenterA = new b2Vec2();
_this.m_localCenterB = new b2Vec2();
_this.m_invMassA = 0;
_this.m_invMassB = 0;
_this.m_invIA = 0;
_this.m_invIB = 0;
_this.m_mass = 0;
_this.m_qA = new b2Rot();
_this.m_qB = new b2Rot();
_this.m_lalcA = new b2Vec2();
_this.m_lalcB = new b2Vec2();
_this.m_frequencyHz = b2Maybe(def.frequencyHz, 0);
_this.m_dampingRatio = b2Maybe(def.dampingRatio, 0);
_this.m_localAnchorA.Copy(def.localAnchorA);
_this.m_localAnchorB.Copy(def.localAnchorB);
_this.m_length = def.length;
return _this;
}
b2DistanceJoint.prototype.GetAnchorA = function (out) {
return this.m_bodyA.GetWorldPoint(this.m_localAnchorA, out);
};
b2DistanceJoint.prototype.GetAnchorB = function (out) {
return this.m_bodyB.GetWorldPoint(this.m_localAnchorB, out);
};
b2DistanceJoint.prototype.GetReactionForce = function (inv_dt, out) {
out.x = inv_dt * this.m_impulse * this.m_u.x;
out.y = inv_dt * this.m_impulse * this.m_u.y;
return out;
};
b2DistanceJoint.prototype.GetReactionTorque = function (inv_dt) {
return 0;
};
b2DistanceJoint.prototype.GetLocalAnchorA = function () { return this.m_localAnchorA; };
b2DistanceJoint.prototype.GetLocalAnchorB = function () { return this.m_localAnchorB; };
b2DistanceJoint.prototype.SetLength = function (length) {
this.m_length = length;
};
b2DistanceJoint.prototype.Length = function () {
return this.m_length;
};
b2DistanceJoint.prototype.SetFrequency = function (hz) {
this.m_frequencyHz = hz;
};
b2DistanceJoint.prototype.GetFrequency = function () {
return this.m_frequencyHz;
};
b2DistanceJoint.prototype.SetDampingRatio = function (ratio) {
this.m_dampingRatio = ratio;
};
b2DistanceJoint.prototype.GetDampingRatio = function () {
return this.m_dampingRatio;
};
b2DistanceJoint.prototype.Dump = function (log) {
var indexA = this.m_bodyA.m_islandIndex;
var indexB = this.m_bodyB.m_islandIndex;
log(" const jd: b2DistanceJointDef = new b2DistanceJointDef();\n");
log(" jd.bodyA = bodies[%d];\n", indexA);
log(" jd.bodyB = bodies[%d];\n", indexB);
log(" jd.collideConnected = %s;\n", (this.m_collideConnected) ? ("true") : ("false"));
log(" jd.localAnchorA.Set(%.15f, %.15f);\n", this.m_localAnchorA.x, this.m_localAnchorA.y);
log(" jd.localAnchorB.Set(%.15f, %.15f);\n", this.m_localAnchorB.x, this.m_localAnchorB.y);
log(" jd.length = %.15f;\n", this.m_length);
log(" jd.frequencyHz = %.15f;\n", this.m_frequencyHz);
log(" jd.dampingRatio = %.15f;\n", this.m_dampingRatio);
log(" joints[%d] = this.m_world.CreateJoint(jd);\n", this.m_index);
};
b2DistanceJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexA = this.m_bodyA.m_islandIndex;
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_localCenterA.Copy(this.m_bodyA.m_sweep.localCenter);
this.m_localCenterB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_invMassA = this.m_bodyA.m_invMass;
this.m_invMassB = this.m_bodyB.m_invMass;
this.m_invIA = this.m_bodyA.m_invI;
this.m_invIB = this.m_bodyB.m_invI;
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
// const qA: b2Rot = new b2Rot(aA), qB: b2Rot = new b2Rot(aB);
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// m_u = cB + m_rB - cA - m_rA;
this.m_u.x = cB.x + this.m_rB.x - cA.x - this.m_rA.x;
this.m_u.y = cB.y + this.m_rB.y - cA.y - this.m_rA.y;
// Handle singularity.
var length = this.m_u.Length();
if (length > b2_linearSlop) {
this.m_u.SelfMul(1 / length);
}
else {
this.m_u.SetZero();
}
// float32 crAu = b2Cross(m_rA, m_u);
var crAu = b2Vec2.CrossVV(this.m_rA, this.m_u);
// float32 crBu = b2Cross(m_rB, m_u);
var crBu = b2Vec2.CrossVV(this.m_rB, this.m_u);
// float32 invMass = m_invMassA + m_invIA * crAu * crAu + m_invMassB + m_invIB * crBu * crBu;
var invMass = this.m_invMassA + this.m_invIA * crAu * crAu + this.m_invMassB + this.m_invIB * crBu * crBu;
// Compute the effective mass matrix.
this.m_mass = invMass !== 0 ? 1 / invMass : 0;
if (this.m_frequencyHz > 0) {
var C = length - this.m_length;
// Frequency
var omega = 2 * b2_pi * this.m_frequencyHz;
// Damping coefficient
var d = 2 * this.m_mass * this.m_dampingRatio * omega;
// Spring stiffness
var k = this.m_mass * omega * omega;
// magic formulas
var h = data.step.dt;
this.m_gamma = h * (d + h * k);
this.m_gamma = this.m_gamma !== 0 ? 1 / this.m_gamma : 0;
this.m_bias = C * h * k * this.m_gamma;
invMass += this.m_gamma;
this.m_mass = invMass !== 0 ? 1 / invMass : 0;
}
else {
this.m_gamma = 0;
this.m_bias = 0;
}
if (data.step.warmStarting) {
// Scale the impulse to support a variable time step.
this.m_impulse *= data.step.dtRatio;
// b2Vec2 P = m_impulse * m_u;
var P = b2Vec2.MulSV(this.m_impulse, this.m_u, b2DistanceJoint.InitVelocityConstraints_s_P);
// vA -= m_invMassA * P;
vA.SelfMulSub(this.m_invMassA, P);
// wA -= m_invIA * b2Cross(m_rA, P);
wA -= this.m_invIA * b2Vec2.CrossVV(this.m_rA, P);
// vB += m_invMassB * P;
vB.SelfMulAdd(this.m_invMassB, P);
// wB += m_invIB * b2Cross(m_rB, P);
wB += this.m_invIB * b2Vec2.CrossVV(this.m_rB, P);
}
else {
this.m_impulse = 0;
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2DistanceJoint.prototype.SolveVelocityConstraints = function (data) {
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
// b2Vec2 vpA = vA + b2Cross(wA, m_rA);
var vpA = b2Vec2.AddVCrossSV(vA, wA, this.m_rA, b2DistanceJoint.SolveVelocityConstraints_s_vpA);
// b2Vec2 vpB = vB + b2Cross(wB, m_rB);
var vpB = b2Vec2.AddVCrossSV(vB, wB, this.m_rB, b2DistanceJoint.SolveVelocityConstraints_s_vpB);
// float32 Cdot = b2Dot(m_u, vpB - vpA);
var Cdot = b2Vec2.DotVV(this.m_u, b2Vec2.SubVV(vpB, vpA, b2Vec2.s_t0));
var impulse = (-this.m_mass * (Cdot + this.m_bias + this.m_gamma * this.m_impulse));
this.m_impulse += impulse;
// b2Vec2 P = impulse * m_u;
var P = b2Vec2.MulSV(impulse, this.m_u, b2DistanceJoint.SolveVelocityConstraints_s_P);
// vA -= m_invMassA * P;
vA.SelfMulSub(this.m_invMassA, P);
// wA -= m_invIA * b2Cross(m_rA, P);
wA -= this.m_invIA * b2Vec2.CrossVV(this.m_rA, P);
// vB += m_invMassB * P;
vB.SelfMulAdd(this.m_invMassB, P);
// wB += m_invIB * b2Cross(m_rB, P);
wB += this.m_invIB * b2Vec2.CrossVV(this.m_rB, P);
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2DistanceJoint.prototype.SolvePositionConstraints = function (data) {
if (this.m_frequencyHz > 0) {
// There is no position correction for soft distance constraints.
return true;
}
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
// const qA: b2Rot = new b2Rot(aA), qB: b2Rot = new b2Rot(aB);
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, this.m_rA); // use m_rA
// b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, this.m_rB); // use m_rB
// b2Vec2 u = cB + rB - cA - rA;
var u = this.m_u; // use m_u
u.x = cB.x + rB.x - cA.x - rA.x;
u.y = cB.y + rB.y - cA.y - rA.y;
// float32 length = u.Normalize();
var length = this.m_u.Normalize();
// float32 C = length - m_length;
var C = length - this.m_length;
C = b2Clamp(C, (-b2_maxLinearCorrection), b2_maxLinearCorrection);
var impulse = (-this.m_mass * C);
// b2Vec2 P = impulse * u;
var P = b2Vec2.MulSV(impulse, u, b2DistanceJoint.SolvePositionConstraints_s_P);
// cA -= m_invMassA * P;
cA.SelfMulSub(this.m_invMassA, P);
// aA -= m_invIA * b2Cross(rA, P);
aA -= this.m_invIA * b2Vec2.CrossVV(rA, P);
// cB += m_invMassB * P;
cB.SelfMulAdd(this.m_invMassB, P);
// aB += m_invIB * b2Cross(rB, P);
aB += this.m_invIB * b2Vec2.CrossVV(rB, P);
// data.positions[this.m_indexA].c = cA;
data.positions[this.m_indexA].a = aA;
// data.positions[this.m_indexB].c = cB;
data.positions[this.m_indexB].a = aB;
return b2Abs(C) < b2_linearSlop;
};
b2DistanceJoint.InitVelocityConstraints_s_P = new b2Vec2();
b2DistanceJoint.SolveVelocityConstraints_s_vpA = new b2Vec2();
b2DistanceJoint.SolveVelocityConstraints_s_vpB = new b2Vec2();
b2DistanceJoint.SolveVelocityConstraints_s_P = new b2Vec2();
b2DistanceJoint.SolvePositionConstraints_s_P = new b2Vec2();
return b2DistanceJoint;
}(b2Joint));
var b2AreaJointDef = /** @class */ (function (_super) {
__extends(b2AreaJointDef, _super);
function b2AreaJointDef() {
var _this = _super.call(this, exports.b2JointType.e_areaJoint) || this;
_this.bodies = [];
_this.frequencyHz = 0;
_this.dampingRatio = 0;
return _this;
}
b2AreaJointDef.prototype.AddBody = function (body) {
this.bodies.push(body);
if (this.bodies.length === 1) {
this.bodyA = body;
}
else if (this.bodies.length === 2) {
this.bodyB = body;
}
};
return b2AreaJointDef;
}(b2JointDef));
var b2AreaJoint = /** @class */ (function (_super) {
__extends(b2AreaJoint, _super);
function b2AreaJoint(def) {
var _this = _super.call(this, def) || this;
_this.m_frequencyHz = 0;
_this.m_dampingRatio = 0;
// Solver shared
_this.m_impulse = 0;
_this.m_targetArea = 0;
// DEBUG: b2Assert(def.bodies.length >= 3, "You cannot create an area joint with less than three bodies.");
_this.m_bodies = def.bodies;
_this.m_frequencyHz = b2Maybe(def.frequencyHz, 0);
_this.m_dampingRatio = b2Maybe(def.dampingRatio, 0);
_this.m_targetLengths = b2MakeNumberArray(def.bodies.length);
_this.m_normals = b2Vec2.MakeArray(def.bodies.length);
_this.m_joints = []; // b2MakeNullArray(def.bodies.length);
_this.m_deltas = b2Vec2.MakeArray(def.bodies.length);
_this.m_delta = new b2Vec2();
var djd = new b2DistanceJointDef();
djd.frequencyHz = _this.m_frequencyHz;
djd.dampingRatio = _this.m_dampingRatio;
_this.m_targetArea = 0;
for (var i = 0; i < _this.m_bodies.length; ++i) {
var body = _this.m_bodies[i];
var next = _this.m_bodies[(i + 1) % _this.m_bodies.length];
var body_c = body.GetWorldCenter();
var next_c = next.GetWorldCenter();
_this.m_targetLengths[i] = b2Vec2.DistanceVV(body_c, next_c);
_this.m_targetArea += b2Vec2.CrossVV(body_c, next_c);
djd.Initialize(body, next, body_c, next_c);
_this.m_joints[i] = body.GetWorld().CreateJoint(djd);
}
_this.m_targetArea *= 0.5;
return _this;
}
b2AreaJoint.prototype.GetAnchorA = function (out) {
return out;
};
b2AreaJoint.prototype.GetAnchorB = function (out) {
return out;
};
b2AreaJoint.prototype.GetReactionForce = function (inv_dt, out) {
return out;
};
b2AreaJoint.prototype.GetReactionTorque = function (inv_dt) {
return 0;
};
b2AreaJoint.prototype.SetFrequency = function (hz) {
this.m_frequencyHz = hz;
for (var i = 0; i < this.m_joints.length; ++i) {
this.m_joints[i].SetFrequency(hz);
}
};
b2AreaJoint.prototype.GetFrequency = function () {
return this.m_frequencyHz;
};
b2AreaJoint.prototype.SetDampingRatio = function (ratio) {
this.m_dampingRatio = ratio;
for (var i = 0; i < this.m_joints.length; ++i) {
this.m_joints[i].SetDampingRatio(ratio);
}
};
b2AreaJoint.prototype.GetDampingRatio = function () {
return this.m_dampingRatio;
};
b2AreaJoint.prototype.Dump = function (log) {
log("Area joint dumping is not supported.\n");
};
b2AreaJoint.prototype.InitVelocityConstraints = function (data) {
for (var i = 0; i < this.m_bodies.length; ++i) {
var prev = this.m_bodies[(i + this.m_bodies.length - 1) % this.m_bodies.length];
var next = this.m_bodies[(i + 1) % this.m_bodies.length];
var prev_c = data.positions[prev.m_islandIndex].c;
var next_c = data.positions[next.m_islandIndex].c;
var delta = this.m_deltas[i];
b2Vec2.SubVV(next_c, prev_c, delta);
}
if (data.step.warmStarting) {
this.m_impulse *= data.step.dtRatio;
for (var i = 0; i < this.m_bodies.length; ++i) {
var body = this.m_bodies[i];
var body_v = data.velocities[body.m_islandIndex].v;
var delta = this.m_deltas[i];
body_v.x += body.m_invMass * delta.y * 0.5 * this.m_impulse;
body_v.y += body.m_invMass * -delta.x * 0.5 * this.m_impulse;
}
}
else {
this.m_impulse = 0;
}
};
b2AreaJoint.prototype.SolveVelocityConstraints = function (data) {
var dotMassSum = 0;
var crossMassSum = 0;
for (var i = 0; i < this.m_bodies.length; ++i) {
var body = this.m_bodies[i];
var body_v = data.velocities[body.m_islandIndex].v;
var delta = this.m_deltas[i];
dotMassSum += delta.LengthSquared() / body.GetMass();
crossMassSum += b2Vec2.CrossVV(body_v, delta);
}
var lambda = -2 * crossMassSum / dotMassSum;
// lambda = b2Clamp(lambda, -b2_maxLinearCorrection, b2_maxLinearCorrection);
this.m_impulse += lambda;
for (var i = 0; i < this.m_bodies.length; ++i) {
var body = this.m_bodies[i];
var body_v = data.velocities[body.m_islandIndex].v;
var delta = this.m_deltas[i];
body_v.x += body.m_invMass * delta.y * 0.5 * lambda;
body_v.y += body.m_invMass * -delta.x * 0.5 * lambda;
}
};
b2AreaJoint.prototype.SolvePositionConstraints = function (data) {
var perimeter = 0;
var area = 0;
for (var i = 0; i < this.m_bodies.length; ++i) {
var body = this.m_bodies[i];
var next = this.m_bodies[(i + 1) % this.m_bodies.length];
var body_c = data.positions[body.m_islandIndex].c;
var next_c = data.positions[next.m_islandIndex].c;
var delta = b2Vec2.SubVV(next_c, body_c, this.m_delta);
var dist = delta.Length();
if (dist < b2_epsilon) {
dist = 1;
}
this.m_normals[i].x = delta.y / dist;
this.m_normals[i].y = -delta.x / dist;
perimeter += dist;
area += b2Vec2.CrossVV(body_c, next_c);
}
area *= 0.5;
var deltaArea = this.m_targetArea - area;
var toExtrude = 0.5 * deltaArea / perimeter;
var done = true;
for (var i = 0; i < this.m_bodies.length; ++i) {
var body = this.m_bodies[i];
var body_c = data.positions[body.m_islandIndex].c;
var next_i = (i + 1) % this.m_bodies.length;
var delta = b2Vec2.AddVV(this.m_normals[i], this.m_normals[next_i], this.m_delta);
delta.SelfMul(toExtrude);
var norm_sq = delta.LengthSquared();
if (norm_sq > b2Sq(b2_maxLinearCorrection)) {
delta.SelfMul(b2_maxLinearCorrection / b2Sqrt(norm_sq));
}
if (norm_sq > b2Sq(b2_linearSlop)) {
done = false;
}
body_c.x += delta.x;
body_c.y += delta.y;
}
return done;
};
return b2AreaJoint;
}(b2Joint));
/*
* Copyright (c) 2006-2007 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.
*/
/// Friction joint definition.
var b2FrictionJointDef = /** @class */ (function (_super) {
__extends(b2FrictionJointDef, _super);
function b2FrictionJointDef() {
var _this = _super.call(this, exports.b2JointType.e_frictionJoint) || this;
_this.localAnchorA = new b2Vec2();
_this.localAnchorB = new b2Vec2();
_this.maxForce = 0;
_this.maxTorque = 0;
return _this;
}
b2FrictionJointDef.prototype.Initialize = function (bA, bB, anchor) {
this.bodyA = bA;
this.bodyB = bB;
this.bodyA.GetLocalPoint(anchor, this.localAnchorA);
this.bodyB.GetLocalPoint(anchor, this.localAnchorB);
};
return b2FrictionJointDef;
}(b2JointDef));
var b2FrictionJoint = /** @class */ (function (_super) {
__extends(b2FrictionJoint, _super);
function b2FrictionJoint(def) {
var _this = _super.call(this, def) || this;
_this.m_localAnchorA = new b2Vec2();
_this.m_localAnchorB = new b2Vec2();
// Solver shared
_this.m_linearImpulse = new b2Vec2();
_this.m_angularImpulse = 0;
_this.m_maxForce = 0;
_this.m_maxTorque = 0;
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_rA = new b2Vec2();
_this.m_rB = new b2Vec2();
_this.m_localCenterA = new b2Vec2();
_this.m_localCenterB = new b2Vec2();
_this.m_invMassA = 0;
_this.m_invMassB = 0;
_this.m_invIA = 0;
_this.m_invIB = 0;
_this.m_linearMass = new b2Mat22();
_this.m_angularMass = 0;
_this.m_qA = new b2Rot();
_this.m_qB = new b2Rot();
_this.m_lalcA = new b2Vec2();
_this.m_lalcB = new b2Vec2();
_this.m_K = new b2Mat22();
_this.m_localAnchorA.Copy(def.localAnchorA);
_this.m_localAnchorB.Copy(def.localAnchorB);
_this.m_linearImpulse.SetZero();
_this.m_maxForce = b2Maybe(def.maxForce, 0);
_this.m_maxTorque = b2Maybe(def.maxTorque, 0);
_this.m_linearMass.SetZero();
return _this;
}
b2FrictionJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexA = this.m_bodyA.m_islandIndex;
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_localCenterA.Copy(this.m_bodyA.m_sweep.localCenter);
this.m_localCenterB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_invMassA = this.m_bodyA.m_invMass;
this.m_invMassB = this.m_bodyB.m_invMass;
this.m_invIA = this.m_bodyA.m_invI;
this.m_invIB = this.m_bodyB.m_invI;
// const cA: b2Vec2 = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
// const cB: b2Vec2 = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
// const qA: b2Rot = new b2Rot(aA), qB: b2Rot = new b2Rot(aB);
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// Compute the effective mass matrix.
// m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// J = [-I -r1_skew I r2_skew]
// [ 0 -1 0 1]
// r_skew = [-ry; rx]
// Matlab
// K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB]
// [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB]
// [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB]
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
var K = this.m_K; // new b2Mat22();
K.ex.x = mA + mB + iA * rA.y * rA.y + iB * rB.y * rB.y;
K.ex.y = -iA * rA.x * rA.y - iB * rB.x * rB.y;
K.ey.x = K.ex.y;
K.ey.y = mA + mB + iA * rA.x * rA.x + iB * rB.x * rB.x;
K.GetInverse(this.m_linearMass);
this.m_angularMass = iA + iB;
if (this.m_angularMass > 0) {
this.m_angularMass = 1 / this.m_angularMass;
}
if (data.step.warmStarting) {
// Scale impulses to support a variable time step.
// m_linearImpulse *= data.step.dtRatio;
this.m_linearImpulse.SelfMul(data.step.dtRatio);
this.m_angularImpulse *= data.step.dtRatio;
// const P: b2Vec2(m_linearImpulse.x, m_linearImpulse.y);
var P = this.m_linearImpulse;
// vA -= mA * P;
vA.SelfMulSub(mA, P);
// wA -= iA * (b2Cross(m_rA, P) + m_angularImpulse);
wA -= iA * (b2Vec2.CrossVV(this.m_rA, P) + this.m_angularImpulse);
// vB += mB * P;
vB.SelfMulAdd(mB, P);
// wB += iB * (b2Cross(m_rB, P) + m_angularImpulse);
wB += iB * (b2Vec2.CrossVV(this.m_rB, P) + this.m_angularImpulse);
}
else {
this.m_linearImpulse.SetZero();
this.m_angularImpulse = 0;
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2FrictionJoint.prototype.SolveVelocityConstraints = function (data) {
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
var h = data.step.dt;
// Solve angular friction
{
var Cdot = wB - wA;
var impulse = (-this.m_angularMass * Cdot);
var oldImpulse = this.m_angularImpulse;
var maxImpulse = h * this.m_maxTorque;
this.m_angularImpulse = b2Clamp(this.m_angularImpulse + impulse, (-maxImpulse), maxImpulse);
impulse = this.m_angularImpulse - oldImpulse;
wA -= iA * impulse;
wB += iB * impulse;
}
// Solve linear friction
{
// b2Vec2 Cdot = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA);
var Cdot_v2 = b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, this.m_rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, this.m_rA, b2Vec2.s_t1), b2FrictionJoint.SolveVelocityConstraints_s_Cdot_v2);
// b2Vec2 impulse = -b2Mul(m_linearMass, Cdot);
var impulseV = b2Mat22.MulMV(this.m_linearMass, Cdot_v2, b2FrictionJoint.SolveVelocityConstraints_s_impulseV).SelfNeg();
// b2Vec2 oldImpulse = m_linearImpulse;
var oldImpulseV = b2FrictionJoint.SolveVelocityConstraints_s_oldImpulseV.Copy(this.m_linearImpulse);
// m_linearImpulse += impulse;
this.m_linearImpulse.SelfAdd(impulseV);
var maxImpulse = h * this.m_maxForce;
if (this.m_linearImpulse.LengthSquared() > maxImpulse * maxImpulse) {
this.m_linearImpulse.Normalize();
this.m_linearImpulse.SelfMul(maxImpulse);
}
// impulse = m_linearImpulse - oldImpulse;
b2Vec2.SubVV(this.m_linearImpulse, oldImpulseV, impulseV);
// vA -= mA * impulse;
vA.SelfMulSub(mA, impulseV);
// wA -= iA * b2Cross(m_rA, impulse);
wA -= iA * b2Vec2.CrossVV(this.m_rA, impulseV);
// vB += mB * impulse;
vB.SelfMulAdd(mB, impulseV);
// wB += iB * b2Cross(m_rB, impulse);
wB += iB * b2Vec2.CrossVV(this.m_rB, impulseV);
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2FrictionJoint.prototype.SolvePositionConstraints = function (data) {
return true;
};
b2FrictionJoint.prototype.GetAnchorA = function (out) {
return this.m_bodyA.GetWorldPoint(this.m_localAnchorA, out);
};
b2FrictionJoint.prototype.GetAnchorB = function (out) {
return this.m_bodyB.GetWorldPoint(this.m_localAnchorB, out);
};
b2FrictionJoint.prototype.GetReactionForce = function (inv_dt, out) {
out.x = inv_dt * this.m_linearImpulse.x;
out.y = inv_dt * this.m_linearImpulse.y;
return out;
};
b2FrictionJoint.prototype.GetReactionTorque = function (inv_dt) {
return inv_dt * this.m_angularImpulse;
};
b2FrictionJoint.prototype.GetLocalAnchorA = function () { return this.m_localAnchorA; };
b2FrictionJoint.prototype.GetLocalAnchorB = function () { return this.m_localAnchorB; };
b2FrictionJoint.prototype.SetMaxForce = function (force) {
this.m_maxForce = force;
};
b2FrictionJoint.prototype.GetMaxForce = function () {
return this.m_maxForce;
};
b2FrictionJoint.prototype.SetMaxTorque = function (torque) {
this.m_maxTorque = torque;
};
b2FrictionJoint.prototype.GetMaxTorque = function () {
return this.m_maxTorque;
};
b2FrictionJoint.prototype.Dump = function (log) {
var indexA = this.m_bodyA.m_islandIndex;
var indexB = this.m_bodyB.m_islandIndex;
log(" const jd: b2FrictionJointDef = new b2FrictionJointDef();\n");
log(" jd.bodyA = bodies[%d];\n", indexA);
log(" jd.bodyB = bodies[%d];\n", indexB);
log(" jd.collideConnected = %s;\n", (this.m_collideConnected) ? ("true") : ("false"));
log(" jd.localAnchorA.Set(%.15f, %.15f);\n", this.m_localAnchorA.x, this.m_localAnchorA.y);
log(" jd.localAnchorB.Set(%.15f, %.15f);\n", this.m_localAnchorB.x, this.m_localAnchorB.y);
log(" jd.maxForce = %.15f;\n", this.m_maxForce);
log(" jd.maxTorque = %.15f;\n", this.m_maxTorque);
log(" joints[%d] = this.m_world.CreateJoint(jd);\n", this.m_index);
};
b2FrictionJoint.SolveVelocityConstraints_s_Cdot_v2 = new b2Vec2();
b2FrictionJoint.SolveVelocityConstraints_s_impulseV = new b2Vec2();
b2FrictionJoint.SolveVelocityConstraints_s_oldImpulseV = new b2Vec2();
return b2FrictionJoint;
}(b2Joint));
/*
* 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.
*/
/// Gear joint definition. This definition requires two existing
/// revolute or prismatic joints (any combination will work).
var b2GearJointDef = /** @class */ (function (_super) {
__extends(b2GearJointDef, _super);
function b2GearJointDef() {
var _this = _super.call(this, exports.b2JointType.e_gearJoint) || this;
_this.ratio = 1;
return _this;
}
return b2GearJointDef;
}(b2JointDef));
var b2GearJoint = /** @class */ (function (_super) {
__extends(b2GearJoint, _super);
function b2GearJoint(def) {
var _this = _super.call(this, def) || this;
_this.m_typeA = exports.b2JointType.e_unknownJoint;
_this.m_typeB = exports.b2JointType.e_unknownJoint;
// Solver shared
_this.m_localAnchorA = new b2Vec2();
_this.m_localAnchorB = new b2Vec2();
_this.m_localAnchorC = new b2Vec2();
_this.m_localAnchorD = new b2Vec2();
_this.m_localAxisC = new b2Vec2();
_this.m_localAxisD = new b2Vec2();
_this.m_referenceAngleA = 0;
_this.m_referenceAngleB = 0;
_this.m_constant = 0;
_this.m_ratio = 0;
_this.m_impulse = 0;
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_indexC = 0;
_this.m_indexD = 0;
_this.m_lcA = new b2Vec2();
_this.m_lcB = new b2Vec2();
_this.m_lcC = new b2Vec2();
_this.m_lcD = new b2Vec2();
_this.m_mA = 0;
_this.m_mB = 0;
_this.m_mC = 0;
_this.m_mD = 0;
_this.m_iA = 0;
_this.m_iB = 0;
_this.m_iC = 0;
_this.m_iD = 0;
_this.m_JvAC = new b2Vec2();
_this.m_JvBD = new b2Vec2();
_this.m_JwA = 0;
_this.m_JwB = 0;
_this.m_JwC = 0;
_this.m_JwD = 0;
_this.m_mass = 0;
_this.m_qA = new b2Rot();
_this.m_qB = new b2Rot();
_this.m_qC = new b2Rot();
_this.m_qD = new b2Rot();
_this.m_lalcA = new b2Vec2();
_this.m_lalcB = new b2Vec2();
_this.m_lalcC = new b2Vec2();
_this.m_lalcD = new b2Vec2();
_this.m_joint1 = def.joint1;
_this.m_joint2 = def.joint2;
_this.m_typeA = _this.m_joint1.GetType();
_this.m_typeB = _this.m_joint2.GetType();
// DEBUG: b2Assert(this.m_typeA === b2JointType.e_revoluteJoint || this.m_typeA === b2JointType.e_prismaticJoint);
// DEBUG: b2Assert(this.m_typeB === b2JointType.e_revoluteJoint || this.m_typeB === b2JointType.e_prismaticJoint);
var coordinateA, coordinateB;
// TODO_ERIN there might be some problem with the joint edges in b2Joint.
_this.m_bodyC = _this.m_joint1.GetBodyA();
_this.m_bodyA = _this.m_joint1.GetBodyB();
// Get geometry of joint1
var xfA = _this.m_bodyA.m_xf;
var aA = _this.m_bodyA.m_sweep.a;
var xfC = _this.m_bodyC.m_xf;
var aC = _this.m_bodyC.m_sweep.a;
if (_this.m_typeA === exports.b2JointType.e_revoluteJoint) {
var revolute = def.joint1;
_this.m_localAnchorC.Copy(revolute.m_localAnchorA);
_this.m_localAnchorA.Copy(revolute.m_localAnchorB);
_this.m_referenceAngleA = revolute.m_referenceAngle;
_this.m_localAxisC.SetZero();
coordinateA = aA - aC - _this.m_referenceAngleA;
}
else {
var prismatic = def.joint1;
_this.m_localAnchorC.Copy(prismatic.m_localAnchorA);
_this.m_localAnchorA.Copy(prismatic.m_localAnchorB);
_this.m_referenceAngleA = prismatic.m_referenceAngle;
_this.m_localAxisC.Copy(prismatic.m_localXAxisA);
// b2Vec2 pC = m_localAnchorC;
var pC = _this.m_localAnchorC;
// b2Vec2 pA = b2MulT(xfC.q, b2Mul(xfA.q, m_localAnchorA) + (xfA.p - xfC.p));
var pA = b2Rot.MulTRV(xfC.q, b2Vec2.AddVV(b2Rot.MulRV(xfA.q, _this.m_localAnchorA, b2Vec2.s_t0), b2Vec2.SubVV(xfA.p, xfC.p, b2Vec2.s_t1), b2Vec2.s_t0), b2Vec2.s_t0); // pA uses s_t0
// coordinateA = b2Dot(pA - pC, m_localAxisC);
coordinateA = b2Vec2.DotVV(b2Vec2.SubVV(pA, pC, b2Vec2.s_t0), _this.m_localAxisC);
}
_this.m_bodyD = _this.m_joint2.GetBodyA();
_this.m_bodyB = _this.m_joint2.GetBodyB();
// Get geometry of joint2
var xfB = _this.m_bodyB.m_xf;
var aB = _this.m_bodyB.m_sweep.a;
var xfD = _this.m_bodyD.m_xf;
var aD = _this.m_bodyD.m_sweep.a;
if (_this.m_typeB === exports.b2JointType.e_revoluteJoint) {
var revolute = def.joint2;
_this.m_localAnchorD.Copy(revolute.m_localAnchorA);
_this.m_localAnchorB.Copy(revolute.m_localAnchorB);
_this.m_referenceAngleB = revolute.m_referenceAngle;
_this.m_localAxisD.SetZero();
coordinateB = aB - aD - _this.m_referenceAngleB;
}
else {
var prismatic = def.joint2;
_this.m_localAnchorD.Copy(prismatic.m_localAnchorA);
_this.m_localAnchorB.Copy(prismatic.m_localAnchorB);
_this.m_referenceAngleB = prismatic.m_referenceAngle;
_this.m_localAxisD.Copy(prismatic.m_localXAxisA);
// b2Vec2 pD = m_localAnchorD;
var pD = _this.m_localAnchorD;
// b2Vec2 pB = b2MulT(xfD.q, b2Mul(xfB.q, m_localAnchorB) + (xfB.p - xfD.p));
var pB = b2Rot.MulTRV(xfD.q, b2Vec2.AddVV(b2Rot.MulRV(xfB.q, _this.m_localAnchorB, b2Vec2.s_t0), b2Vec2.SubVV(xfB.p, xfD.p, b2Vec2.s_t1), b2Vec2.s_t0), b2Vec2.s_t0); // pB uses s_t0
// coordinateB = b2Dot(pB - pD, m_localAxisD);
coordinateB = b2Vec2.DotVV(b2Vec2.SubVV(pB, pD, b2Vec2.s_t0), _this.m_localAxisD);
}
_this.m_ratio = b2Maybe(def.ratio, 1);
_this.m_constant = coordinateA + _this.m_ratio * coordinateB;
_this.m_impulse = 0;
return _this;
}
b2GearJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexA = this.m_bodyA.m_islandIndex;
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_indexC = this.m_bodyC.m_islandIndex;
this.m_indexD = this.m_bodyD.m_islandIndex;
this.m_lcA.Copy(this.m_bodyA.m_sweep.localCenter);
this.m_lcB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_lcC.Copy(this.m_bodyC.m_sweep.localCenter);
this.m_lcD.Copy(this.m_bodyD.m_sweep.localCenter);
this.m_mA = this.m_bodyA.m_invMass;
this.m_mB = this.m_bodyB.m_invMass;
this.m_mC = this.m_bodyC.m_invMass;
this.m_mD = this.m_bodyD.m_invMass;
this.m_iA = this.m_bodyA.m_invI;
this.m_iB = this.m_bodyB.m_invI;
this.m_iC = this.m_bodyC.m_invI;
this.m_iD = this.m_bodyD.m_invI;
var aA = data.positions[this.m_indexA].a;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var aC = data.positions[this.m_indexC].a;
var vC = data.velocities[this.m_indexC].v;
var wC = data.velocities[this.m_indexC].w;
var aD = data.positions[this.m_indexD].a;
var vD = data.velocities[this.m_indexD].v;
var wD = data.velocities[this.m_indexD].w;
// b2Rot qA(aA), qB(aB), qC(aC), qD(aD);
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB), qC = this.m_qC.SetAngle(aC), qD = this.m_qD.SetAngle(aD);
this.m_mass = 0;
if (this.m_typeA === exports.b2JointType.e_revoluteJoint) {
this.m_JvAC.SetZero();
this.m_JwA = 1;
this.m_JwC = 1;
this.m_mass += this.m_iA + this.m_iC;
}
else {
// b2Vec2 u = b2Mul(qC, m_localAxisC);
var u = b2Rot.MulRV(qC, this.m_localAxisC, b2GearJoint.InitVelocityConstraints_s_u);
// b2Vec2 rC = b2Mul(qC, m_localAnchorC - m_lcC);
b2Vec2.SubVV(this.m_localAnchorC, this.m_lcC, this.m_lalcC);
var rC = b2Rot.MulRV(qC, this.m_lalcC, b2GearJoint.InitVelocityConstraints_s_rC);
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_lcA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_lcA, this.m_lalcA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, b2GearJoint.InitVelocityConstraints_s_rA);
// m_JvAC = u;
this.m_JvAC.Copy(u);
// m_JwC = b2Cross(rC, u);
this.m_JwC = b2Vec2.CrossVV(rC, u);
// m_JwA = b2Cross(rA, u);
this.m_JwA = b2Vec2.CrossVV(rA, u);
this.m_mass += this.m_mC + this.m_mA + this.m_iC * this.m_JwC * this.m_JwC + this.m_iA * this.m_JwA * this.m_JwA;
}
if (this.m_typeB === exports.b2JointType.e_revoluteJoint) {
this.m_JvBD.SetZero();
this.m_JwB = this.m_ratio;
this.m_JwD = this.m_ratio;
this.m_mass += this.m_ratio * this.m_ratio * (this.m_iB + this.m_iD);
}
else {
// b2Vec2 u = b2Mul(qD, m_localAxisD);
var u = b2Rot.MulRV(qD, this.m_localAxisD, b2GearJoint.InitVelocityConstraints_s_u);
// b2Vec2 rD = b2Mul(qD, m_localAnchorD - m_lcD);
b2Vec2.SubVV(this.m_localAnchorD, this.m_lcD, this.m_lalcD);
var rD = b2Rot.MulRV(qD, this.m_lalcD, b2GearJoint.InitVelocityConstraints_s_rD);
// b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_lcB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_lcB, this.m_lalcB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, b2GearJoint.InitVelocityConstraints_s_rB);
// m_JvBD = m_ratio * u;
b2Vec2.MulSV(this.m_ratio, u, this.m_JvBD);
// m_JwD = m_ratio * b2Cross(rD, u);
this.m_JwD = this.m_ratio * b2Vec2.CrossVV(rD, u);
// m_JwB = m_ratio * b2Cross(rB, u);
this.m_JwB = this.m_ratio * b2Vec2.CrossVV(rB, u);
this.m_mass += this.m_ratio * this.m_ratio * (this.m_mD + this.m_mB) + this.m_iD * this.m_JwD * this.m_JwD + this.m_iB * this.m_JwB * this.m_JwB;
}
// Compute effective mass.
this.m_mass = this.m_mass > 0 ? 1 / this.m_mass : 0;
if (data.step.warmStarting) {
// vA += (m_mA * m_impulse) * m_JvAC;
vA.SelfMulAdd(this.m_mA * this.m_impulse, this.m_JvAC);
wA += this.m_iA * this.m_impulse * this.m_JwA;
// vB += (m_mB * m_impulse) * m_JvBD;
vB.SelfMulAdd(this.m_mB * this.m_impulse, this.m_JvBD);
wB += this.m_iB * this.m_impulse * this.m_JwB;
// vC -= (m_mC * m_impulse) * m_JvAC;
vC.SelfMulSub(this.m_mC * this.m_impulse, this.m_JvAC);
wC -= this.m_iC * this.m_impulse * this.m_JwC;
// vD -= (m_mD * m_impulse) * m_JvBD;
vD.SelfMulSub(this.m_mD * this.m_impulse, this.m_JvBD);
wD -= this.m_iD * this.m_impulse * this.m_JwD;
}
else {
this.m_impulse = 0;
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
// data.velocities[this.m_indexC].v = vC;
data.velocities[this.m_indexC].w = wC;
// data.velocities[this.m_indexD].v = vD;
data.velocities[this.m_indexD].w = wD;
};
b2GearJoint.prototype.SolveVelocityConstraints = function (data) {
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var vC = data.velocities[this.m_indexC].v;
var wC = data.velocities[this.m_indexC].w;
var vD = data.velocities[this.m_indexD].v;
var wD = data.velocities[this.m_indexD].w;
// float32 Cdot = b2Dot(m_JvAC, vA - vC) + b2Dot(m_JvBD, vB - vD);
var Cdot = b2Vec2.DotVV(this.m_JvAC, b2Vec2.SubVV(vA, vC, b2Vec2.s_t0)) +
b2Vec2.DotVV(this.m_JvBD, b2Vec2.SubVV(vB, vD, b2Vec2.s_t0));
Cdot += (this.m_JwA * wA - this.m_JwC * wC) + (this.m_JwB * wB - this.m_JwD * wD);
var impulse = -this.m_mass * Cdot;
this.m_impulse += impulse;
// vA += (m_mA * impulse) * m_JvAC;
vA.SelfMulAdd((this.m_mA * impulse), this.m_JvAC);
wA += this.m_iA * impulse * this.m_JwA;
// vB += (m_mB * impulse) * m_JvBD;
vB.SelfMulAdd((this.m_mB * impulse), this.m_JvBD);
wB += this.m_iB * impulse * this.m_JwB;
// vC -= (m_mC * impulse) * m_JvAC;
vC.SelfMulSub((this.m_mC * impulse), this.m_JvAC);
wC -= this.m_iC * impulse * this.m_JwC;
// vD -= (m_mD * impulse) * m_JvBD;
vD.SelfMulSub((this.m_mD * impulse), this.m_JvBD);
wD -= this.m_iD * impulse * this.m_JwD;
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
// data.velocities[this.m_indexC].v = vC;
data.velocities[this.m_indexC].w = wC;
// data.velocities[this.m_indexD].v = vD;
data.velocities[this.m_indexD].w = wD;
};
b2GearJoint.prototype.SolvePositionConstraints = function (data) {
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var cC = data.positions[this.m_indexC].c;
var aC = data.positions[this.m_indexC].a;
var cD = data.positions[this.m_indexD].c;
var aD = data.positions[this.m_indexD].a;
// b2Rot qA(aA), qB(aB), qC(aC), qD(aD);
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB), qC = this.m_qC.SetAngle(aC), qD = this.m_qD.SetAngle(aD);
var linearError = 0;
var coordinateA, coordinateB;
var JvAC = this.m_JvAC, JvBD = this.m_JvBD;
var JwA, JwB, JwC, JwD;
var mass = 0;
if (this.m_typeA === exports.b2JointType.e_revoluteJoint) {
JvAC.SetZero();
JwA = 1;
JwC = 1;
mass += this.m_iA + this.m_iC;
coordinateA = aA - aC - this.m_referenceAngleA;
}
else {
// b2Vec2 u = b2Mul(qC, m_localAxisC);
var u = b2Rot.MulRV(qC, this.m_localAxisC, b2GearJoint.SolvePositionConstraints_s_u);
// b2Vec2 rC = b2Mul(qC, m_localAnchorC - m_lcC);
var rC = b2Rot.MulRV(qC, this.m_lalcC, b2GearJoint.SolvePositionConstraints_s_rC);
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_lcA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, b2GearJoint.SolvePositionConstraints_s_rA);
// JvAC = u;
JvAC.Copy(u);
// JwC = b2Cross(rC, u);
JwC = b2Vec2.CrossVV(rC, u);
// JwA = b2Cross(rA, u);
JwA = b2Vec2.CrossVV(rA, u);
mass += this.m_mC + this.m_mA + this.m_iC * JwC * JwC + this.m_iA * JwA * JwA;
// b2Vec2 pC = m_localAnchorC - m_lcC;
var pC = this.m_lalcC;
// b2Vec2 pA = b2MulT(qC, rA + (cA - cC));
var pA = b2Rot.MulTRV(qC, b2Vec2.AddVV(rA, b2Vec2.SubVV(cA, cC, b2Vec2.s_t0), b2Vec2.s_t0), b2Vec2.s_t0); // pA uses s_t0
// coordinateA = b2Dot(pA - pC, m_localAxisC);
coordinateA = b2Vec2.DotVV(b2Vec2.SubVV(pA, pC, b2Vec2.s_t0), this.m_localAxisC);
}
if (this.m_typeB === exports.b2JointType.e_revoluteJoint) {
JvBD.SetZero();
JwB = this.m_ratio;
JwD = this.m_ratio;
mass += this.m_ratio * this.m_ratio * (this.m_iB + this.m_iD);
coordinateB = aB - aD - this.m_referenceAngleB;
}
else {
// b2Vec2 u = b2Mul(qD, m_localAxisD);
var u = b2Rot.MulRV(qD, this.m_localAxisD, b2GearJoint.SolvePositionConstraints_s_u);
// b2Vec2 rD = b2Mul(qD, m_localAnchorD - m_lcD);
var rD = b2Rot.MulRV(qD, this.m_lalcD, b2GearJoint.SolvePositionConstraints_s_rD);
// b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_lcB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, b2GearJoint.SolvePositionConstraints_s_rB);
// JvBD = m_ratio * u;
b2Vec2.MulSV(this.m_ratio, u, JvBD);
// JwD = m_ratio * b2Cross(rD, u);
JwD = this.m_ratio * b2Vec2.CrossVV(rD, u);
// JwB = m_ratio * b2Cross(rB, u);
JwB = this.m_ratio * b2Vec2.CrossVV(rB, u);
mass += this.m_ratio * this.m_ratio * (this.m_mD + this.m_mB) + this.m_iD * JwD * JwD + this.m_iB * JwB * JwB;
// b2Vec2 pD = m_localAnchorD - m_lcD;
var pD = this.m_lalcD;
// b2Vec2 pB = b2MulT(qD, rB + (cB - cD));
var pB = b2Rot.MulTRV(qD, b2Vec2.AddVV(rB, b2Vec2.SubVV(cB, cD, b2Vec2.s_t0), b2Vec2.s_t0), b2Vec2.s_t0); // pB uses s_t0
// coordinateB = b2Dot(pB - pD, m_localAxisD);
coordinateB = b2Vec2.DotVV(b2Vec2.SubVV(pB, pD, b2Vec2.s_t0), this.m_localAxisD);
}
var C = (coordinateA + this.m_ratio * coordinateB) - this.m_constant;
var impulse = 0;
if (mass > 0) {
impulse = -C / mass;
}
// cA += m_mA * impulse * JvAC;
cA.SelfMulAdd(this.m_mA * impulse, JvAC);
aA += this.m_iA * impulse * JwA;
// cB += m_mB * impulse * JvBD;
cB.SelfMulAdd(this.m_mB * impulse, JvBD);
aB += this.m_iB * impulse * JwB;
// cC -= m_mC * impulse * JvAC;
cC.SelfMulSub(this.m_mC * impulse, JvAC);
aC -= this.m_iC * impulse * JwC;
// cD -= m_mD * impulse * JvBD;
cD.SelfMulSub(this.m_mD * impulse, JvBD);
aD -= this.m_iD * impulse * JwD;
// data.positions[this.m_indexA].c = cA;
data.positions[this.m_indexA].a = aA;
// data.positions[this.m_indexB].c = cB;
data.positions[this.m_indexB].a = aB;
// data.positions[this.m_indexC].c = cC;
data.positions[this.m_indexC].a = aC;
// data.positions[this.m_indexD].c = cD;
data.positions[this.m_indexD].a = aD;
// TODO_ERIN not implemented
return linearError < b2_linearSlop;
};
b2GearJoint.prototype.GetAnchorA = function (out) {
return this.m_bodyA.GetWorldPoint(this.m_localAnchorA, out);
};
b2GearJoint.prototype.GetAnchorB = function (out) {
return this.m_bodyB.GetWorldPoint(this.m_localAnchorB, out);
};
b2GearJoint.prototype.GetReactionForce = function (inv_dt, out) {
// b2Vec2 P = m_impulse * m_JvAC;
// return inv_dt * P;
return b2Vec2.MulSV(inv_dt * this.m_impulse, this.m_JvAC, out);
};
b2GearJoint.prototype.GetReactionTorque = function (inv_dt) {
// float32 L = m_impulse * m_JwA;
// return inv_dt * L;
return inv_dt * this.m_impulse * this.m_JwA;
};
b2GearJoint.prototype.GetJoint1 = function () { return this.m_joint1; };
b2GearJoint.prototype.GetJoint2 = function () { return this.m_joint2; };
b2GearJoint.prototype.GetRatio = function () {
return this.m_ratio;
};
b2GearJoint.prototype.SetRatio = function (ratio) {
// DEBUG: b2Assert(b2IsValid(ratio));
this.m_ratio = ratio;
};
b2GearJoint.prototype.Dump = function (log) {
var indexA = this.m_bodyA.m_islandIndex;
var indexB = this.m_bodyB.m_islandIndex;
var index1 = this.m_joint1.m_index;
var index2 = this.m_joint2.m_index;
log(" const jd: b2GearJointDef = new b2GearJointDef();\n");
log(" jd.bodyA = bodies[%d];\n", indexA);
log(" jd.bodyB = bodies[%d];\n", indexB);
log(" jd.collideConnected = %s;\n", (this.m_collideConnected) ? ("true") : ("false"));
log(" jd.joint1 = joints[%d];\n", index1);
log(" jd.joint2 = joints[%d];\n", index2);
log(" jd.ratio = %.15f;\n", this.m_ratio);
log(" joints[%d] = this.m_world.CreateJoint(jd);\n", this.m_index);
};
b2GearJoint.InitVelocityConstraints_s_u = new b2Vec2();
b2GearJoint.InitVelocityConstraints_s_rA = new b2Vec2();
b2GearJoint.InitVelocityConstraints_s_rB = new b2Vec2();
b2GearJoint.InitVelocityConstraints_s_rC = new b2Vec2();
b2GearJoint.InitVelocityConstraints_s_rD = new b2Vec2();
b2GearJoint.SolvePositionConstraints_s_u = new b2Vec2();
b2GearJoint.SolvePositionConstraints_s_rA = new b2Vec2();
b2GearJoint.SolvePositionConstraints_s_rB = new b2Vec2();
b2GearJoint.SolvePositionConstraints_s_rC = new b2Vec2();
b2GearJoint.SolvePositionConstraints_s_rD = new b2Vec2();
return b2GearJoint;
}(b2Joint));
/*
* Copyright (c) 2006-2012 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.
*/
var b2MotorJointDef = /** @class */ (function (_super) {
__extends(b2MotorJointDef, _super);
function b2MotorJointDef() {
var _this = _super.call(this, exports.b2JointType.e_motorJoint) || this;
_this.linearOffset = new b2Vec2(0, 0);
_this.angularOffset = 0;
_this.maxForce = 1;
_this.maxTorque = 1;
_this.correctionFactor = 0.3;
return _this;
}
b2MotorJointDef.prototype.Initialize = function (bA, bB) {
this.bodyA = bA;
this.bodyB = bB;
// b2Vec2 xB = bodyB->GetPosition();
// linearOffset = bodyA->GetLocalPoint(xB);
this.bodyA.GetLocalPoint(this.bodyB.GetPosition(), this.linearOffset);
var angleA = this.bodyA.GetAngle();
var angleB = this.bodyB.GetAngle();
this.angularOffset = angleB - angleA;
};
return b2MotorJointDef;
}(b2JointDef));
var b2MotorJoint = /** @class */ (function (_super) {
__extends(b2MotorJoint, _super);
function b2MotorJoint(def) {
var _this = _super.call(this, def) || this;
// Solver shared
_this.m_linearOffset = new b2Vec2();
_this.m_angularOffset = 0;
_this.m_linearImpulse = new b2Vec2();
_this.m_angularImpulse = 0;
_this.m_maxForce = 0;
_this.m_maxTorque = 0;
_this.m_correctionFactor = 0.3;
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_rA = new b2Vec2();
_this.m_rB = new b2Vec2();
_this.m_localCenterA = new b2Vec2();
_this.m_localCenterB = new b2Vec2();
_this.m_linearError = new b2Vec2();
_this.m_angularError = 0;
_this.m_invMassA = 0;
_this.m_invMassB = 0;
_this.m_invIA = 0;
_this.m_invIB = 0;
_this.m_linearMass = new b2Mat22();
_this.m_angularMass = 0;
_this.m_qA = new b2Rot();
_this.m_qB = new b2Rot();
_this.m_K = new b2Mat22();
_this.m_linearOffset.Copy(b2Maybe(def.linearOffset, b2Vec2.ZERO));
_this.m_linearImpulse.SetZero();
_this.m_maxForce = b2Maybe(def.maxForce, 0);
_this.m_maxTorque = b2Maybe(def.maxTorque, 0);
_this.m_correctionFactor = b2Maybe(def.correctionFactor, 0.3);
return _this;
}
b2MotorJoint.prototype.GetAnchorA = function (out) {
var pos = this.m_bodyA.GetPosition();
out.x = pos.x;
out.y = pos.y;
return out;
};
b2MotorJoint.prototype.GetAnchorB = function (out) {
var pos = this.m_bodyB.GetPosition();
out.x = pos.x;
out.y = pos.y;
return out;
};
b2MotorJoint.prototype.GetReactionForce = function (inv_dt, out) {
// return inv_dt * m_linearImpulse;
return b2Vec2.MulSV(inv_dt, this.m_linearImpulse, out);
};
b2MotorJoint.prototype.GetReactionTorque = function (inv_dt) {
return inv_dt * this.m_angularImpulse;
};
b2MotorJoint.prototype.SetLinearOffset = function (linearOffset) {
if (!b2Vec2.IsEqualToV(linearOffset, this.m_linearOffset)) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_linearOffset.Copy(linearOffset);
}
};
b2MotorJoint.prototype.GetLinearOffset = function () {
return this.m_linearOffset;
};
b2MotorJoint.prototype.SetAngularOffset = function (angularOffset) {
if (angularOffset !== this.m_angularOffset) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_angularOffset = angularOffset;
}
};
b2MotorJoint.prototype.GetAngularOffset = function () {
return this.m_angularOffset;
};
b2MotorJoint.prototype.SetMaxForce = function (force) {
// DEBUG: b2Assert(b2IsValid(force) && force >= 0);
this.m_maxForce = force;
};
b2MotorJoint.prototype.GetMaxForce = function () {
return this.m_maxForce;
};
b2MotorJoint.prototype.SetMaxTorque = function (torque) {
// DEBUG: b2Assert(b2IsValid(torque) && torque >= 0);
this.m_maxTorque = torque;
};
b2MotorJoint.prototype.GetMaxTorque = function () {
return this.m_maxTorque;
};
b2MotorJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexA = this.m_bodyA.m_islandIndex;
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_localCenterA.Copy(this.m_bodyA.m_sweep.localCenter);
this.m_localCenterB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_invMassA = this.m_bodyA.m_invMass;
this.m_invMassB = this.m_bodyB.m_invMass;
this.m_invIA = this.m_bodyA.m_invI;
this.m_invIB = this.m_bodyB.m_invI;
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// Compute the effective mass matrix.
// this.m_rA = b2Mul(qA, m_linearOffset - this.m_localCenterA);
var rA = b2Rot.MulRV(qA, b2Vec2.SubVV(this.m_linearOffset, this.m_localCenterA, b2Vec2.s_t0), this.m_rA);
// this.m_rB = b2Mul(qB, -this.m_localCenterB);
var rB = b2Rot.MulRV(qB, b2Vec2.NegV(this.m_localCenterB, b2Vec2.s_t0), this.m_rB);
// J = [-I -r1_skew I r2_skew]
// r_skew = [-ry; rx]
// Matlab
// K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB]
// [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB]
// [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB]
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
// Upper 2 by 2 of K for point to point
var K = this.m_K;
K.ex.x = mA + mB + iA * rA.y * rA.y + iB * rB.y * rB.y;
K.ex.y = -iA * rA.x * rA.y - iB * rB.x * rB.y;
K.ey.x = K.ex.y;
K.ey.y = mA + mB + iA * rA.x * rA.x + iB * rB.x * rB.x;
// this.m_linearMass = K.GetInverse();
K.GetInverse(this.m_linearMass);
this.m_angularMass = iA + iB;
if (this.m_angularMass > 0) {
this.m_angularMass = 1 / this.m_angularMass;
}
// this.m_linearError = cB + rB - cA - rA;
b2Vec2.SubVV(b2Vec2.AddVV(cB, rB, b2Vec2.s_t0), b2Vec2.AddVV(cA, rA, b2Vec2.s_t1), this.m_linearError);
this.m_angularError = aB - aA - this.m_angularOffset;
if (data.step.warmStarting) {
// Scale impulses to support a variable time step.
// this.m_linearImpulse *= data.step.dtRatio;
this.m_linearImpulse.SelfMul(data.step.dtRatio);
this.m_angularImpulse *= data.step.dtRatio;
// b2Vec2 P(this.m_linearImpulse.x, this.m_linearImpulse.y);
var P = this.m_linearImpulse;
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * (b2Vec2.CrossVV(rA, P) + this.m_angularImpulse);
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * (b2Vec2.CrossVV(rB, P) + this.m_angularImpulse);
}
else {
this.m_linearImpulse.SetZero();
this.m_angularImpulse = 0;
}
// data.velocities[this.m_indexA].v = vA; // vA is a reference
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB; // vB is a reference
data.velocities[this.m_indexB].w = wB;
};
b2MotorJoint.prototype.SolveVelocityConstraints = function (data) {
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
var h = data.step.dt;
var inv_h = data.step.inv_dt;
// Solve angular friction
{
var Cdot = wB - wA + inv_h * this.m_correctionFactor * this.m_angularError;
var impulse = -this.m_angularMass * Cdot;
var oldImpulse = this.m_angularImpulse;
var maxImpulse = h * this.m_maxTorque;
this.m_angularImpulse = b2Clamp(this.m_angularImpulse + impulse, -maxImpulse, maxImpulse);
impulse = this.m_angularImpulse - oldImpulse;
wA -= iA * impulse;
wB += iB * impulse;
}
// Solve linear friction
{
var rA = this.m_rA;
var rB = this.m_rB;
// b2Vec2 Cdot = vB + b2Vec2.CrossSV(wB, rB) - vA - b2Vec2.CrossSV(wA, rA) + inv_h * this.m_correctionFactor * this.m_linearError;
var Cdot_v2 = b2Vec2.AddVV(b2Vec2.SubVV(b2Vec2.AddVV(vB, b2Vec2.CrossSV(wB, rB, b2Vec2.s_t0), b2Vec2.s_t0), b2Vec2.AddVV(vA, b2Vec2.CrossSV(wA, rA, b2Vec2.s_t1), b2Vec2.s_t1), b2Vec2.s_t2), b2Vec2.MulSV(inv_h * this.m_correctionFactor, this.m_linearError, b2Vec2.s_t3), b2MotorJoint.SolveVelocityConstraints_s_Cdot_v2);
// b2Vec2 impulse = -b2Mul(this.m_linearMass, Cdot);
var impulse_v2 = b2Mat22.MulMV(this.m_linearMass, Cdot_v2, b2MotorJoint.SolveVelocityConstraints_s_impulse_v2).SelfNeg();
// b2Vec2 oldImpulse = this.m_linearImpulse;
var oldImpulse_v2 = b2MotorJoint.SolveVelocityConstraints_s_oldImpulse_v2.Copy(this.m_linearImpulse);
// this.m_linearImpulse += impulse;
this.m_linearImpulse.SelfAdd(impulse_v2);
var maxImpulse = h * this.m_maxForce;
if (this.m_linearImpulse.LengthSquared() > maxImpulse * maxImpulse) {
this.m_linearImpulse.Normalize();
// this.m_linearImpulse *= maxImpulse;
this.m_linearImpulse.SelfMul(maxImpulse);
}
// impulse = this.m_linearImpulse - oldImpulse;
b2Vec2.SubVV(this.m_linearImpulse, oldImpulse_v2, impulse_v2);
// vA -= mA * impulse;
vA.SelfMulSub(mA, impulse_v2);
// wA -= iA * b2Vec2.CrossVV(rA, impulse);
wA -= iA * b2Vec2.CrossVV(rA, impulse_v2);
// vB += mB * impulse;
vB.SelfMulAdd(mB, impulse_v2);
// wB += iB * b2Vec2.CrossVV(rB, impulse);
wB += iB * b2Vec2.CrossVV(rB, impulse_v2);
}
// data.velocities[this.m_indexA].v = vA; // vA is a reference
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB; // vB is a reference
data.velocities[this.m_indexB].w = wB;
};
b2MotorJoint.prototype.SolvePositionConstraints = function (data) {
return true;
};
b2MotorJoint.prototype.Dump = function (log) {
var indexA = this.m_bodyA.m_islandIndex;
var indexB = this.m_bodyB.m_islandIndex;
log(" const jd: b2MotorJointDef = new b2MotorJointDef();\n");
log(" jd.bodyA = bodies[%d];\n", indexA);
log(" jd.bodyB = bodies[%d];\n", indexB);
log(" jd.collideConnected = %s;\n", (this.m_collideConnected) ? ("true") : ("false"));
log(" jd.linearOffset.Set(%.15f, %.15f);\n", this.m_linearOffset.x, this.m_linearOffset.y);
log(" jd.angularOffset = %.15f;\n", this.m_angularOffset);
log(" jd.maxForce = %.15f;\n", this.m_maxForce);
log(" jd.maxTorque = %.15f;\n", this.m_maxTorque);
log(" jd.correctionFactor = %.15f;\n", this.m_correctionFactor);
log(" joints[%d] = this.m_world.CreateJoint(jd);\n", this.m_index);
};
b2MotorJoint.SolveVelocityConstraints_s_Cdot_v2 = new b2Vec2();
b2MotorJoint.SolveVelocityConstraints_s_impulse_v2 = new b2Vec2();
b2MotorJoint.SolveVelocityConstraints_s_oldImpulse_v2 = new b2Vec2();
return b2MotorJoint;
}(b2Joint));
/*
* Copyright (c) 2006-2007 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.
*/
/// Mouse joint definition. This requires a world target point,
/// tuning parameters, and the time step.
var b2MouseJointDef = /** @class */ (function (_super) {
__extends(b2MouseJointDef, _super);
function b2MouseJointDef() {
var _this = _super.call(this, exports.b2JointType.e_mouseJoint) || this;
_this.target = new b2Vec2();
_this.maxForce = 0;
_this.frequencyHz = 5;
_this.dampingRatio = 0.7;
return _this;
}
return b2MouseJointDef;
}(b2JointDef));
var b2MouseJoint = /** @class */ (function (_super) {
__extends(b2MouseJoint, _super);
function b2MouseJoint(def) {
var _this = _super.call(this, def) || this;
_this.m_localAnchorB = new b2Vec2();
_this.m_targetA = new b2Vec2();
_this.m_frequencyHz = 0;
_this.m_dampingRatio = 0;
_this.m_beta = 0;
// Solver shared
_this.m_impulse = new b2Vec2();
_this.m_maxForce = 0;
_this.m_gamma = 0;
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_rB = new b2Vec2();
_this.m_localCenterB = new b2Vec2();
_this.m_invMassB = 0;
_this.m_invIB = 0;
_this.m_mass = new b2Mat22();
_this.m_C = new b2Vec2();
_this.m_qB = new b2Rot();
_this.m_lalcB = new b2Vec2();
_this.m_K = new b2Mat22();
_this.m_targetA.Copy(b2Maybe(def.target, b2Vec2.ZERO));
// DEBUG: b2Assert(this.m_targetA.IsValid());
b2Transform.MulTXV(_this.m_bodyB.GetTransform(), _this.m_targetA, _this.m_localAnchorB);
_this.m_maxForce = b2Maybe(def.maxForce, 0);
// DEBUG: b2Assert(b2IsValid(this.m_maxForce) && this.m_maxForce >= 0);
_this.m_impulse.SetZero();
_this.m_frequencyHz = b2Maybe(def.frequencyHz, 0);
// DEBUG: b2Assert(b2IsValid(this.m_frequencyHz) && this.m_frequencyHz >= 0);
_this.m_dampingRatio = b2Maybe(def.dampingRatio, 0);
// DEBUG: b2Assert(b2IsValid(this.m_dampingRatio) && this.m_dampingRatio >= 0);
_this.m_beta = 0;
_this.m_gamma = 0;
return _this;
}
b2MouseJoint.prototype.SetTarget = function (target) {
if (!this.m_bodyB.IsAwake()) {
this.m_bodyB.SetAwake(true);
}
this.m_targetA.Copy(target);
};
b2MouseJoint.prototype.GetTarget = function () {
return this.m_targetA;
};
b2MouseJoint.prototype.SetMaxForce = function (maxForce) {
this.m_maxForce = maxForce;
};
b2MouseJoint.prototype.GetMaxForce = function () {
return this.m_maxForce;
};
b2MouseJoint.prototype.SetFrequency = function (hz) {
this.m_frequencyHz = hz;
};
b2MouseJoint.prototype.GetFrequency = function () {
return this.m_frequencyHz;
};
b2MouseJoint.prototype.SetDampingRatio = function (ratio) {
this.m_dampingRatio = ratio;
};
b2MouseJoint.prototype.GetDampingRatio = function () {
return this.m_dampingRatio;
};
b2MouseJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_localCenterB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_invMassB = this.m_bodyB.m_invMass;
this.m_invIB = this.m_bodyB.m_invI;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var qB = this.m_qB.SetAngle(aB);
var mass = this.m_bodyB.GetMass();
// Frequency
var omega = 2 * b2_pi * this.m_frequencyHz;
// Damping coefficient
var d = 2 * mass * this.m_dampingRatio * omega;
// Spring stiffness
var k = mass * (omega * omega);
// magic formulas
// gamma has units of inverse mass.
// beta has units of inverse time.
var h = data.step.dt;
// DEBUG: b2Assert(d + h * k > b2_epsilon);
this.m_gamma = h * (d + h * k);
if (this.m_gamma !== 0) {
this.m_gamma = 1 / this.m_gamma;
}
this.m_beta = h * k * this.m_gamma;
// Compute the effective mass matrix.
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)]
// = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y]
// [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x]
var K = this.m_K;
K.ex.x = this.m_invMassB + this.m_invIB * this.m_rB.y * this.m_rB.y + this.m_gamma;
K.ex.y = -this.m_invIB * this.m_rB.x * this.m_rB.y;
K.ey.x = K.ex.y;
K.ey.y = this.m_invMassB + this.m_invIB * this.m_rB.x * this.m_rB.x + this.m_gamma;
K.GetInverse(this.m_mass);
// m_C = cB + m_rB - m_targetA;
this.m_C.x = cB.x + this.m_rB.x - this.m_targetA.x;
this.m_C.y = cB.y + this.m_rB.y - this.m_targetA.y;
// m_C *= m_beta;
this.m_C.SelfMul(this.m_beta);
// Cheat with some damping
wB *= 0.98;
if (data.step.warmStarting) {
this.m_impulse.SelfMul(data.step.dtRatio);
// vB += m_invMassB * m_impulse;
vB.x += this.m_invMassB * this.m_impulse.x;
vB.y += this.m_invMassB * this.m_impulse.y;
wB += this.m_invIB * b2Vec2.CrossVV(this.m_rB, this.m_impulse);
}
else {
this.m_impulse.SetZero();
}
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2MouseJoint.prototype.SolveVelocityConstraints = function (data) {
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
// Cdot = v + cross(w, r)
// b2Vec2 Cdot = vB + b2Cross(wB, m_rB);
var Cdot = b2Vec2.AddVCrossSV(vB, wB, this.m_rB, b2MouseJoint.SolveVelocityConstraints_s_Cdot);
// b2Vec2 impulse = b2Mul(m_mass, -(Cdot + m_C + m_gamma * m_impulse));
var impulse = b2Mat22.MulMV(this.m_mass, b2Vec2.AddVV(Cdot, b2Vec2.AddVV(this.m_C, b2Vec2.MulSV(this.m_gamma, this.m_impulse, b2Vec2.s_t0), b2Vec2.s_t0), b2Vec2.s_t0).SelfNeg(), b2MouseJoint.SolveVelocityConstraints_s_impulse);
// b2Vec2 oldImpulse = m_impulse;
var oldImpulse = b2MouseJoint.SolveVelocityConstraints_s_oldImpulse.Copy(this.m_impulse);
// m_impulse += impulse;
this.m_impulse.SelfAdd(impulse);
var maxImpulse = data.step.dt * this.m_maxForce;
if (this.m_impulse.LengthSquared() > maxImpulse * maxImpulse) {
this.m_impulse.SelfMul(maxImpulse / this.m_impulse.Length());
}
// impulse = m_impulse - oldImpulse;
b2Vec2.SubVV(this.m_impulse, oldImpulse, impulse);
// vB += m_invMassB * impulse;
vB.SelfMulAdd(this.m_invMassB, impulse);
wB += this.m_invIB * b2Vec2.CrossVV(this.m_rB, impulse);
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2MouseJoint.prototype.SolvePositionConstraints = function (data) {
return true;
};
b2MouseJoint.prototype.GetAnchorA = function (out) {
out.x = this.m_targetA.x;
out.y = this.m_targetA.y;
return out;
};
b2MouseJoint.prototype.GetAnchorB = function (out) {
return this.m_bodyB.GetWorldPoint(this.m_localAnchorB, out);
};
b2MouseJoint.prototype.GetReactionForce = function (inv_dt, out) {
return b2Vec2.MulSV(inv_dt, this.m_impulse, out);
};
b2MouseJoint.prototype.GetReactionTorque = function (inv_dt) {
return 0;
};
b2MouseJoint.prototype.Dump = function (log) {
log("Mouse joint dumping is not supported.\n");
};
b2MouseJoint.prototype.ShiftOrigin = function (newOrigin) {
this.m_targetA.SelfSub(newOrigin);
};
b2MouseJoint.SolveVelocityConstraints_s_Cdot = new b2Vec2();
b2MouseJoint.SolveVelocityConstraints_s_impulse = new b2Vec2();
b2MouseJoint.SolveVelocityConstraints_s_oldImpulse = new b2Vec2();
return b2MouseJoint;
}(b2Joint));
/*
* 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.
*/
/// Prismatic joint definition. This requires defining a line of
/// motion using an axis and an anchor point. The definition uses local
/// anchor points and a local axis so that the initial configuration
/// can violate the constraint slightly. The joint translation is zero
/// when the local anchor points coincide in world space. Using local
/// anchors and a local axis helps when saving and loading a game.
var b2PrismaticJointDef = /** @class */ (function (_super) {
__extends(b2PrismaticJointDef, _super);
function b2PrismaticJointDef() {
var _this = _super.call(this, exports.b2JointType.e_prismaticJoint) || this;
_this.localAnchorA = new b2Vec2();
_this.localAnchorB = new b2Vec2();
_this.localAxisA = new b2Vec2(1, 0);
_this.referenceAngle = 0;
_this.enableLimit = false;
_this.lowerTranslation = 0;
_this.upperTranslation = 0;
_this.enableMotor = false;
_this.maxMotorForce = 0;
_this.motorSpeed = 0;
return _this;
}
b2PrismaticJointDef.prototype.Initialize = function (bA, bB, anchor, axis) {
this.bodyA = bA;
this.bodyB = bB;
this.bodyA.GetLocalPoint(anchor, this.localAnchorA);
this.bodyB.GetLocalPoint(anchor, this.localAnchorB);
this.bodyA.GetLocalVector(axis, this.localAxisA);
this.referenceAngle = this.bodyB.GetAngle() - this.bodyA.GetAngle();
};
return b2PrismaticJointDef;
}(b2JointDef));
var b2PrismaticJoint = /** @class */ (function (_super) {
__extends(b2PrismaticJoint, _super);
function b2PrismaticJoint(def) {
var _this = _super.call(this, def) || this;
// Solver shared
_this.m_localAnchorA = new b2Vec2();
_this.m_localAnchorB = new b2Vec2();
_this.m_localXAxisA = new b2Vec2();
_this.m_localYAxisA = new b2Vec2();
_this.m_referenceAngle = 0;
_this.m_impulse = new b2Vec3(0, 0, 0);
_this.m_motorImpulse = 0;
_this.m_lowerTranslation = 0;
_this.m_upperTranslation = 0;
_this.m_maxMotorForce = 0;
_this.m_motorSpeed = 0;
_this.m_enableLimit = false;
_this.m_enableMotor = false;
_this.m_limitState = exports.b2LimitState.e_inactiveLimit;
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_localCenterA = new b2Vec2();
_this.m_localCenterB = new b2Vec2();
_this.m_invMassA = 0;
_this.m_invMassB = 0;
_this.m_invIA = 0;
_this.m_invIB = 0;
_this.m_axis = new b2Vec2(0, 0);
_this.m_perp = new b2Vec2(0, 0);
_this.m_s1 = 0;
_this.m_s2 = 0;
_this.m_a1 = 0;
_this.m_a2 = 0;
_this.m_K = new b2Mat33();
_this.m_K3 = new b2Mat33();
_this.m_K2 = new b2Mat22();
_this.m_motorMass = 0;
_this.m_qA = new b2Rot();
_this.m_qB = new b2Rot();
_this.m_lalcA = new b2Vec2();
_this.m_lalcB = new b2Vec2();
_this.m_rA = new b2Vec2();
_this.m_rB = new b2Vec2();
_this.m_localAnchorA.Copy(b2Maybe(def.localAnchorA, b2Vec2.ZERO));
_this.m_localAnchorB.Copy(b2Maybe(def.localAnchorB, b2Vec2.ZERO));
_this.m_localXAxisA.Copy(b2Maybe(def.localAxisA, new b2Vec2(1, 0))).SelfNormalize();
b2Vec2.CrossOneV(_this.m_localXAxisA, _this.m_localYAxisA);
_this.m_referenceAngle = b2Maybe(def.referenceAngle, 0);
_this.m_lowerTranslation = b2Maybe(def.lowerTranslation, 0);
_this.m_upperTranslation = b2Maybe(def.upperTranslation, 0);
_this.m_maxMotorForce = b2Maybe(def.maxMotorForce, 0);
_this.m_motorSpeed = b2Maybe(def.motorSpeed, 0);
_this.m_enableLimit = b2Maybe(def.enableLimit, false);
_this.m_enableMotor = b2Maybe(def.enableMotor, false);
return _this;
}
b2PrismaticJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexA = this.m_bodyA.m_islandIndex;
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_localCenterA.Copy(this.m_bodyA.m_sweep.localCenter);
this.m_localCenterB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_invMassA = this.m_bodyA.m_invMass;
this.m_invMassB = this.m_bodyB.m_invMass;
this.m_invIA = this.m_bodyA.m_invI;
this.m_invIB = this.m_bodyB.m_invI;
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// Compute the effective masses.
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// b2Vec2 d = (cB - cA) + rB - rA;
var d = b2Vec2.AddVV(b2Vec2.SubVV(cB, cA, b2Vec2.s_t0), b2Vec2.SubVV(rB, rA, b2Vec2.s_t1), b2PrismaticJoint.InitVelocityConstraints_s_d);
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
// Compute motor Jacobian and effective mass.
{
// m_axis = b2Mul(qA, m_localXAxisA);
b2Rot.MulRV(qA, this.m_localXAxisA, this.m_axis);
// m_a1 = b2Cross(d + rA, m_axis);
this.m_a1 = b2Vec2.CrossVV(b2Vec2.AddVV(d, rA, b2Vec2.s_t0), this.m_axis);
// m_a2 = b2Cross(rB, m_axis);
this.m_a2 = b2Vec2.CrossVV(rB, this.m_axis);
this.m_motorMass = mA + mB + iA * this.m_a1 * this.m_a1 + iB * this.m_a2 * this.m_a2;
if (this.m_motorMass > 0) {
this.m_motorMass = 1 / this.m_motorMass;
}
}
// Prismatic constraint.
{
// m_perp = b2Mul(qA, m_localYAxisA);
b2Rot.MulRV(qA, this.m_localYAxisA, this.m_perp);
// m_s1 = b2Cross(d + rA, m_perp);
this.m_s1 = b2Vec2.CrossVV(b2Vec2.AddVV(d, rA, b2Vec2.s_t0), this.m_perp);
// m_s2 = b2Cross(rB, m_perp);
this.m_s2 = b2Vec2.CrossVV(rB, this.m_perp);
// float32 k11 = mA + mB + iA * m_s1 * m_s1 + iB * m_s2 * m_s2;
this.m_K.ex.x = mA + mB + iA * this.m_s1 * this.m_s1 + iB * this.m_s2 * this.m_s2;
// float32 k12 = iA * m_s1 + iB * m_s2;
this.m_K.ex.y = iA * this.m_s1 + iB * this.m_s2;
// float32 k13 = iA * m_s1 * m_a1 + iB * m_s2 * m_a2;
this.m_K.ex.z = iA * this.m_s1 * this.m_a1 + iB * this.m_s2 * this.m_a2;
this.m_K.ey.x = this.m_K.ex.y;
// float32 k22 = iA + iB;
this.m_K.ey.y = iA + iB;
if (this.m_K.ey.y === 0) {
// For bodies with fixed rotation.
this.m_K.ey.y = 1;
}
// float32 k23 = iA * m_a1 + iB * m_a2;
this.m_K.ey.z = iA * this.m_a1 + iB * this.m_a2;
this.m_K.ez.x = this.m_K.ex.z;
this.m_K.ez.y = this.m_K.ey.z;
// float32 k33 = mA + mB + iA * m_a1 * m_a1 + iB * m_a2 * m_a2;
this.m_K.ez.z = mA + mB + iA * this.m_a1 * this.m_a1 + iB * this.m_a2 * this.m_a2;
// m_K.ex.Set(k11, k12, k13);
// m_K.ey.Set(k12, k22, k23);
// m_K.ez.Set(k13, k23, k33);
}
// Compute motor and limit terms.
if (this.m_enableLimit) {
// float32 jointTranslation = b2Dot(m_axis, d);
var jointTranslation = b2Vec2.DotVV(this.m_axis, d);
if (b2Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2 * b2_linearSlop) {
this.m_limitState = exports.b2LimitState.e_equalLimits;
}
else if (jointTranslation <= this.m_lowerTranslation) {
if (this.m_limitState !== exports.b2LimitState.e_atLowerLimit) {
this.m_limitState = exports.b2LimitState.e_atLowerLimit;
this.m_impulse.z = 0;
}
}
else if (jointTranslation >= this.m_upperTranslation) {
if (this.m_limitState !== exports.b2LimitState.e_atUpperLimit) {
this.m_limitState = exports.b2LimitState.e_atUpperLimit;
this.m_impulse.z = 0;
}
}
else {
this.m_limitState = exports.b2LimitState.e_inactiveLimit;
this.m_impulse.z = 0;
}
}
else {
this.m_limitState = exports.b2LimitState.e_inactiveLimit;
this.m_impulse.z = 0;
}
if (!this.m_enableMotor) {
this.m_motorImpulse = 0;
}
if (data.step.warmStarting) {
// Account for variable time step.
// m_impulse *= data.step.dtRatio;
this.m_impulse.SelfMul(data.step.dtRatio);
this.m_motorImpulse *= data.step.dtRatio;
// b2Vec2 P = m_impulse.x * m_perp + (m_motorImpulse + m_impulse.z) * m_axis;
var P = b2Vec2.AddVV(b2Vec2.MulSV(this.m_impulse.x, this.m_perp, b2Vec2.s_t0), b2Vec2.MulSV((this.m_motorImpulse + this.m_impulse.z), this.m_axis, b2Vec2.s_t1), b2PrismaticJoint.InitVelocityConstraints_s_P);
// float32 LA = m_impulse.x * m_s1 + m_impulse.y + (m_motorImpulse + m_impulse.z) * m_a1;
var LA = this.m_impulse.x * this.m_s1 + this.m_impulse.y + (this.m_motorImpulse + this.m_impulse.z) * this.m_a1;
// float32 LB = m_impulse.x * m_s2 + m_impulse.y + (m_motorImpulse + m_impulse.z) * m_a2;
var LB = this.m_impulse.x * this.m_s2 + this.m_impulse.y + (this.m_motorImpulse + this.m_impulse.z) * this.m_a2;
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * LA;
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * LB;
}
else {
this.m_impulse.SetZero();
this.m_motorImpulse = 0;
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2PrismaticJoint.prototype.SolveVelocityConstraints = function (data) {
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
// Solve linear motor constraint.
if (this.m_enableMotor && this.m_limitState !== exports.b2LimitState.e_equalLimits) {
// float32 Cdot = b2Dot(m_axis, vB - vA) + m_a2 * wB - m_a1 * wA;
var Cdot = b2Vec2.DotVV(this.m_axis, b2Vec2.SubVV(vB, vA, b2Vec2.s_t0)) + this.m_a2 * wB - this.m_a1 * wA;
var impulse = this.m_motorMass * (this.m_motorSpeed - Cdot);
var oldImpulse = this.m_motorImpulse;
var maxImpulse = data.step.dt * this.m_maxMotorForce;
this.m_motorImpulse = b2Clamp(this.m_motorImpulse + impulse, (-maxImpulse), maxImpulse);
impulse = this.m_motorImpulse - oldImpulse;
// b2Vec2 P = impulse * m_axis;
var P = b2Vec2.MulSV(impulse, this.m_axis, b2PrismaticJoint.SolveVelocityConstraints_s_P);
var LA = impulse * this.m_a1;
var LB = impulse * this.m_a2;
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * LA;
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * LB;
}
// b2Vec2 Cdot1;
// Cdot1.x = b2Dot(m_perp, vB - vA) + m_s2 * wB - m_s1 * wA;
var Cdot1_x = b2Vec2.DotVV(this.m_perp, b2Vec2.SubVV(vB, vA, b2Vec2.s_t0)) + this.m_s2 * wB - this.m_s1 * wA;
// Cdot1.y = wB - wA;
var Cdot1_y = wB - wA;
if (this.m_enableLimit && this.m_limitState !== exports.b2LimitState.e_inactiveLimit) {
// Solve prismatic and limit constraint in block form.
// float32 Cdot2;
// Cdot2 = b2Dot(m_axis, vB - vA) + m_a2 * wB - m_a1 * wA;
var Cdot2 = b2Vec2.DotVV(this.m_axis, b2Vec2.SubVV(vB, vA, b2Vec2.s_t0)) + this.m_a2 * wB - this.m_a1 * wA;
// b2Vec3 Cdot(Cdot1.x, Cdot1.y, Cdot2);
// b2Vec3 f1 = m_impulse;
var f1 = b2PrismaticJoint.SolveVelocityConstraints_s_f1.Copy(this.m_impulse);
// b2Vec3 df = m_K.Solve33(-Cdot);
var df3 = this.m_K.Solve33((-Cdot1_x), (-Cdot1_y), (-Cdot2), b2PrismaticJoint.SolveVelocityConstraints_s_df3);
// m_impulse += df;
this.m_impulse.SelfAdd(df3);
if (this.m_limitState === exports.b2LimitState.e_atLowerLimit) {
this.m_impulse.z = b2Max(this.m_impulse.z, 0);
}
else if (this.m_limitState === exports.b2LimitState.e_atUpperLimit) {
this.m_impulse.z = b2Min(this.m_impulse.z, 0);
}
// f2(1:2) = invK(1:2,1:2) * (-Cdot(1:2) - K(1:2,3) * (f2(3) - f1(3))) + f1(1:2)
// b2Vec2 b = -Cdot1 - (m_impulse.z - f1.z) * b2Vec2(m_K.ez.x, m_K.ez.y);
var b_x = (-Cdot1_x) - (this.m_impulse.z - f1.z) * this.m_K.ez.x;
var b_y = (-Cdot1_y) - (this.m_impulse.z - f1.z) * this.m_K.ez.y;
// b2Vec2 f2r = m_K.Solve22(b) + b2Vec2(f1.x, f1.y);
var f2r = this.m_K.Solve22(b_x, b_y, b2PrismaticJoint.SolveVelocityConstraints_s_f2r);
f2r.x += f1.x;
f2r.y += f1.y;
// m_impulse.x = f2r.x;
this.m_impulse.x = f2r.x;
// m_impulse.y = f2r.y;
this.m_impulse.y = f2r.y;
// df = m_impulse - f1;
df3.x = this.m_impulse.x - f1.x;
df3.y = this.m_impulse.y - f1.y;
df3.z = this.m_impulse.z - f1.z;
// b2Vec2 P = df.x * m_perp + df.z * m_axis;
var P = b2Vec2.AddVV(b2Vec2.MulSV(df3.x, this.m_perp, b2Vec2.s_t0), b2Vec2.MulSV(df3.z, this.m_axis, b2Vec2.s_t1), b2PrismaticJoint.SolveVelocityConstraints_s_P);
// float32 LA = df.x * m_s1 + df.y + df.z * m_a1;
var LA = df3.x * this.m_s1 + df3.y + df3.z * this.m_a1;
// float32 LB = df.x * m_s2 + df.y + df.z * m_a2;
var LB = df3.x * this.m_s2 + df3.y + df3.z * this.m_a2;
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * LA;
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * LB;
}
else {
// Limit is inactive, just solve the prismatic constraint in block form.
// b2Vec2 df = m_K.Solve22(-Cdot1);
var df2 = this.m_K.Solve22((-Cdot1_x), (-Cdot1_y), b2PrismaticJoint.SolveVelocityConstraints_s_df2);
this.m_impulse.x += df2.x;
this.m_impulse.y += df2.y;
// b2Vec2 P = df.x * m_perp;
var P = b2Vec2.MulSV(df2.x, this.m_perp, b2PrismaticJoint.SolveVelocityConstraints_s_P);
// float32 LA = df.x * m_s1 + df.y;
var LA = df2.x * this.m_s1 + df2.y;
// float32 LB = df.x * m_s2 + df.y;
var LB = df2.x * this.m_s2 + df2.y;
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * LA;
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * LB;
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2PrismaticJoint.prototype.SolvePositionConstraints = function (data) {
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// b2Vec2 d = cB + rB - cA - rA;
var d = b2Vec2.SubVV(b2Vec2.AddVV(cB, rB, b2Vec2.s_t0), b2Vec2.AddVV(cA, rA, b2Vec2.s_t1), b2PrismaticJoint.SolvePositionConstraints_s_d);
// b2Vec2 axis = b2Mul(qA, m_localXAxisA);
var axis = b2Rot.MulRV(qA, this.m_localXAxisA, this.m_axis);
// float32 a1 = b2Cross(d + rA, axis);
var a1 = b2Vec2.CrossVV(b2Vec2.AddVV(d, rA, b2Vec2.s_t0), axis);
// float32 a2 = b2Cross(rB, axis);
var a2 = b2Vec2.CrossVV(rB, axis);
// b2Vec2 perp = b2Mul(qA, m_localYAxisA);
var perp = b2Rot.MulRV(qA, this.m_localYAxisA, this.m_perp);
// float32 s1 = b2Cross(d + rA, perp);
var s1 = b2Vec2.CrossVV(b2Vec2.AddVV(d, rA, b2Vec2.s_t0), perp);
// float32 s2 = b2Cross(rB, perp);
var s2 = b2Vec2.CrossVV(rB, perp);
// b2Vec3 impulse;
var impulse = b2PrismaticJoint.SolvePositionConstraints_s_impulse;
// b2Vec2 C1;
// C1.x = b2Dot(perp, d);
var C1_x = b2Vec2.DotVV(perp, d);
// C1.y = aB - aA - m_referenceAngle;
var C1_y = aB - aA - this.m_referenceAngle;
var linearError = b2Abs(C1_x);
var angularError = b2Abs(C1_y);
var active = false;
var C2 = 0;
if (this.m_enableLimit) {
// float32 translation = b2Dot(axis, d);
var translation = b2Vec2.DotVV(axis, d);
if (b2Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2 * b2_linearSlop) {
// Prevent large angular corrections
C2 = b2Clamp(translation, (-b2_maxLinearCorrection), b2_maxLinearCorrection);
linearError = b2Max(linearError, b2Abs(translation));
active = true;
}
else if (translation <= this.m_lowerTranslation) {
// Prevent large linear corrections and allow some slop.
C2 = b2Clamp(translation - this.m_lowerTranslation + b2_linearSlop, (-b2_maxLinearCorrection), 0);
linearError = b2Max(linearError, this.m_lowerTranslation - translation);
active = true;
}
else if (translation >= this.m_upperTranslation) {
// Prevent large linear corrections and allow some slop.
C2 = b2Clamp(translation - this.m_upperTranslation - b2_linearSlop, 0, b2_maxLinearCorrection);
linearError = b2Max(linearError, translation - this.m_upperTranslation);
active = true;
}
}
if (active) {
// float32 k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2;
var k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2;
// float32 k12 = iA * s1 + iB * s2;
var k12 = iA * s1 + iB * s2;
// float32 k13 = iA * s1 * a1 + iB * s2 * a2;
var k13 = iA * s1 * a1 + iB * s2 * a2;
// float32 k22 = iA + iB;
var k22 = iA + iB;
if (k22 === 0) {
// For fixed rotation
k22 = 1;
}
// float32 k23 = iA * a1 + iB * a2;
var k23 = iA * a1 + iB * a2;
// float32 k33 = mA + mB + iA * a1 * a1 + iB * a2 * a2;
var k33 = mA + mB + iA * a1 * a1 + iB * a2 * a2;
// b2Mat33 K;
var K = this.m_K3;
// K.ex.Set(k11, k12, k13);
K.ex.SetXYZ(k11, k12, k13);
// K.ey.Set(k12, k22, k23);
K.ey.SetXYZ(k12, k22, k23);
// K.ez.Set(k13, k23, k33);
K.ez.SetXYZ(k13, k23, k33);
// b2Vec3 C;
// C.x = C1.x;
// C.y = C1.y;
// C.z = C2;
// impulse = K.Solve33(-C);
impulse = K.Solve33((-C1_x), (-C1_y), (-C2), impulse);
}
else {
// float32 k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2;
var k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2;
// float32 k12 = iA * s1 + iB * s2;
var k12 = iA * s1 + iB * s2;
// float32 k22 = iA + iB;
var k22 = iA + iB;
if (k22 === 0) {
k22 = 1;
}
// b2Mat22 K;
var K2 = this.m_K2;
// K.ex.Set(k11, k12);
K2.ex.Set(k11, k12);
// K.ey.Set(k12, k22);
K2.ey.Set(k12, k22);
// b2Vec2 impulse1 = K.Solve(-C1);
var impulse1 = K2.Solve((-C1_x), (-C1_y), b2PrismaticJoint.SolvePositionConstraints_s_impulse1);
impulse.x = impulse1.x;
impulse.y = impulse1.y;
impulse.z = 0;
}
// b2Vec2 P = impulse.x * perp + impulse.z * axis;
var P = b2Vec2.AddVV(b2Vec2.MulSV(impulse.x, perp, b2Vec2.s_t0), b2Vec2.MulSV(impulse.z, axis, b2Vec2.s_t1), b2PrismaticJoint.SolvePositionConstraints_s_P);
// float32 LA = impulse.x * s1 + impulse.y + impulse.z * a1;
var LA = impulse.x * s1 + impulse.y + impulse.z * a1;
// float32 LB = impulse.x * s2 + impulse.y + impulse.z * a2;
var LB = impulse.x * s2 + impulse.y + impulse.z * a2;
// cA -= mA * P;
cA.SelfMulSub(mA, P);
aA -= iA * LA;
// cB += mB * P;
cB.SelfMulAdd(mB, P);
aB += iB * LB;
// data.positions[this.m_indexA].c = cA;
data.positions[this.m_indexA].a = aA;
// data.positions[this.m_indexB].c = cB;
data.positions[this.m_indexB].a = aB;
return linearError <= b2_linearSlop && angularError <= b2_angularSlop;
};
b2PrismaticJoint.prototype.GetAnchorA = function (out) {
return this.m_bodyA.GetWorldPoint(this.m_localAnchorA, out);
};
b2PrismaticJoint.prototype.GetAnchorB = function (out) {
return this.m_bodyB.GetWorldPoint(this.m_localAnchorB, out);
};
b2PrismaticJoint.prototype.GetReactionForce = function (inv_dt, out) {
// return inv_dt * (m_impulse.x * m_perp + (m_motorImpulse + m_impulse.z) * m_axis);
out.x = inv_dt * (this.m_impulse.x * this.m_perp.x + (this.m_motorImpulse + this.m_impulse.z) * this.m_axis.x);
out.y = inv_dt * (this.m_impulse.x * this.m_perp.y + (this.m_motorImpulse + this.m_impulse.z) * this.m_axis.y);
return out;
};
b2PrismaticJoint.prototype.GetReactionTorque = function (inv_dt) {
return inv_dt * this.m_impulse.y;
};
b2PrismaticJoint.prototype.GetLocalAnchorA = function () { return this.m_localAnchorA; };
b2PrismaticJoint.prototype.GetLocalAnchorB = function () { return this.m_localAnchorB; };
b2PrismaticJoint.prototype.GetLocalAxisA = function () { return this.m_localXAxisA; };
b2PrismaticJoint.prototype.GetReferenceAngle = function () { return this.m_referenceAngle; };
b2PrismaticJoint.prototype.GetJointTranslation = function () {
// b2Vec2 pA = m_bodyA.GetWorldPoint(m_localAnchorA);
var pA = this.m_bodyA.GetWorldPoint(this.m_localAnchorA, b2PrismaticJoint.GetJointTranslation_s_pA);
// b2Vec2 pB = m_bodyB.GetWorldPoint(m_localAnchorB);
var pB = this.m_bodyB.GetWorldPoint(this.m_localAnchorB, b2PrismaticJoint.GetJointTranslation_s_pB);
// b2Vec2 d = pB - pA;
var d = b2Vec2.SubVV(pB, pA, b2PrismaticJoint.GetJointTranslation_s_d);
// b2Vec2 axis = m_bodyA.GetWorldVector(m_localXAxisA);
var axis = this.m_bodyA.GetWorldVector(this.m_localXAxisA, b2PrismaticJoint.GetJointTranslation_s_axis);
// float32 translation = b2Dot(d, axis);
var translation = b2Vec2.DotVV(d, axis);
return translation;
};
b2PrismaticJoint.prototype.GetJointSpeed = function () {
var bA = this.m_bodyA;
var bB = this.m_bodyB;
// b2Vec2 rA = b2Mul(bA->m_xf.q, m_localAnchorA - bA->m_sweep.localCenter);
b2Vec2.SubVV(this.m_localAnchorA, bA.m_sweep.localCenter, this.m_lalcA);
var rA = b2Rot.MulRV(bA.m_xf.q, this.m_lalcA, this.m_rA);
// b2Vec2 rB = b2Mul(bB->m_xf.q, m_localAnchorB - bB->m_sweep.localCenter);
b2Vec2.SubVV(this.m_localAnchorB, bB.m_sweep.localCenter, this.m_lalcB);
var rB = b2Rot.MulRV(bB.m_xf.q, this.m_lalcB, this.m_rB);
// b2Vec2 pA = bA->m_sweep.c + rA;
var pA = b2Vec2.AddVV(bA.m_sweep.c, rA, b2Vec2.s_t0); // pA uses s_t0
// b2Vec2 pB = bB->m_sweep.c + rB;
var pB = b2Vec2.AddVV(bB.m_sweep.c, rB, b2Vec2.s_t1); // pB uses s_t1
// b2Vec2 d = pB - pA;
var d = b2Vec2.SubVV(pB, pA, b2Vec2.s_t2); // d uses s_t2
// b2Vec2 axis = b2Mul(bA.m_xf.q, m_localXAxisA);
var axis = bA.GetWorldVector(this.m_localXAxisA, this.m_axis);
var vA = bA.m_linearVelocity;
var vB = bB.m_linearVelocity;
var wA = bA.m_angularVelocity;
var wB = bB.m_angularVelocity;
// float32 speed = b2Dot(d, b2Cross(wA, axis)) + b2Dot(axis, vB + b2Cross(wB, rB) - vA - b2Cross(wA, rA));
var speed = b2Vec2.DotVV(d, b2Vec2.CrossSV(wA, axis, b2Vec2.s_t0)) +
b2Vec2.DotVV(axis, b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, rA, b2Vec2.s_t1), b2Vec2.s_t0));
return speed;
};
b2PrismaticJoint.prototype.IsLimitEnabled = function () {
return this.m_enableLimit;
};
b2PrismaticJoint.prototype.EnableLimit = function (flag) {
if (flag !== this.m_enableLimit) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_enableLimit = flag;
this.m_impulse.z = 0;
}
};
b2PrismaticJoint.prototype.GetLowerLimit = function () {
return this.m_lowerTranslation;
};
b2PrismaticJoint.prototype.GetUpperLimit = function () {
return this.m_upperTranslation;
};
b2PrismaticJoint.prototype.SetLimits = function (lower, upper) {
if (lower !== this.m_lowerTranslation || upper !== this.m_upperTranslation) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_lowerTranslation = lower;
this.m_upperTranslation = upper;
this.m_impulse.z = 0;
}
};
b2PrismaticJoint.prototype.IsMotorEnabled = function () {
return this.m_enableMotor;
};
b2PrismaticJoint.prototype.EnableMotor = function (flag) {
if (flag !== this.m_enableMotor) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_enableMotor = flag;
}
};
b2PrismaticJoint.prototype.SetMotorSpeed = function (speed) {
if (speed !== this.m_motorSpeed) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_motorSpeed = speed;
}
};
b2PrismaticJoint.prototype.GetMotorSpeed = function () {
return this.m_motorSpeed;
};
b2PrismaticJoint.prototype.SetMaxMotorForce = function (force) {
if (force !== this.m_maxMotorForce) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_maxMotorForce = force;
}
};
b2PrismaticJoint.prototype.GetMaxMotorForce = function () { return this.m_maxMotorForce; };
b2PrismaticJoint.prototype.GetMotorForce = function (inv_dt) {
return inv_dt * this.m_motorImpulse;
};
b2PrismaticJoint.prototype.Dump = function (log) {
var indexA = this.m_bodyA.m_islandIndex;
var indexB = this.m_bodyB.m_islandIndex;
log(" const jd: b2PrismaticJointDef = new b2PrismaticJointDef();\n");
log(" jd.bodyA = bodies[%d];\n", indexA);
log(" jd.bodyB = bodies[%d];\n", indexB);
log(" jd.collideConnected = %s;\n", (this.m_collideConnected) ? ("true") : ("false"));
log(" jd.localAnchorA.Set(%.15f, %.15f);\n", this.m_localAnchorA.x, this.m_localAnchorA.y);
log(" jd.localAnchorB.Set(%.15f, %.15f);\n", this.m_localAnchorB.x, this.m_localAnchorB.y);
log(" jd.localAxisA.Set(%.15f, %.15f);\n", this.m_localXAxisA.x, this.m_localXAxisA.y);
log(" jd.referenceAngle = %.15f;\n", this.m_referenceAngle);
log(" jd.enableLimit = %s;\n", (this.m_enableLimit) ? ("true") : ("false"));
log(" jd.lowerTranslation = %.15f;\n", this.m_lowerTranslation);
log(" jd.upperTranslation = %.15f;\n", this.m_upperTranslation);
log(" jd.enableMotor = %s;\n", (this.m_enableMotor) ? ("true") : ("false"));
log(" jd.motorSpeed = %.15f;\n", this.m_motorSpeed);
log(" jd.maxMotorForce = %.15f;\n", this.m_maxMotorForce);
log(" joints[%d] = this.m_world.CreateJoint(jd);\n", this.m_index);
};
b2PrismaticJoint.InitVelocityConstraints_s_d = new b2Vec2();
b2PrismaticJoint.InitVelocityConstraints_s_P = new b2Vec2();
b2PrismaticJoint.SolveVelocityConstraints_s_P = new b2Vec2();
b2PrismaticJoint.SolveVelocityConstraints_s_f2r = new b2Vec2();
b2PrismaticJoint.SolveVelocityConstraints_s_f1 = new b2Vec3();
b2PrismaticJoint.SolveVelocityConstraints_s_df3 = new b2Vec3();
b2PrismaticJoint.SolveVelocityConstraints_s_df2 = new b2Vec2();
// A velocity based solver computes reaction forces(impulses) using the velocity constraint solver.Under this context,
// the position solver is not there to resolve forces.It is only there to cope with integration error.
//
// Therefore, the pseudo impulses in the position solver do not have any physical meaning.Thus it is okay if they suck.
//
// We could take the active state from the velocity solver.However, the joint might push past the limit when the velocity
// solver indicates the limit is inactive.
b2PrismaticJoint.SolvePositionConstraints_s_d = new b2Vec2();
b2PrismaticJoint.SolvePositionConstraints_s_impulse = new b2Vec3();
b2PrismaticJoint.SolvePositionConstraints_s_impulse1 = new b2Vec2();
b2PrismaticJoint.SolvePositionConstraints_s_P = new b2Vec2();
b2PrismaticJoint.GetJointTranslation_s_pA = new b2Vec2();
b2PrismaticJoint.GetJointTranslation_s_pB = new b2Vec2();
b2PrismaticJoint.GetJointTranslation_s_d = new b2Vec2();
b2PrismaticJoint.GetJointTranslation_s_axis = new b2Vec2();
return b2PrismaticJoint;
}(b2Joint));
/*
* 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.
*/
var b2_minPulleyLength = 2;
/// Pulley joint definition. This requires two ground anchors,
/// two dynamic body anchor points, and a pulley ratio.
var b2PulleyJointDef = /** @class */ (function (_super) {
__extends(b2PulleyJointDef, _super);
function b2PulleyJointDef() {
var _this = _super.call(this, exports.b2JointType.e_pulleyJoint) || this;
_this.groundAnchorA = new b2Vec2(-1, 1);
_this.groundAnchorB = new b2Vec2(1, 1);
_this.localAnchorA = new b2Vec2(-1, 0);
_this.localAnchorB = new b2Vec2(1, 0);
_this.lengthA = 0;
_this.lengthB = 0;
_this.ratio = 1;
_this.collideConnected = true;
return _this;
}
b2PulleyJointDef.prototype.Initialize = function (bA, bB, groundA, groundB, anchorA, anchorB, r) {
this.bodyA = bA;
this.bodyB = bB;
this.groundAnchorA.Copy(groundA);
this.groundAnchorB.Copy(groundB);
this.bodyA.GetLocalPoint(anchorA, this.localAnchorA);
this.bodyB.GetLocalPoint(anchorB, this.localAnchorB);
this.lengthA = b2Vec2.DistanceVV(anchorA, groundA);
this.lengthB = b2Vec2.DistanceVV(anchorB, groundB);
this.ratio = r;
// DEBUG: b2Assert(this.ratio > b2_epsilon);
};
return b2PulleyJointDef;
}(b2JointDef));
var b2PulleyJoint = /** @class */ (function (_super) {
__extends(b2PulleyJoint, _super);
function b2PulleyJoint(def) {
var _this = _super.call(this, def) || this;
_this.m_groundAnchorA = new b2Vec2();
_this.m_groundAnchorB = new b2Vec2();
_this.m_lengthA = 0;
_this.m_lengthB = 0;
// Solver shared
_this.m_localAnchorA = new b2Vec2();
_this.m_localAnchorB = new b2Vec2();
_this.m_constant = 0;
_this.m_ratio = 0;
_this.m_impulse = 0;
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_uA = new b2Vec2();
_this.m_uB = new b2Vec2();
_this.m_rA = new b2Vec2();
_this.m_rB = new b2Vec2();
_this.m_localCenterA = new b2Vec2();
_this.m_localCenterB = new b2Vec2();
_this.m_invMassA = 0;
_this.m_invMassB = 0;
_this.m_invIA = 0;
_this.m_invIB = 0;
_this.m_mass = 0;
_this.m_qA = new b2Rot();
_this.m_qB = new b2Rot();
_this.m_lalcA = new b2Vec2();
_this.m_lalcB = new b2Vec2();
_this.m_groundAnchorA.Copy(b2Maybe(def.groundAnchorA, new b2Vec2(-1, 1)));
_this.m_groundAnchorB.Copy(b2Maybe(def.groundAnchorB, new b2Vec2(1, 0)));
_this.m_localAnchorA.Copy(b2Maybe(def.localAnchorA, new b2Vec2(-1, 0)));
_this.m_localAnchorB.Copy(b2Maybe(def.localAnchorB, new b2Vec2(1, 0)));
_this.m_lengthA = b2Maybe(def.lengthA, 0);
_this.m_lengthB = b2Maybe(def.lengthB, 0);
// DEBUG: b2Assert(b2Maybe(def.ratio, 1) !== 0);
_this.m_ratio = b2Maybe(def.ratio, 1);
_this.m_constant = b2Maybe(def.lengthA, 0) + _this.m_ratio * b2Maybe(def.lengthB, 0);
_this.m_impulse = 0;
return _this;
}
b2PulleyJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexA = this.m_bodyA.m_islandIndex;
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_localCenterA.Copy(this.m_bodyA.m_sweep.localCenter);
this.m_localCenterB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_invMassA = this.m_bodyA.m_invMass;
this.m_invMassB = this.m_bodyB.m_invMass;
this.m_invIA = this.m_bodyA.m_invI;
this.m_invIB = this.m_bodyB.m_invI;
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
// b2Rot qA(aA), qB(aB);
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// Get the pulley axes.
// m_uA = cA + m_rA - m_groundAnchorA;
this.m_uA.Copy(cA).SelfAdd(this.m_rA).SelfSub(this.m_groundAnchorA);
// m_uB = cB + m_rB - m_groundAnchorB;
this.m_uB.Copy(cB).SelfAdd(this.m_rB).SelfSub(this.m_groundAnchorB);
var lengthA = this.m_uA.Length();
var lengthB = this.m_uB.Length();
if (lengthA > 10 * b2_linearSlop) {
this.m_uA.SelfMul(1 / lengthA);
}
else {
this.m_uA.SetZero();
}
if (lengthB > 10 * b2_linearSlop) {
this.m_uB.SelfMul(1 / lengthB);
}
else {
this.m_uB.SetZero();
}
// Compute effective mass.
var ruA = b2Vec2.CrossVV(this.m_rA, this.m_uA);
var ruB = b2Vec2.CrossVV(this.m_rB, this.m_uB);
var mA = this.m_invMassA + this.m_invIA * ruA * ruA;
var mB = this.m_invMassB + this.m_invIB * ruB * ruB;
this.m_mass = mA + this.m_ratio * this.m_ratio * mB;
if (this.m_mass > 0) {
this.m_mass = 1 / this.m_mass;
}
if (data.step.warmStarting) {
// Scale impulses to support variable time steps.
this.m_impulse *= data.step.dtRatio;
// Warm starting.
// b2Vec2 PA = -(m_impulse) * m_uA;
var PA = b2Vec2.MulSV(-(this.m_impulse), this.m_uA, b2PulleyJoint.InitVelocityConstraints_s_PA);
// b2Vec2 PB = (-m_ratio * m_impulse) * m_uB;
var PB = b2Vec2.MulSV((-this.m_ratio * this.m_impulse), this.m_uB, b2PulleyJoint.InitVelocityConstraints_s_PB);
// vA += m_invMassA * PA;
vA.SelfMulAdd(this.m_invMassA, PA);
wA += this.m_invIA * b2Vec2.CrossVV(this.m_rA, PA);
// vB += m_invMassB * PB;
vB.SelfMulAdd(this.m_invMassB, PB);
wB += this.m_invIB * b2Vec2.CrossVV(this.m_rB, PB);
}
else {
this.m_impulse = 0;
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2PulleyJoint.prototype.SolveVelocityConstraints = function (data) {
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
// b2Vec2 vpA = vA + b2Cross(wA, m_rA);
var vpA = b2Vec2.AddVCrossSV(vA, wA, this.m_rA, b2PulleyJoint.SolveVelocityConstraints_s_vpA);
// b2Vec2 vpB = vB + b2Cross(wB, m_rB);
var vpB = b2Vec2.AddVCrossSV(vB, wB, this.m_rB, b2PulleyJoint.SolveVelocityConstraints_s_vpB);
var Cdot = -b2Vec2.DotVV(this.m_uA, vpA) - this.m_ratio * b2Vec2.DotVV(this.m_uB, vpB);
var impulse = -this.m_mass * Cdot;
this.m_impulse += impulse;
// b2Vec2 PA = -impulse * m_uA;
var PA = b2Vec2.MulSV(-impulse, this.m_uA, b2PulleyJoint.SolveVelocityConstraints_s_PA);
// b2Vec2 PB = -m_ratio * impulse * m_uB;
var PB = b2Vec2.MulSV(-this.m_ratio * impulse, this.m_uB, b2PulleyJoint.SolveVelocityConstraints_s_PB);
// vA += m_invMassA * PA;
vA.SelfMulAdd(this.m_invMassA, PA);
wA += this.m_invIA * b2Vec2.CrossVV(this.m_rA, PA);
// vB += m_invMassB * PB;
vB.SelfMulAdd(this.m_invMassB, PB);
wB += this.m_invIB * b2Vec2.CrossVV(this.m_rB, PB);
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2PulleyJoint.prototype.SolvePositionConstraints = function (data) {
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
// b2Rot qA(aA), qB(aB);
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// Get the pulley axes.
// b2Vec2 uA = cA + rA - m_groundAnchorA;
var uA = this.m_uA.Copy(cA).SelfAdd(rA).SelfSub(this.m_groundAnchorA);
// b2Vec2 uB = cB + rB - m_groundAnchorB;
var uB = this.m_uB.Copy(cB).SelfAdd(rB).SelfSub(this.m_groundAnchorB);
var lengthA = uA.Length();
var lengthB = uB.Length();
if (lengthA > 10 * b2_linearSlop) {
uA.SelfMul(1 / lengthA);
}
else {
uA.SetZero();
}
if (lengthB > 10 * b2_linearSlop) {
uB.SelfMul(1 / lengthB);
}
else {
uB.SetZero();
}
// Compute effective mass.
var ruA = b2Vec2.CrossVV(rA, uA);
var ruB = b2Vec2.CrossVV(rB, uB);
var mA = this.m_invMassA + this.m_invIA * ruA * ruA;
var mB = this.m_invMassB + this.m_invIB * ruB * ruB;
var mass = mA + this.m_ratio * this.m_ratio * mB;
if (mass > 0) {
mass = 1 / mass;
}
var C = this.m_constant - lengthA - this.m_ratio * lengthB;
var linearError = b2Abs(C);
var impulse = -mass * C;
// b2Vec2 PA = -impulse * uA;
var PA = b2Vec2.MulSV(-impulse, uA, b2PulleyJoint.SolvePositionConstraints_s_PA);
// b2Vec2 PB = -m_ratio * impulse * uB;
var PB = b2Vec2.MulSV(-this.m_ratio * impulse, uB, b2PulleyJoint.SolvePositionConstraints_s_PB);
// cA += m_invMassA * PA;
cA.SelfMulAdd(this.m_invMassA, PA);
aA += this.m_invIA * b2Vec2.CrossVV(rA, PA);
// cB += m_invMassB * PB;
cB.SelfMulAdd(this.m_invMassB, PB);
aB += this.m_invIB * b2Vec2.CrossVV(rB, PB);
// data.positions[this.m_indexA].c = cA;
data.positions[this.m_indexA].a = aA;
// data.positions[this.m_indexB].c = cB;
data.positions[this.m_indexB].a = aB;
return linearError < b2_linearSlop;
};
b2PulleyJoint.prototype.GetAnchorA = function (out) {
return this.m_bodyA.GetWorldPoint(this.m_localAnchorA, out);
};
b2PulleyJoint.prototype.GetAnchorB = function (out) {
return this.m_bodyB.GetWorldPoint(this.m_localAnchorB, out);
};
b2PulleyJoint.prototype.GetReactionForce = function (inv_dt, out) {
// b2Vec2 P = m_impulse * m_uB;
// return inv_dt * P;
out.x = inv_dt * this.m_impulse * this.m_uB.x;
out.y = inv_dt * this.m_impulse * this.m_uB.y;
return out;
};
b2PulleyJoint.prototype.GetReactionTorque = function (inv_dt) {
return 0;
};
b2PulleyJoint.prototype.GetGroundAnchorA = function () {
return this.m_groundAnchorA;
};
b2PulleyJoint.prototype.GetGroundAnchorB = function () {
return this.m_groundAnchorB;
};
b2PulleyJoint.prototype.GetLengthA = function () {
return this.m_lengthA;
};
b2PulleyJoint.prototype.GetLengthB = function () {
return this.m_lengthB;
};
b2PulleyJoint.prototype.GetRatio = function () {
return this.m_ratio;
};
b2PulleyJoint.prototype.GetCurrentLengthA = function () {
// b2Vec2 p = m_bodyA->GetWorldPoint(m_localAnchorA);
// b2Vec2 s = m_groundAnchorA;
// b2Vec2 d = p - s;
// return d.Length();
var p = this.m_bodyA.GetWorldPoint(this.m_localAnchorA, b2PulleyJoint.GetCurrentLengthA_s_p);
var s = this.m_groundAnchorA;
return b2Vec2.DistanceVV(p, s);
};
b2PulleyJoint.prototype.GetCurrentLengthB = function () {
// b2Vec2 p = m_bodyB->GetWorldPoint(m_localAnchorB);
// b2Vec2 s = m_groundAnchorB;
// b2Vec2 d = p - s;
// return d.Length();
var p = this.m_bodyB.GetWorldPoint(this.m_localAnchorB, b2PulleyJoint.GetCurrentLengthB_s_p);
var s = this.m_groundAnchorB;
return b2Vec2.DistanceVV(p, s);
};
b2PulleyJoint.prototype.Dump = function (log) {
var indexA = this.m_bodyA.m_islandIndex;
var indexB = this.m_bodyB.m_islandIndex;
log(" const jd: b2PulleyJointDef = new b2PulleyJointDef();\n");
log(" jd.bodyA = bodies[%d];\n", indexA);
log(" jd.bodyB = bodies[%d];\n", indexB);
log(" jd.collideConnected = %s;\n", (this.m_collideConnected) ? ("true") : ("false"));
log(" jd.groundAnchorA.Set(%.15f, %.15f);\n", this.m_groundAnchorA.x, this.m_groundAnchorA.y);
log(" jd.groundAnchorB.Set(%.15f, %.15f);\n", this.m_groundAnchorB.x, this.m_groundAnchorB.y);
log(" jd.localAnchorA.Set(%.15f, %.15f);\n", this.m_localAnchorA.x, this.m_localAnchorA.y);
log(" jd.localAnchorB.Set(%.15f, %.15f);\n", this.m_localAnchorB.x, this.m_localAnchorB.y);
log(" jd.lengthA = %.15f;\n", this.m_lengthA);
log(" jd.lengthB = %.15f;\n", this.m_lengthB);
log(" jd.ratio = %.15f;\n", this.m_ratio);
log(" joints[%d] = this.m_world.CreateJoint(jd);\n", this.m_index);
};
b2PulleyJoint.prototype.ShiftOrigin = function (newOrigin) {
this.m_groundAnchorA.SelfSub(newOrigin);
this.m_groundAnchorB.SelfSub(newOrigin);
};
b2PulleyJoint.InitVelocityConstraints_s_PA = new b2Vec2();
b2PulleyJoint.InitVelocityConstraints_s_PB = new b2Vec2();
b2PulleyJoint.SolveVelocityConstraints_s_vpA = new b2Vec2();
b2PulleyJoint.SolveVelocityConstraints_s_vpB = new b2Vec2();
b2PulleyJoint.SolveVelocityConstraints_s_PA = new b2Vec2();
b2PulleyJoint.SolveVelocityConstraints_s_PB = new b2Vec2();
b2PulleyJoint.SolvePositionConstraints_s_PA = new b2Vec2();
b2PulleyJoint.SolvePositionConstraints_s_PB = new b2Vec2();
b2PulleyJoint.GetCurrentLengthA_s_p = new b2Vec2();
b2PulleyJoint.GetCurrentLengthB_s_p = new b2Vec2();
return b2PulleyJoint;
}(b2Joint));
/*
* 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.
*/
/// Revolute joint definition. This requires defining an
/// anchor point where the bodies are joined. The definition
/// uses local anchor points so that the initial configuration
/// can violate the constraint slightly. You also need to
/// specify the initial relative angle for joint limits. This
/// helps when saving and loading a game.
/// The local anchor points are measured from the body's origin
/// rather than the center of mass because:
/// 1. you might not know where the center of mass will be.
/// 2. if you add/remove shapes from a body and recompute the mass,
/// the joints will be broken.
var b2RevoluteJointDef = /** @class */ (function (_super) {
__extends(b2RevoluteJointDef, _super);
function b2RevoluteJointDef() {
var _this = _super.call(this, exports.b2JointType.e_revoluteJoint) || this;
_this.localAnchorA = new b2Vec2(0, 0);
_this.localAnchorB = new b2Vec2(0, 0);
_this.referenceAngle = 0;
_this.enableLimit = false;
_this.lowerAngle = 0;
_this.upperAngle = 0;
_this.enableMotor = false;
_this.motorSpeed = 0;
_this.maxMotorTorque = 0;
return _this;
}
b2RevoluteJointDef.prototype.Initialize = function (bA, bB, anchor) {
this.bodyA = bA;
this.bodyB = bB;
this.bodyA.GetLocalPoint(anchor, this.localAnchorA);
this.bodyB.GetLocalPoint(anchor, this.localAnchorB);
this.referenceAngle = this.bodyB.GetAngle() - this.bodyA.GetAngle();
};
return b2RevoluteJointDef;
}(b2JointDef));
var b2RevoluteJoint = /** @class */ (function (_super) {
__extends(b2RevoluteJoint, _super);
function b2RevoluteJoint(def) {
var _this = _super.call(this, def) || this;
// Solver shared
_this.m_localAnchorA = new b2Vec2();
_this.m_localAnchorB = new b2Vec2();
_this.m_impulse = new b2Vec3();
_this.m_motorImpulse = 0;
_this.m_enableMotor = false;
_this.m_maxMotorTorque = 0;
_this.m_motorSpeed = 0;
_this.m_enableLimit = false;
_this.m_referenceAngle = 0;
_this.m_lowerAngle = 0;
_this.m_upperAngle = 0;
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_rA = new b2Vec2();
_this.m_rB = new b2Vec2();
_this.m_localCenterA = new b2Vec2();
_this.m_localCenterB = new b2Vec2();
_this.m_invMassA = 0;
_this.m_invMassB = 0;
_this.m_invIA = 0;
_this.m_invIB = 0;
_this.m_mass = new b2Mat33(); // effective mass for point-to-point constraint.
_this.m_motorMass = 0; // effective mass for motor/limit angular constraint.
_this.m_limitState = exports.b2LimitState.e_inactiveLimit;
_this.m_qA = new b2Rot();
_this.m_qB = new b2Rot();
_this.m_lalcA = new b2Vec2();
_this.m_lalcB = new b2Vec2();
_this.m_K = new b2Mat22();
_this.m_localAnchorA.Copy(b2Maybe(def.localAnchorA, b2Vec2.ZERO));
_this.m_localAnchorB.Copy(b2Maybe(def.localAnchorB, b2Vec2.ZERO));
_this.m_referenceAngle = b2Maybe(def.referenceAngle, 0);
_this.m_impulse.SetZero();
_this.m_motorImpulse = 0;
_this.m_lowerAngle = b2Maybe(def.lowerAngle, 0);
_this.m_upperAngle = b2Maybe(def.upperAngle, 0);
_this.m_maxMotorTorque = b2Maybe(def.maxMotorTorque, 0);
_this.m_motorSpeed = b2Maybe(def.motorSpeed, 0);
_this.m_enableLimit = b2Maybe(def.enableLimit, false);
_this.m_enableMotor = b2Maybe(def.enableMotor, false);
_this.m_limitState = exports.b2LimitState.e_inactiveLimit;
return _this;
}
b2RevoluteJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexA = this.m_bodyA.m_islandIndex;
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_localCenterA.Copy(this.m_bodyA.m_sweep.localCenter);
this.m_localCenterB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_invMassA = this.m_bodyA.m_invMass;
this.m_invMassB = this.m_bodyB.m_invMass;
this.m_invIA = this.m_bodyA.m_invI;
this.m_invIB = this.m_bodyB.m_invI;
var aA = data.positions[this.m_indexA].a;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
// b2Rot qA(aA), qB(aB);
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// J = [-I -r1_skew I r2_skew]
// [ 0 -1 0 1]
// r_skew = [-ry; rx]
// Matlab
// K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB]
// [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB]
// [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB]
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
var fixedRotation = (iA + iB === 0);
this.m_mass.ex.x = mA + mB + this.m_rA.y * this.m_rA.y * iA + this.m_rB.y * this.m_rB.y * iB;
this.m_mass.ey.x = -this.m_rA.y * this.m_rA.x * iA - this.m_rB.y * this.m_rB.x * iB;
this.m_mass.ez.x = -this.m_rA.y * iA - this.m_rB.y * iB;
this.m_mass.ex.y = this.m_mass.ey.x;
this.m_mass.ey.y = mA + mB + this.m_rA.x * this.m_rA.x * iA + this.m_rB.x * this.m_rB.x * iB;
this.m_mass.ez.y = this.m_rA.x * iA + this.m_rB.x * iB;
this.m_mass.ex.z = this.m_mass.ez.x;
this.m_mass.ey.z = this.m_mass.ez.y;
this.m_mass.ez.z = iA + iB;
this.m_motorMass = iA + iB;
if (this.m_motorMass > 0) {
this.m_motorMass = 1 / this.m_motorMass;
}
if (!this.m_enableMotor || fixedRotation) {
this.m_motorImpulse = 0;
}
if (this.m_enableLimit && !fixedRotation) {
var jointAngle = aB - aA - this.m_referenceAngle;
if (b2Abs(this.m_upperAngle - this.m_lowerAngle) < 2 * b2_angularSlop) {
this.m_limitState = exports.b2LimitState.e_equalLimits;
}
else if (jointAngle <= this.m_lowerAngle) {
if (this.m_limitState !== exports.b2LimitState.e_atLowerLimit) {
this.m_impulse.z = 0;
}
this.m_limitState = exports.b2LimitState.e_atLowerLimit;
}
else if (jointAngle >= this.m_upperAngle) {
if (this.m_limitState !== exports.b2LimitState.e_atUpperLimit) {
this.m_impulse.z = 0;
}
this.m_limitState = exports.b2LimitState.e_atUpperLimit;
}
else {
this.m_limitState = exports.b2LimitState.e_inactiveLimit;
this.m_impulse.z = 0;
}
}
else {
this.m_limitState = exports.b2LimitState.e_inactiveLimit;
}
if (data.step.warmStarting) {
// Scale impulses to support a variable time step.
this.m_impulse.SelfMul(data.step.dtRatio);
this.m_motorImpulse *= data.step.dtRatio;
// b2Vec2 P(m_impulse.x, m_impulse.y);
var P = b2RevoluteJoint.InitVelocityConstraints_s_P.Set(this.m_impulse.x, this.m_impulse.y);
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * (b2Vec2.CrossVV(this.m_rA, P) + this.m_motorImpulse + this.m_impulse.z);
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * (b2Vec2.CrossVV(this.m_rB, P) + this.m_motorImpulse + this.m_impulse.z);
}
else {
this.m_impulse.SetZero();
this.m_motorImpulse = 0;
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2RevoluteJoint.prototype.SolveVelocityConstraints = function (data) {
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
var fixedRotation = (iA + iB === 0);
// Solve motor constraint.
if (this.m_enableMotor && this.m_limitState !== exports.b2LimitState.e_equalLimits && !fixedRotation) {
var Cdot = wB - wA - this.m_motorSpeed;
var impulse = -this.m_motorMass * Cdot;
var oldImpulse = this.m_motorImpulse;
var maxImpulse = data.step.dt * this.m_maxMotorTorque;
this.m_motorImpulse = b2Clamp(this.m_motorImpulse + impulse, -maxImpulse, maxImpulse);
impulse = this.m_motorImpulse - oldImpulse;
wA -= iA * impulse;
wB += iB * impulse;
}
// Solve limit constraint.
if (this.m_enableLimit && this.m_limitState !== exports.b2LimitState.e_inactiveLimit && !fixedRotation) {
// b2Vec2 Cdot1 = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA);
var Cdot1 = b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, this.m_rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, this.m_rA, b2Vec2.s_t1), b2RevoluteJoint.SolveVelocityConstraints_s_Cdot1);
var Cdot2 = wB - wA;
// b2Vec3 Cdot(Cdot1.x, Cdot1.y, Cdot2);
// b2Vec3 impulse = -this.m_mass.Solve33(Cdot);
var impulse_v3 = this.m_mass.Solve33(Cdot1.x, Cdot1.y, Cdot2, b2RevoluteJoint.SolveVelocityConstraints_s_impulse_v3).SelfNeg();
if (this.m_limitState === exports.b2LimitState.e_equalLimits) {
this.m_impulse.SelfAdd(impulse_v3);
}
else if (this.m_limitState === exports.b2LimitState.e_atLowerLimit) {
var newImpulse = this.m_impulse.z + impulse_v3.z;
if (newImpulse < 0) {
// b2Vec2 rhs = -Cdot1 + m_impulse.z * b2Vec2(m_mass.ez.x, m_mass.ez.y);
var rhs_x = -Cdot1.x + this.m_impulse.z * this.m_mass.ez.x;
var rhs_y = -Cdot1.y + this.m_impulse.z * this.m_mass.ez.y;
var reduced_v2 = this.m_mass.Solve22(rhs_x, rhs_y, b2RevoluteJoint.SolveVelocityConstraints_s_reduced_v2);
impulse_v3.x = reduced_v2.x;
impulse_v3.y = reduced_v2.y;
impulse_v3.z = -this.m_impulse.z;
this.m_impulse.x += reduced_v2.x;
this.m_impulse.y += reduced_v2.y;
this.m_impulse.z = 0;
}
else {
this.m_impulse.SelfAdd(impulse_v3);
}
}
else if (this.m_limitState === exports.b2LimitState.e_atUpperLimit) {
var newImpulse = this.m_impulse.z + impulse_v3.z;
if (newImpulse > 0) {
// b2Vec2 rhs = -Cdot1 + m_impulse.z * b2Vec2(m_mass.ez.x, m_mass.ez.y);
var rhs_x = -Cdot1.x + this.m_impulse.z * this.m_mass.ez.x;
var rhs_y = -Cdot1.y + this.m_impulse.z * this.m_mass.ez.y;
var reduced_v2 = this.m_mass.Solve22(rhs_x, rhs_y, b2RevoluteJoint.SolveVelocityConstraints_s_reduced_v2);
impulse_v3.x = reduced_v2.x;
impulse_v3.y = reduced_v2.y;
impulse_v3.z = -this.m_impulse.z;
this.m_impulse.x += reduced_v2.x;
this.m_impulse.y += reduced_v2.y;
this.m_impulse.z = 0;
}
else {
this.m_impulse.SelfAdd(impulse_v3);
}
}
// b2Vec2 P(impulse.x, impulse.y);
var P = b2RevoluteJoint.SolveVelocityConstraints_s_P.Set(impulse_v3.x, impulse_v3.y);
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * (b2Vec2.CrossVV(this.m_rA, P) + impulse_v3.z);
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * (b2Vec2.CrossVV(this.m_rB, P) + impulse_v3.z);
}
else {
// Solve point-to-point constraint
// b2Vec2 Cdot = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA);
var Cdot_v2 = b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, this.m_rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, this.m_rA, b2Vec2.s_t1), b2RevoluteJoint.SolveVelocityConstraints_s_Cdot_v2);
// b2Vec2 impulse = m_mass.Solve22(-Cdot);
var impulse_v2 = this.m_mass.Solve22(-Cdot_v2.x, -Cdot_v2.y, b2RevoluteJoint.SolveVelocityConstraints_s_impulse_v2);
this.m_impulse.x += impulse_v2.x;
this.m_impulse.y += impulse_v2.y;
// vA -= mA * impulse;
vA.SelfMulSub(mA, impulse_v2);
wA -= iA * b2Vec2.CrossVV(this.m_rA, impulse_v2);
// vB += mB * impulse;
vB.SelfMulAdd(mB, impulse_v2);
wB += iB * b2Vec2.CrossVV(this.m_rB, impulse_v2);
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2RevoluteJoint.prototype.SolvePositionConstraints = function (data) {
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
// b2Rot qA(aA), qB(aB);
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
var angularError = 0;
var positionError = 0;
var fixedRotation = (this.m_invIA + this.m_invIB === 0);
// Solve angular limit constraint.
if (this.m_enableLimit && this.m_limitState !== exports.b2LimitState.e_inactiveLimit && !fixedRotation) {
var angle = aB - aA - this.m_referenceAngle;
var limitImpulse = 0;
if (this.m_limitState === exports.b2LimitState.e_equalLimits) {
// Prevent large angular corrections
var C = b2Clamp(angle - this.m_lowerAngle, -b2_maxAngularCorrection, b2_maxAngularCorrection);
limitImpulse = -this.m_motorMass * C;
angularError = b2Abs(C);
}
else if (this.m_limitState === exports.b2LimitState.e_atLowerLimit) {
var C = angle - this.m_lowerAngle;
angularError = -C;
// Prevent large angular corrections and allow some slop.
C = b2Clamp(C + b2_angularSlop, -b2_maxAngularCorrection, 0);
limitImpulse = -this.m_motorMass * C;
}
else if (this.m_limitState === exports.b2LimitState.e_atUpperLimit) {
var C = angle - this.m_upperAngle;
angularError = C;
// Prevent large angular corrections and allow some slop.
C = b2Clamp(C - b2_angularSlop, 0, b2_maxAngularCorrection);
limitImpulse = -this.m_motorMass * C;
}
aA -= this.m_invIA * limitImpulse;
aB += this.m_invIB * limitImpulse;
}
// Solve point-to-point constraint.
{
qA.SetAngle(aA);
qB.SetAngle(aB);
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// b2Vec2 C = cB + rB - cA - rA;
var C_v2 = b2Vec2.SubVV(b2Vec2.AddVV(cB, rB, b2Vec2.s_t0), b2Vec2.AddVV(cA, rA, b2Vec2.s_t1), b2RevoluteJoint.SolvePositionConstraints_s_C_v2);
// positionError = C.Length();
positionError = C_v2.Length();
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
var K = this.m_K;
K.ex.x = mA + mB + iA * rA.y * rA.y + iB * rB.y * rB.y;
K.ex.y = -iA * rA.x * rA.y - iB * rB.x * rB.y;
K.ey.x = K.ex.y;
K.ey.y = mA + mB + iA * rA.x * rA.x + iB * rB.x * rB.x;
// b2Vec2 impulse = -K.Solve(C);
var impulse = K.Solve(C_v2.x, C_v2.y, b2RevoluteJoint.SolvePositionConstraints_s_impulse).SelfNeg();
// cA -= mA * impulse;
cA.SelfMulSub(mA, impulse);
aA -= iA * b2Vec2.CrossVV(rA, impulse);
// cB += mB * impulse;
cB.SelfMulAdd(mB, impulse);
aB += iB * b2Vec2.CrossVV(rB, impulse);
}
// data.positions[this.m_indexA].c = cA;
data.positions[this.m_indexA].a = aA;
// data.positions[this.m_indexB].c = cB;
data.positions[this.m_indexB].a = aB;
return positionError <= b2_linearSlop && angularError <= b2_angularSlop;
};
b2RevoluteJoint.prototype.GetAnchorA = function (out) {
return this.m_bodyA.GetWorldPoint(this.m_localAnchorA, out);
};
b2RevoluteJoint.prototype.GetAnchorB = function (out) {
return this.m_bodyB.GetWorldPoint(this.m_localAnchorB, out);
};
b2RevoluteJoint.prototype.GetReactionForce = function (inv_dt, out) {
// b2Vec2 P(this.m_impulse.x, this.m_impulse.y);
// return inv_dt * P;
out.x = inv_dt * this.m_impulse.x;
out.y = inv_dt * this.m_impulse.y;
return out;
};
b2RevoluteJoint.prototype.GetReactionTorque = function (inv_dt) {
return inv_dt * this.m_impulse.z;
};
b2RevoluteJoint.prototype.GetLocalAnchorA = function () { return this.m_localAnchorA; };
b2RevoluteJoint.prototype.GetLocalAnchorB = function () { return this.m_localAnchorB; };
b2RevoluteJoint.prototype.GetReferenceAngle = function () { return this.m_referenceAngle; };
b2RevoluteJoint.prototype.GetJointAngle = function () {
// b2Body* bA = this.m_bodyA;
// b2Body* bB = this.m_bodyB;
// return bB->this.m_sweep.a - bA->this.m_sweep.a - this.m_referenceAngle;
return this.m_bodyB.m_sweep.a - this.m_bodyA.m_sweep.a - this.m_referenceAngle;
};
b2RevoluteJoint.prototype.GetJointSpeed = function () {
// b2Body* bA = this.m_bodyA;
// b2Body* bB = this.m_bodyB;
// return bB->this.m_angularVelocity - bA->this.m_angularVelocity;
return this.m_bodyB.m_angularVelocity - this.m_bodyA.m_angularVelocity;
};
b2RevoluteJoint.prototype.IsMotorEnabled = function () {
return this.m_enableMotor;
};
b2RevoluteJoint.prototype.EnableMotor = function (flag) {
if (flag !== this.m_enableMotor) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_enableMotor = flag;
}
};
b2RevoluteJoint.prototype.GetMotorTorque = function (inv_dt) {
return inv_dt * this.m_motorImpulse;
};
b2RevoluteJoint.prototype.GetMotorSpeed = function () {
return this.m_motorSpeed;
};
b2RevoluteJoint.prototype.SetMaxMotorTorque = function (torque) {
if (torque !== this.m_maxMotorTorque) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_maxMotorTorque = torque;
}
};
b2RevoluteJoint.prototype.GetMaxMotorTorque = function () { return this.m_maxMotorTorque; };
b2RevoluteJoint.prototype.IsLimitEnabled = function () {
return this.m_enableLimit;
};
b2RevoluteJoint.prototype.EnableLimit = function (flag) {
if (flag !== this.m_enableLimit) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_enableLimit = flag;
this.m_impulse.z = 0;
}
};
b2RevoluteJoint.prototype.GetLowerLimit = function () {
return this.m_lowerAngle;
};
b2RevoluteJoint.prototype.GetUpperLimit = function () {
return this.m_upperAngle;
};
b2RevoluteJoint.prototype.SetLimits = function (lower, upper) {
if (lower !== this.m_lowerAngle || upper !== this.m_upperAngle) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_impulse.z = 0;
this.m_lowerAngle = lower;
this.m_upperAngle = upper;
}
};
b2RevoluteJoint.prototype.SetMotorSpeed = function (speed) {
if (speed !== this.m_motorSpeed) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_motorSpeed = speed;
}
};
b2RevoluteJoint.prototype.Dump = function (log) {
var indexA = this.m_bodyA.m_islandIndex;
var indexB = this.m_bodyB.m_islandIndex;
log(" const jd: b2RevoluteJointDef = new b2RevoluteJointDef();\n");
log(" jd.bodyA = bodies[%d];\n", indexA);
log(" jd.bodyB = bodies[%d];\n", indexB);
log(" jd.collideConnected = %s;\n", (this.m_collideConnected) ? ("true") : ("false"));
log(" jd.localAnchorA.Set(%.15f, %.15f);\n", this.m_localAnchorA.x, this.m_localAnchorA.y);
log(" jd.localAnchorB.Set(%.15f, %.15f);\n", this.m_localAnchorB.x, this.m_localAnchorB.y);
log(" jd.referenceAngle = %.15f;\n", this.m_referenceAngle);
log(" jd.enableLimit = %s;\n", (this.m_enableLimit) ? ("true") : ("false"));
log(" jd.lowerAngle = %.15f;\n", this.m_lowerAngle);
log(" jd.upperAngle = %.15f;\n", this.m_upperAngle);
log(" jd.enableMotor = %s;\n", (this.m_enableMotor) ? ("true") : ("false"));
log(" jd.motorSpeed = %.15f;\n", this.m_motorSpeed);
log(" jd.maxMotorTorque = %.15f;\n", this.m_maxMotorTorque);
log(" joints[%d] = this.m_world.CreateJoint(jd);\n", this.m_index);
};
b2RevoluteJoint.InitVelocityConstraints_s_P = new b2Vec2();
b2RevoluteJoint.SolveVelocityConstraints_s_P = new b2Vec2();
b2RevoluteJoint.SolveVelocityConstraints_s_Cdot_v2 = new b2Vec2();
b2RevoluteJoint.SolveVelocityConstraints_s_Cdot1 = new b2Vec2();
b2RevoluteJoint.SolveVelocityConstraints_s_impulse_v3 = new b2Vec3();
b2RevoluteJoint.SolveVelocityConstraints_s_reduced_v2 = new b2Vec2();
b2RevoluteJoint.SolveVelocityConstraints_s_impulse_v2 = new b2Vec2();
b2RevoluteJoint.SolvePositionConstraints_s_C_v2 = new b2Vec2();
b2RevoluteJoint.SolvePositionConstraints_s_impulse = new b2Vec2();
return b2RevoluteJoint;
}(b2Joint));
/*
* 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.
*/
/// Rope joint definition. This requires two body anchor points and
/// a maximum lengths.
/// Note: by default the connected objects will not collide.
/// see collideConnected in b2JointDef.
var b2RopeJointDef = /** @class */ (function (_super) {
__extends(b2RopeJointDef, _super);
function b2RopeJointDef() {
var _this = _super.call(this, exports.b2JointType.e_ropeJoint) || this;
_this.localAnchorA = new b2Vec2(-1, 0);
_this.localAnchorB = new b2Vec2(1, 0);
_this.maxLength = 0;
return _this;
}
return b2RopeJointDef;
}(b2JointDef));
var b2RopeJoint = /** @class */ (function (_super) {
__extends(b2RopeJoint, _super);
function b2RopeJoint(def) {
var _this = _super.call(this, def) || this;
// Solver shared
_this.m_localAnchorA = new b2Vec2();
_this.m_localAnchorB = new b2Vec2();
_this.m_maxLength = 0;
_this.m_length = 0;
_this.m_impulse = 0;
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_u = new b2Vec2();
_this.m_rA = new b2Vec2();
_this.m_rB = new b2Vec2();
_this.m_localCenterA = new b2Vec2();
_this.m_localCenterB = new b2Vec2();
_this.m_invMassA = 0;
_this.m_invMassB = 0;
_this.m_invIA = 0;
_this.m_invIB = 0;
_this.m_mass = 0;
_this.m_state = exports.b2LimitState.e_inactiveLimit;
_this.m_qA = new b2Rot();
_this.m_qB = new b2Rot();
_this.m_lalcA = new b2Vec2();
_this.m_lalcB = new b2Vec2();
_this.m_localAnchorA.Copy(b2Maybe(def.localAnchorA, new b2Vec2(-1, 0)));
_this.m_localAnchorB.Copy(b2Maybe(def.localAnchorB, new b2Vec2(1, 0)));
_this.m_maxLength = b2Maybe(def.maxLength, 0);
return _this;
}
b2RopeJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexA = this.m_bodyA.m_islandIndex;
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_localCenterA.Copy(this.m_bodyA.m_sweep.localCenter);
this.m_localCenterB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_invMassA = this.m_bodyA.m_invMass;
this.m_invMassB = this.m_bodyB.m_invMass;
this.m_invIA = this.m_bodyA.m_invI;
this.m_invIB = this.m_bodyB.m_invI;
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// this.m_rA = b2Mul(qA, this.m_localAnchorA - this.m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// this.m_rB = b2Mul(qB, this.m_localAnchorB - this.m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// this.m_u = cB + this.m_rB - cA - this.m_rA;
this.m_u.Copy(cB).SelfAdd(this.m_rB).SelfSub(cA).SelfSub(this.m_rA);
this.m_length = this.m_u.Length();
var C = this.m_length - this.m_maxLength;
if (C > 0) {
this.m_state = exports.b2LimitState.e_atUpperLimit;
}
else {
this.m_state = exports.b2LimitState.e_inactiveLimit;
}
if (this.m_length > b2_linearSlop) {
this.m_u.SelfMul(1 / this.m_length);
}
else {
this.m_u.SetZero();
this.m_mass = 0;
this.m_impulse = 0;
return;
}
// Compute effective mass.
var crA = b2Vec2.CrossVV(this.m_rA, this.m_u);
var crB = b2Vec2.CrossVV(this.m_rB, this.m_u);
var invMass = this.m_invMassA + this.m_invIA * crA * crA + this.m_invMassB + this.m_invIB * crB * crB;
this.m_mass = invMass !== 0 ? 1 / invMass : 0;
if (data.step.warmStarting) {
// Scale the impulse to support a variable time step.
this.m_impulse *= data.step.dtRatio;
// b2Vec2 P = m_impulse * m_u;
var P = b2Vec2.MulSV(this.m_impulse, this.m_u, b2RopeJoint.InitVelocityConstraints_s_P);
// vA -= m_invMassA * P;
vA.SelfMulSub(this.m_invMassA, P);
wA -= this.m_invIA * b2Vec2.CrossVV(this.m_rA, P);
// vB += m_invMassB * P;
vB.SelfMulAdd(this.m_invMassB, P);
wB += this.m_invIB * b2Vec2.CrossVV(this.m_rB, P);
}
else {
this.m_impulse = 0;
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2RopeJoint.prototype.SolveVelocityConstraints = function (data) {
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
// Cdot = dot(u, v + cross(w, r))
// b2Vec2 vpA = vA + b2Cross(wA, m_rA);
var vpA = b2Vec2.AddVCrossSV(vA, wA, this.m_rA, b2RopeJoint.SolveVelocityConstraints_s_vpA);
// b2Vec2 vpB = vB + b2Cross(wB, m_rB);
var vpB = b2Vec2.AddVCrossSV(vB, wB, this.m_rB, b2RopeJoint.SolveVelocityConstraints_s_vpB);
// float32 C = m_length - m_maxLength;
var C = this.m_length - this.m_maxLength;
// float32 Cdot = b2Dot(m_u, vpB - vpA);
var Cdot = b2Vec2.DotVV(this.m_u, b2Vec2.SubVV(vpB, vpA, b2Vec2.s_t0));
// Predictive constraint.
if (C < 0) {
Cdot += data.step.inv_dt * C;
}
var impulse = -this.m_mass * Cdot;
var oldImpulse = this.m_impulse;
this.m_impulse = b2Min(0, this.m_impulse + impulse);
impulse = this.m_impulse - oldImpulse;
// b2Vec2 P = impulse * m_u;
var P = b2Vec2.MulSV(impulse, this.m_u, b2RopeJoint.SolveVelocityConstraints_s_P);
// vA -= m_invMassA * P;
vA.SelfMulSub(this.m_invMassA, P);
wA -= this.m_invIA * b2Vec2.CrossVV(this.m_rA, P);
// vB += m_invMassB * P;
vB.SelfMulAdd(this.m_invMassB, P);
wB += this.m_invIB * b2Vec2.CrossVV(this.m_rB, P);
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2RopeJoint.prototype.SolvePositionConstraints = function (data) {
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// b2Vec2 rA = b2Mul(qA, this.m_localAnchorA - this.m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// b2Vec2 rB = b2Mul(qB, this.m_localAnchorB - this.m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// b2Vec2 u = cB + rB - cA - rA;
var u = this.m_u.Copy(cB).SelfAdd(rB).SelfSub(cA).SelfSub(rA);
var length = u.Normalize();
var C = length - this.m_maxLength;
C = b2Clamp(C, 0, b2_maxLinearCorrection);
var impulse = -this.m_mass * C;
// b2Vec2 P = impulse * u;
var P = b2Vec2.MulSV(impulse, u, b2RopeJoint.SolvePositionConstraints_s_P);
// cA -= m_invMassA * P;
cA.SelfMulSub(this.m_invMassA, P);
aA -= this.m_invIA * b2Vec2.CrossVV(rA, P);
// cB += m_invMassB * P;
cB.SelfMulAdd(this.m_invMassB, P);
aB += this.m_invIB * b2Vec2.CrossVV(rB, P);
// data.positions[this.m_indexA].c = cA;
data.positions[this.m_indexA].a = aA;
// data.positions[this.m_indexB].c = cB;
data.positions[this.m_indexB].a = aB;
return length - this.m_maxLength < b2_linearSlop;
};
b2RopeJoint.prototype.GetAnchorA = function (out) {
return this.m_bodyA.GetWorldPoint(this.m_localAnchorA, out);
};
b2RopeJoint.prototype.GetAnchorB = function (out) {
return this.m_bodyB.GetWorldPoint(this.m_localAnchorB, out);
};
b2RopeJoint.prototype.GetReactionForce = function (inv_dt, out) {
// return out.Set(inv_dt * this.m_linearImpulse.x, inv_dt * this.m_linearImpulse.y);
return b2Vec2.MulSV((inv_dt * this.m_impulse), this.m_u, out);
};
b2RopeJoint.prototype.GetReactionTorque = function (inv_dt) {
return 0;
};
b2RopeJoint.prototype.GetLocalAnchorA = function () { return this.m_localAnchorA; };
b2RopeJoint.prototype.GetLocalAnchorB = function () { return this.m_localAnchorB; };
b2RopeJoint.prototype.SetMaxLength = function (length) { this.m_maxLength = length; };
b2RopeJoint.prototype.GetMaxLength = function () {
return this.m_maxLength;
};
b2RopeJoint.prototype.GetLimitState = function () {
return this.m_state;
};
b2RopeJoint.prototype.Dump = function (log) {
var indexA = this.m_bodyA.m_islandIndex;
var indexB = this.m_bodyB.m_islandIndex;
log(" const jd: b2RopeJointDef = new b2RopeJointDef();\n");
log(" jd.bodyA = bodies[%d];\n", indexA);
log(" jd.bodyB = bodies[%d];\n", indexB);
log(" jd.collideConnected = %s;\n", (this.m_collideConnected) ? ("true") : ("false"));
log(" jd.localAnchorA.Set(%.15f, %.15f);\n", this.m_localAnchorA.x, this.m_localAnchorA.y);
log(" jd.localAnchorB.Set(%.15f, %.15f);\n", this.m_localAnchorB.x, this.m_localAnchorB.y);
log(" jd.maxLength = %.15f;\n", this.m_maxLength);
log(" joints[%d] = this.m_world.CreateJoint(jd);\n", this.m_index);
};
b2RopeJoint.InitVelocityConstraints_s_P = new b2Vec2();
b2RopeJoint.SolveVelocityConstraints_s_vpA = new b2Vec2();
b2RopeJoint.SolveVelocityConstraints_s_vpB = new b2Vec2();
b2RopeJoint.SolveVelocityConstraints_s_P = new b2Vec2();
b2RopeJoint.SolvePositionConstraints_s_P = new b2Vec2();
return b2RopeJoint;
}(b2Joint));
/*
* 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.
*/
/// Weld joint definition. You need to specify local anchor points
/// where they are attached and the relative body angle. The position
/// of the anchor points is important for computing the reaction torque.
var b2WeldJointDef = /** @class */ (function (_super) {
__extends(b2WeldJointDef, _super);
function b2WeldJointDef() {
var _this = _super.call(this, exports.b2JointType.e_weldJoint) || this;
_this.localAnchorA = new b2Vec2();
_this.localAnchorB = new b2Vec2();
_this.referenceAngle = 0;
_this.frequencyHz = 0;
_this.dampingRatio = 0;
return _this;
}
b2WeldJointDef.prototype.Initialize = function (bA, bB, anchor) {
this.bodyA = bA;
this.bodyB = bB;
this.bodyA.GetLocalPoint(anchor, this.localAnchorA);
this.bodyB.GetLocalPoint(anchor, this.localAnchorB);
this.referenceAngle = this.bodyB.GetAngle() - this.bodyA.GetAngle();
};
return b2WeldJointDef;
}(b2JointDef));
var b2WeldJoint = /** @class */ (function (_super) {
__extends(b2WeldJoint, _super);
function b2WeldJoint(def) {
var _this = _super.call(this, def) || this;
_this.m_frequencyHz = 0;
_this.m_dampingRatio = 0;
_this.m_bias = 0;
// Solver shared
_this.m_localAnchorA = new b2Vec2();
_this.m_localAnchorB = new b2Vec2();
_this.m_referenceAngle = 0;
_this.m_gamma = 0;
_this.m_impulse = new b2Vec3(0, 0, 0);
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_rA = new b2Vec2();
_this.m_rB = new b2Vec2();
_this.m_localCenterA = new b2Vec2();
_this.m_localCenterB = new b2Vec2();
_this.m_invMassA = 0;
_this.m_invMassB = 0;
_this.m_invIA = 0;
_this.m_invIB = 0;
_this.m_mass = new b2Mat33();
_this.m_qA = new b2Rot();
_this.m_qB = new b2Rot();
_this.m_lalcA = new b2Vec2();
_this.m_lalcB = new b2Vec2();
_this.m_K = new b2Mat33();
_this.m_frequencyHz = b2Maybe(def.frequencyHz, 0);
_this.m_dampingRatio = b2Maybe(def.dampingRatio, 0);
_this.m_localAnchorA.Copy(b2Maybe(def.localAnchorA, b2Vec2.ZERO));
_this.m_localAnchorB.Copy(b2Maybe(def.localAnchorB, b2Vec2.ZERO));
_this.m_referenceAngle = b2Maybe(def.referenceAngle, 0);
_this.m_impulse.SetZero();
return _this;
}
b2WeldJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexA = this.m_bodyA.m_islandIndex;
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_localCenterA.Copy(this.m_bodyA.m_sweep.localCenter);
this.m_localCenterB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_invMassA = this.m_bodyA.m_invMass;
this.m_invMassB = this.m_bodyB.m_invMass;
this.m_invIA = this.m_bodyA.m_invI;
this.m_invIB = this.m_bodyB.m_invI;
var aA = data.positions[this.m_indexA].a;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// J = [-I -r1_skew I r2_skew]
// [ 0 -1 0 1]
// r_skew = [-ry; rx]
// Matlab
// K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB]
// [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB]
// [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB]
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
var K = this.m_K;
K.ex.x = mA + mB + this.m_rA.y * this.m_rA.y * iA + this.m_rB.y * this.m_rB.y * iB;
K.ey.x = -this.m_rA.y * this.m_rA.x * iA - this.m_rB.y * this.m_rB.x * iB;
K.ez.x = -this.m_rA.y * iA - this.m_rB.y * iB;
K.ex.y = K.ey.x;
K.ey.y = mA + mB + this.m_rA.x * this.m_rA.x * iA + this.m_rB.x * this.m_rB.x * iB;
K.ez.y = this.m_rA.x * iA + this.m_rB.x * iB;
K.ex.z = K.ez.x;
K.ey.z = K.ez.y;
K.ez.z = iA + iB;
if (this.m_frequencyHz > 0) {
K.GetInverse22(this.m_mass);
var invM = iA + iB;
var m = invM > 0 ? 1 / invM : 0;
var C = aB - aA - this.m_referenceAngle;
// Frequency
var omega = 2 * b2_pi * this.m_frequencyHz;
// Damping coefficient
var d = 2 * m * this.m_dampingRatio * omega;
// Spring stiffness
var k = m * omega * omega;
// magic formulas
var h = data.step.dt;
this.m_gamma = h * (d + h * k);
this.m_gamma = this.m_gamma !== 0 ? 1 / this.m_gamma : 0;
this.m_bias = C * h * k * this.m_gamma;
invM += this.m_gamma;
this.m_mass.ez.z = invM !== 0 ? 1 / invM : 0;
}
else {
K.GetSymInverse33(this.m_mass);
this.m_gamma = 0;
this.m_bias = 0;
}
if (data.step.warmStarting) {
// Scale impulses to support a variable time step.
this.m_impulse.SelfMul(data.step.dtRatio);
// b2Vec2 P(m_impulse.x, m_impulse.y);
var P = b2WeldJoint.InitVelocityConstraints_s_P.Set(this.m_impulse.x, this.m_impulse.y);
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * (b2Vec2.CrossVV(this.m_rA, P) + this.m_impulse.z);
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * (b2Vec2.CrossVV(this.m_rB, P) + this.m_impulse.z);
}
else {
this.m_impulse.SetZero();
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2WeldJoint.prototype.SolveVelocityConstraints = function (data) {
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
if (this.m_frequencyHz > 0) {
var Cdot2 = wB - wA;
var impulse2 = -this.m_mass.ez.z * (Cdot2 + this.m_bias + this.m_gamma * this.m_impulse.z);
this.m_impulse.z += impulse2;
wA -= iA * impulse2;
wB += iB * impulse2;
// b2Vec2 Cdot1 = vB + b2Vec2.CrossSV(wB, this.m_rB) - vA - b2Vec2.CrossSV(wA, this.m_rA);
var Cdot1 = b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, this.m_rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, this.m_rA, b2Vec2.s_t1), b2WeldJoint.SolveVelocityConstraints_s_Cdot1);
// b2Vec2 impulse1 = -b2Mul22(m_mass, Cdot1);
var impulse1 = b2Mat33.MulM33XY(this.m_mass, Cdot1.x, Cdot1.y, b2WeldJoint.SolveVelocityConstraints_s_impulse1).SelfNeg();
this.m_impulse.x += impulse1.x;
this.m_impulse.y += impulse1.y;
// b2Vec2 P = impulse1;
var P = impulse1;
// vA -= mA * P;
vA.SelfMulSub(mA, P);
// wA -= iA * b2Cross(m_rA, P);
wA -= iA * b2Vec2.CrossVV(this.m_rA, P);
// vB += mB * P;
vB.SelfMulAdd(mB, P);
// wB += iB * b2Cross(m_rB, P);
wB += iB * b2Vec2.CrossVV(this.m_rB, P);
}
else {
// b2Vec2 Cdot1 = vB + b2Cross(wB, this.m_rB) - vA - b2Cross(wA, this.m_rA);
var Cdot1 = b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, this.m_rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, this.m_rA, b2Vec2.s_t1), b2WeldJoint.SolveVelocityConstraints_s_Cdot1);
var Cdot2 = wB - wA;
// b2Vec3 const Cdot(Cdot1.x, Cdot1.y, Cdot2);
// b2Vec3 impulse = -b2Mul(m_mass, Cdot);
var impulse = b2Mat33.MulM33XYZ(this.m_mass, Cdot1.x, Cdot1.y, Cdot2, b2WeldJoint.SolveVelocityConstraints_s_impulse).SelfNeg();
this.m_impulse.SelfAdd(impulse);
// b2Vec2 P(impulse.x, impulse.y);
var P = b2WeldJoint.SolveVelocityConstraints_s_P.Set(impulse.x, impulse.y);
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * (b2Vec2.CrossVV(this.m_rA, P) + impulse.z);
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * (b2Vec2.CrossVV(this.m_rB, P) + impulse.z);
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2WeldJoint.prototype.SolvePositionConstraints = function (data) {
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
var positionError, angularError;
var K = this.m_K;
K.ex.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB;
K.ey.x = -rA.y * rA.x * iA - rB.y * rB.x * iB;
K.ez.x = -rA.y * iA - rB.y * iB;
K.ex.y = K.ey.x;
K.ey.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB;
K.ez.y = rA.x * iA + rB.x * iB;
K.ex.z = K.ez.x;
K.ey.z = K.ez.y;
K.ez.z = iA + iB;
if (this.m_frequencyHz > 0) {
// b2Vec2 C1 = cB + rB - cA - rA;
var C1 = b2Vec2.SubVV(b2Vec2.AddVV(cB, rB, b2Vec2.s_t0), b2Vec2.AddVV(cA, rA, b2Vec2.s_t1), b2WeldJoint.SolvePositionConstraints_s_C1);
positionError = C1.Length();
angularError = 0;
// b2Vec2 P = -K.Solve22(C1);
var P = K.Solve22(C1.x, C1.y, b2WeldJoint.SolvePositionConstraints_s_P).SelfNeg();
// cA -= mA * P;
cA.SelfMulSub(mA, P);
aA -= iA * b2Vec2.CrossVV(rA, P);
// cB += mB * P;
cB.SelfMulAdd(mB, P);
aB += iB * b2Vec2.CrossVV(rB, P);
}
else {
// b2Vec2 C1 = cB + rB - cA - rA;
var C1 = b2Vec2.SubVV(b2Vec2.AddVV(cB, rB, b2Vec2.s_t0), b2Vec2.AddVV(cA, rA, b2Vec2.s_t1), b2WeldJoint.SolvePositionConstraints_s_C1);
var C2 = aB - aA - this.m_referenceAngle;
positionError = C1.Length();
angularError = b2Abs(C2);
// b2Vec3 C(C1.x, C1.y, C2);
// b2Vec3 impulse = -K.Solve33(C);
var impulse = K.Solve33(C1.x, C1.y, C2, b2WeldJoint.SolvePositionConstraints_s_impulse).SelfNeg();
// b2Vec2 P(impulse.x, impulse.y);
var P = b2WeldJoint.SolvePositionConstraints_s_P.Set(impulse.x, impulse.y);
// cA -= mA * P;
cA.SelfMulSub(mA, P);
aA -= iA * (b2Vec2.CrossVV(this.m_rA, P) + impulse.z);
// cB += mB * P;
cB.SelfMulAdd(mB, P);
aB += iB * (b2Vec2.CrossVV(this.m_rB, P) + impulse.z);
}
// data.positions[this.m_indexA].c = cA;
data.positions[this.m_indexA].a = aA;
// data.positions[this.m_indexB].c = cB;
data.positions[this.m_indexB].a = aB;
return positionError <= b2_linearSlop && angularError <= b2_angularSlop;
};
b2WeldJoint.prototype.GetAnchorA = function (out) {
return this.m_bodyA.GetWorldPoint(this.m_localAnchorA, out);
};
b2WeldJoint.prototype.GetAnchorB = function (out) {
return this.m_bodyB.GetWorldPoint(this.m_localAnchorB, out);
};
b2WeldJoint.prototype.GetReactionForce = function (inv_dt, out) {
// b2Vec2 P(this.m_impulse.x, this.m_impulse.y);
// return inv_dt * P;
out.x = inv_dt * this.m_impulse.x;
out.y = inv_dt * this.m_impulse.y;
return out;
};
b2WeldJoint.prototype.GetReactionTorque = function (inv_dt) {
return inv_dt * this.m_impulse.z;
};
b2WeldJoint.prototype.GetLocalAnchorA = function () { return this.m_localAnchorA; };
b2WeldJoint.prototype.GetLocalAnchorB = function () { return this.m_localAnchorB; };
b2WeldJoint.prototype.GetReferenceAngle = function () { return this.m_referenceAngle; };
b2WeldJoint.prototype.SetFrequency = function (hz) { this.m_frequencyHz = hz; };
b2WeldJoint.prototype.GetFrequency = function () { return this.m_frequencyHz; };
b2WeldJoint.prototype.SetDampingRatio = function (ratio) { this.m_dampingRatio = ratio; };
b2WeldJoint.prototype.GetDampingRatio = function () { return this.m_dampingRatio; };
b2WeldJoint.prototype.Dump = function (log) {
var indexA = this.m_bodyA.m_islandIndex;
var indexB = this.m_bodyB.m_islandIndex;
log(" const jd: b2WeldJointDef = new b2WeldJointDef();\n");
log(" jd.bodyA = bodies[%d];\n", indexA);
log(" jd.bodyB = bodies[%d];\n", indexB);
log(" jd.collideConnected = %s;\n", (this.m_collideConnected) ? ("true") : ("false"));
log(" jd.localAnchorA.Set(%.15f, %.15f);\n", this.m_localAnchorA.x, this.m_localAnchorA.y);
log(" jd.localAnchorB.Set(%.15f, %.15f);\n", this.m_localAnchorB.x, this.m_localAnchorB.y);
log(" jd.referenceAngle = %.15f;\n", this.m_referenceAngle);
log(" jd.frequencyHz = %.15f;\n", this.m_frequencyHz);
log(" jd.dampingRatio = %.15f;\n", this.m_dampingRatio);
log(" joints[%d] = this.m_world.CreateJoint(jd);\n", this.m_index);
};
b2WeldJoint.InitVelocityConstraints_s_P = new b2Vec2();
b2WeldJoint.SolveVelocityConstraints_s_Cdot1 = new b2Vec2();
b2WeldJoint.SolveVelocityConstraints_s_impulse1 = new b2Vec2();
b2WeldJoint.SolveVelocityConstraints_s_impulse = new b2Vec3();
b2WeldJoint.SolveVelocityConstraints_s_P = new b2Vec2();
b2WeldJoint.SolvePositionConstraints_s_C1 = new b2Vec2();
b2WeldJoint.SolvePositionConstraints_s_P = new b2Vec2();
b2WeldJoint.SolvePositionConstraints_s_impulse = new b2Vec3();
return b2WeldJoint;
}(b2Joint));
/*
* 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.
*/
/// Wheel joint definition. This requires defining a line of
/// motion using an axis and an anchor point. The definition uses local
/// anchor points and a local axis so that the initial configuration
/// can violate the constraint slightly. The joint translation is zero
/// when the local anchor points coincide in world space. Using local
/// anchors and a local axis helps when saving and loading a game.
var b2WheelJointDef = /** @class */ (function (_super) {
__extends(b2WheelJointDef, _super);
function b2WheelJointDef() {
var _this = _super.call(this, exports.b2JointType.e_wheelJoint) || this;
_this.localAnchorA = new b2Vec2(0, 0);
_this.localAnchorB = new b2Vec2(0, 0);
_this.localAxisA = new b2Vec2(1, 0);
_this.enableMotor = false;
_this.maxMotorTorque = 0;
_this.motorSpeed = 0;
_this.frequencyHz = 2;
_this.dampingRatio = 0.7;
return _this;
}
b2WheelJointDef.prototype.Initialize = function (bA, bB, anchor, axis) {
this.bodyA = bA;
this.bodyB = bB;
this.bodyA.GetLocalPoint(anchor, this.localAnchorA);
this.bodyB.GetLocalPoint(anchor, this.localAnchorB);
this.bodyA.GetLocalVector(axis, this.localAxisA);
};
return b2WheelJointDef;
}(b2JointDef));
var b2WheelJoint = /** @class */ (function (_super) {
__extends(b2WheelJoint, _super);
function b2WheelJoint(def) {
var _this = _super.call(this, def) || this;
_this.m_frequencyHz = 0;
_this.m_dampingRatio = 0;
// Solver shared
_this.m_localAnchorA = new b2Vec2();
_this.m_localAnchorB = new b2Vec2();
_this.m_localXAxisA = new b2Vec2();
_this.m_localYAxisA = new b2Vec2();
_this.m_impulse = 0;
_this.m_motorImpulse = 0;
_this.m_springImpulse = 0;
_this.m_maxMotorTorque = 0;
_this.m_motorSpeed = 0;
_this.m_enableMotor = false;
// Solver temp
_this.m_indexA = 0;
_this.m_indexB = 0;
_this.m_localCenterA = new b2Vec2();
_this.m_localCenterB = new b2Vec2();
_this.m_invMassA = 0;
_this.m_invMassB = 0;
_this.m_invIA = 0;
_this.m_invIB = 0;
_this.m_ax = new b2Vec2();
_this.m_ay = new b2Vec2();
_this.m_sAx = 0;
_this.m_sBx = 0;
_this.m_sAy = 0;
_this.m_sBy = 0;
_this.m_mass = 0;
_this.m_motorMass = 0;
_this.m_springMass = 0;
_this.m_bias = 0;
_this.m_gamma = 0;
_this.m_qA = new b2Rot();
_this.m_qB = new b2Rot();
_this.m_lalcA = new b2Vec2();
_this.m_lalcB = new b2Vec2();
_this.m_rA = new b2Vec2();
_this.m_rB = new b2Vec2();
_this.m_frequencyHz = b2Maybe(def.frequencyHz, 2);
_this.m_dampingRatio = b2Maybe(def.dampingRatio, 0.7);
_this.m_localAnchorA.Copy(b2Maybe(def.localAnchorA, b2Vec2.ZERO));
_this.m_localAnchorB.Copy(b2Maybe(def.localAnchorB, b2Vec2.ZERO));
_this.m_localXAxisA.Copy(b2Maybe(def.localAxisA, b2Vec2.UNITX));
b2Vec2.CrossOneV(_this.m_localXAxisA, _this.m_localYAxisA);
_this.m_maxMotorTorque = b2Maybe(def.maxMotorTorque, 0);
_this.m_motorSpeed = b2Maybe(def.motorSpeed, 0);
_this.m_enableMotor = b2Maybe(def.enableMotor, false);
_this.m_ax.SetZero();
_this.m_ay.SetZero();
return _this;
}
b2WheelJoint.prototype.GetMotorSpeed = function () {
return this.m_motorSpeed;
};
b2WheelJoint.prototype.GetMaxMotorTorque = function () {
return this.m_maxMotorTorque;
};
b2WheelJoint.prototype.SetSpringFrequencyHz = function (hz) {
this.m_frequencyHz = hz;
};
b2WheelJoint.prototype.GetSpringFrequencyHz = function () {
return this.m_frequencyHz;
};
b2WheelJoint.prototype.SetSpringDampingRatio = function (ratio) {
this.m_dampingRatio = ratio;
};
b2WheelJoint.prototype.GetSpringDampingRatio = function () {
return this.m_dampingRatio;
};
b2WheelJoint.prototype.InitVelocityConstraints = function (data) {
this.m_indexA = this.m_bodyA.m_islandIndex;
this.m_indexB = this.m_bodyB.m_islandIndex;
this.m_localCenterA.Copy(this.m_bodyA.m_sweep.localCenter);
this.m_localCenterB.Copy(this.m_bodyB.m_sweep.localCenter);
this.m_invMassA = this.m_bodyA.m_invMass;
this.m_invMassB = this.m_bodyB.m_invMass;
this.m_invIA = this.m_bodyA.m_invI;
this.m_invIB = this.m_bodyB.m_invI;
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// Compute the effective masses.
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// b2Vec2 d = cB + rB - cA - rA;
var d = b2Vec2.SubVV(b2Vec2.AddVV(cB, rB, b2Vec2.s_t0), b2Vec2.AddVV(cA, rA, b2Vec2.s_t1), b2WheelJoint.InitVelocityConstraints_s_d);
// Point to line constraint
{
// m_ay = b2Mul(qA, m_localYAxisA);
b2Rot.MulRV(qA, this.m_localYAxisA, this.m_ay);
// m_sAy = b2Cross(d + rA, m_ay);
this.m_sAy = b2Vec2.CrossVV(b2Vec2.AddVV(d, rA, b2Vec2.s_t0), this.m_ay);
// m_sBy = b2Cross(rB, m_ay);
this.m_sBy = b2Vec2.CrossVV(rB, this.m_ay);
this.m_mass = mA + mB + iA * this.m_sAy * this.m_sAy + iB * this.m_sBy * this.m_sBy;
if (this.m_mass > 0) {
this.m_mass = 1 / this.m_mass;
}
}
// Spring constraint
this.m_springMass = 0;
this.m_bias = 0;
this.m_gamma = 0;
if (this.m_frequencyHz > 0) {
// m_ax = b2Mul(qA, m_localXAxisA);
b2Rot.MulRV(qA, this.m_localXAxisA, this.m_ax);
// m_sAx = b2Cross(d + rA, m_ax);
this.m_sAx = b2Vec2.CrossVV(b2Vec2.AddVV(d, rA, b2Vec2.s_t0), this.m_ax);
// m_sBx = b2Cross(rB, m_ax);
this.m_sBx = b2Vec2.CrossVV(rB, this.m_ax);
var invMass = mA + mB + iA * this.m_sAx * this.m_sAx + iB * this.m_sBx * this.m_sBx;
if (invMass > 0) {
this.m_springMass = 1 / invMass;
var C = b2Vec2.DotVV(d, this.m_ax);
// Frequency
var omega = 2 * b2_pi * this.m_frequencyHz;
// Damping coefficient
var damp = 2 * this.m_springMass * this.m_dampingRatio * omega;
// Spring stiffness
var k = this.m_springMass * omega * omega;
// magic formulas
var h = data.step.dt;
this.m_gamma = h * (damp + h * k);
if (this.m_gamma > 0) {
this.m_gamma = 1 / this.m_gamma;
}
this.m_bias = C * h * k * this.m_gamma;
this.m_springMass = invMass + this.m_gamma;
if (this.m_springMass > 0) {
this.m_springMass = 1 / this.m_springMass;
}
}
}
else {
this.m_springImpulse = 0;
}
// Rotational motor
if (this.m_enableMotor) {
this.m_motorMass = iA + iB;
if (this.m_motorMass > 0) {
this.m_motorMass = 1 / this.m_motorMass;
}
}
else {
this.m_motorMass = 0;
this.m_motorImpulse = 0;
}
if (data.step.warmStarting) {
// Account for variable time step.
this.m_impulse *= data.step.dtRatio;
this.m_springImpulse *= data.step.dtRatio;
this.m_motorImpulse *= data.step.dtRatio;
// b2Vec2 P = m_impulse * m_ay + m_springImpulse * m_ax;
var P = b2Vec2.AddVV(b2Vec2.MulSV(this.m_impulse, this.m_ay, b2Vec2.s_t0), b2Vec2.MulSV(this.m_springImpulse, this.m_ax, b2Vec2.s_t1), b2WheelJoint.InitVelocityConstraints_s_P);
// float32 LA = m_impulse * m_sAy + m_springImpulse * m_sAx + m_motorImpulse;
var LA = this.m_impulse * this.m_sAy + this.m_springImpulse * this.m_sAx + this.m_motorImpulse;
// float32 LB = m_impulse * m_sBy + m_springImpulse * m_sBx + m_motorImpulse;
var LB = this.m_impulse * this.m_sBy + this.m_springImpulse * this.m_sBx + this.m_motorImpulse;
// vA -= m_invMassA * P;
vA.SelfMulSub(this.m_invMassA, P);
wA -= this.m_invIA * LA;
// vB += m_invMassB * P;
vB.SelfMulAdd(this.m_invMassB, P);
wB += this.m_invIB * LB;
}
else {
this.m_impulse = 0;
this.m_springImpulse = 0;
this.m_motorImpulse = 0;
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2WheelJoint.prototype.SolveVelocityConstraints = function (data) {
var mA = this.m_invMassA, mB = this.m_invMassB;
var iA = this.m_invIA, iB = this.m_invIB;
var vA = data.velocities[this.m_indexA].v;
var wA = data.velocities[this.m_indexA].w;
var vB = data.velocities[this.m_indexB].v;
var wB = data.velocities[this.m_indexB].w;
// Solve spring constraint
{
var Cdot = b2Vec2.DotVV(this.m_ax, b2Vec2.SubVV(vB, vA, b2Vec2.s_t0)) + this.m_sBx * wB - this.m_sAx * wA;
var impulse = -this.m_springMass * (Cdot + this.m_bias + this.m_gamma * this.m_springImpulse);
this.m_springImpulse += impulse;
// b2Vec2 P = impulse * m_ax;
var P = b2Vec2.MulSV(impulse, this.m_ax, b2WheelJoint.SolveVelocityConstraints_s_P);
var LA = impulse * this.m_sAx;
var LB = impulse * this.m_sBx;
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * LA;
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * LB;
}
// Solve rotational motor constraint
{
var Cdot = wB - wA - this.m_motorSpeed;
var impulse = -this.m_motorMass * Cdot;
var oldImpulse = this.m_motorImpulse;
var maxImpulse = data.step.dt * this.m_maxMotorTorque;
this.m_motorImpulse = b2Clamp(this.m_motorImpulse + impulse, -maxImpulse, maxImpulse);
impulse = this.m_motorImpulse - oldImpulse;
wA -= iA * impulse;
wB += iB * impulse;
}
// Solve point to line constraint
{
var Cdot = b2Vec2.DotVV(this.m_ay, b2Vec2.SubVV(vB, vA, b2Vec2.s_t0)) + this.m_sBy * wB - this.m_sAy * wA;
var impulse = -this.m_mass * Cdot;
this.m_impulse += impulse;
// b2Vec2 P = impulse * m_ay;
var P = b2Vec2.MulSV(impulse, this.m_ay, b2WheelJoint.SolveVelocityConstraints_s_P);
var LA = impulse * this.m_sAy;
var LB = impulse * this.m_sBy;
// vA -= mA * P;
vA.SelfMulSub(mA, P);
wA -= iA * LA;
// vB += mB * P;
vB.SelfMulAdd(mB, P);
wB += iB * LB;
}
// data.velocities[this.m_indexA].v = vA;
data.velocities[this.m_indexA].w = wA;
// data.velocities[this.m_indexB].v = vB;
data.velocities[this.m_indexB].w = wB;
};
b2WheelJoint.prototype.SolvePositionConstraints = function (data) {
var cA = data.positions[this.m_indexA].c;
var aA = data.positions[this.m_indexA].a;
var cB = data.positions[this.m_indexB].c;
var aB = data.positions[this.m_indexB].a;
var qA = this.m_qA.SetAngle(aA), qB = this.m_qB.SetAngle(aB);
// b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA);
b2Vec2.SubVV(this.m_localAnchorA, this.m_localCenterA, this.m_lalcA);
var rA = b2Rot.MulRV(qA, this.m_lalcA, this.m_rA);
// b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB);
b2Vec2.SubVV(this.m_localAnchorB, this.m_localCenterB, this.m_lalcB);
var rB = b2Rot.MulRV(qB, this.m_lalcB, this.m_rB);
// b2Vec2 d = (cB - cA) + rB - rA;
var d = b2Vec2.AddVV(b2Vec2.SubVV(cB, cA, b2Vec2.s_t0), b2Vec2.SubVV(rB, rA, b2Vec2.s_t1), b2WheelJoint.SolvePositionConstraints_s_d);
// b2Vec2 ay = b2Mul(qA, m_localYAxisA);
var ay = b2Rot.MulRV(qA, this.m_localYAxisA, this.m_ay);
// float32 sAy = b2Cross(d + rA, ay);
var sAy = b2Vec2.CrossVV(b2Vec2.AddVV(d, rA, b2Vec2.s_t0), ay);
// float32 sBy = b2Cross(rB, ay);
var sBy = b2Vec2.CrossVV(rB, ay);
// float32 C = b2Dot(d, ay);
var C = b2Vec2.DotVV(d, this.m_ay);
var k = this.m_invMassA + this.m_invMassB + this.m_invIA * this.m_sAy * this.m_sAy + this.m_invIB * this.m_sBy * this.m_sBy;
var impulse;
if (k !== 0) {
impulse = -C / k;
}
else {
impulse = 0;
}
// b2Vec2 P = impulse * ay;
var P = b2Vec2.MulSV(impulse, ay, b2WheelJoint.SolvePositionConstraints_s_P);
var LA = impulse * sAy;
var LB = impulse * sBy;
// cA -= m_invMassA * P;
cA.SelfMulSub(this.m_invMassA, P);
aA -= this.m_invIA * LA;
// cB += m_invMassB * P;
cB.SelfMulAdd(this.m_invMassB, P);
aB += this.m_invIB * LB;
// data.positions[this.m_indexA].c = cA;
data.positions[this.m_indexA].a = aA;
// data.positions[this.m_indexB].c = cB;
data.positions[this.m_indexB].a = aB;
return b2Abs(C) <= b2_linearSlop;
};
b2WheelJoint.prototype.GetDefinition = function (def) {
// DEBUG: b2Assert(false); // TODO
return def;
};
b2WheelJoint.prototype.GetAnchorA = function (out) {
return this.m_bodyA.GetWorldPoint(this.m_localAnchorA, out);
};
b2WheelJoint.prototype.GetAnchorB = function (out) {
return this.m_bodyB.GetWorldPoint(this.m_localAnchorB, out);
};
b2WheelJoint.prototype.GetReactionForce = function (inv_dt, out) {
// return inv_dt * (m_impulse * m_ay + m_springImpulse * m_ax);
out.x = inv_dt * (this.m_impulse * this.m_ay.x + this.m_springImpulse * this.m_ax.x);
out.y = inv_dt * (this.m_impulse * this.m_ay.y + this.m_springImpulse * this.m_ax.y);
return out;
};
b2WheelJoint.prototype.GetReactionTorque = function (inv_dt) {
return inv_dt * this.m_motorImpulse;
};
b2WheelJoint.prototype.GetLocalAnchorA = function () { return this.m_localAnchorA; };
b2WheelJoint.prototype.GetLocalAnchorB = function () { return this.m_localAnchorB; };
b2WheelJoint.prototype.GetLocalAxisA = function () { return this.m_localXAxisA; };
b2WheelJoint.prototype.GetJointTranslation = function () {
return this.GetPrismaticJointTranslation();
};
b2WheelJoint.prototype.GetJointLinearSpeed = function () {
return this.GetPrismaticJointSpeed();
};
b2WheelJoint.prototype.GetJointAngle = function () {
return this.GetRevoluteJointAngle();
};
b2WheelJoint.prototype.GetJointAngularSpeed = function () {
return this.GetRevoluteJointSpeed();
};
b2WheelJoint.prototype.GetPrismaticJointTranslation = function () {
var bA = this.m_bodyA;
var bB = this.m_bodyB;
var pA = bA.GetWorldPoint(this.m_localAnchorA, new b2Vec2());
var pB = bB.GetWorldPoint(this.m_localAnchorB, new b2Vec2());
var d = b2Vec2.SubVV(pB, pA, new b2Vec2());
var axis = bA.GetWorldVector(this.m_localXAxisA, new b2Vec2());
var translation = b2Vec2.DotVV(d, axis);
return translation;
};
b2WheelJoint.prototype.GetPrismaticJointSpeed = function () {
var bA = this.m_bodyA;
var bB = this.m_bodyB;
// b2Vec2 rA = b2Mul(bA->m_xf.q, m_localAnchorA - bA->m_sweep.localCenter);
b2Vec2.SubVV(this.m_localAnchorA, bA.m_sweep.localCenter, this.m_lalcA);
var rA = b2Rot.MulRV(bA.m_xf.q, this.m_lalcA, this.m_rA);
// b2Vec2 rB = b2Mul(bB->m_xf.q, m_localAnchorB - bB->m_sweep.localCenter);
b2Vec2.SubVV(this.m_localAnchorB, bB.m_sweep.localCenter, this.m_lalcB);
var rB = b2Rot.MulRV(bB.m_xf.q, this.m_lalcB, this.m_rB);
// b2Vec2 pA = bA->m_sweep.c + rA;
var pA = b2Vec2.AddVV(bA.m_sweep.c, rA, b2Vec2.s_t0); // pA uses s_t0
// b2Vec2 pB = bB->m_sweep.c + rB;
var pB = b2Vec2.AddVV(bB.m_sweep.c, rB, b2Vec2.s_t1); // pB uses s_t1
// b2Vec2 d = pB - pA;
var d = b2Vec2.SubVV(pB, pA, b2Vec2.s_t2); // d uses s_t2
// b2Vec2 axis = b2Mul(bA.m_xf.q, m_localXAxisA);
var axis = bA.GetWorldVector(this.m_localXAxisA, new b2Vec2());
var vA = bA.m_linearVelocity;
var vB = bB.m_linearVelocity;
var wA = bA.m_angularVelocity;
var wB = bB.m_angularVelocity;
// float32 speed = b2Dot(d, b2Cross(wA, axis)) + b2Dot(axis, vB + b2Cross(wB, rB) - vA - b2Cross(wA, rA));
var speed = b2Vec2.DotVV(d, b2Vec2.CrossSV(wA, axis, b2Vec2.s_t0)) +
b2Vec2.DotVV(axis, b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, rA, b2Vec2.s_t1), b2Vec2.s_t0));
return speed;
};
b2WheelJoint.prototype.GetRevoluteJointAngle = function () {
// b2Body* bA = this.m_bodyA;
// b2Body* bB = this.m_bodyB;
// return bB->this.m_sweep.a - bA->this.m_sweep.a;
return this.m_bodyB.m_sweep.a - this.m_bodyA.m_sweep.a;
};
b2WheelJoint.prototype.GetRevoluteJointSpeed = function () {
var wA = this.m_bodyA.m_angularVelocity;
var wB = this.m_bodyB.m_angularVelocity;
return wB - wA;
};
b2WheelJoint.prototype.IsMotorEnabled = function () {
return this.m_enableMotor;
};
b2WheelJoint.prototype.EnableMotor = function (flag) {
if (flag !== this.m_enableMotor) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_enableMotor = flag;
}
};
b2WheelJoint.prototype.SetMotorSpeed = function (speed) {
if (speed !== this.m_motorSpeed) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_motorSpeed = speed;
}
};
b2WheelJoint.prototype.SetMaxMotorTorque = function (force) {
if (force !== this.m_maxMotorTorque) {
this.m_bodyA.SetAwake(true);
this.m_bodyB.SetAwake(true);
this.m_maxMotorTorque = force;
}
};
b2WheelJoint.prototype.GetMotorTorque = function (inv_dt) {
return inv_dt * this.m_motorImpulse;
};
b2WheelJoint.prototype.Dump = function (log) {
var indexA = this.m_bodyA.m_islandIndex;
var indexB = this.m_bodyB.m_islandIndex;
log(" const jd: b2WheelJointDef = new b2WheelJointDef();\n");
log(" jd.bodyA = bodies[%d];\n", indexA);
log(" jd.bodyB = bodies[%d];\n", indexB);
log(" jd.collideConnected = %s;\n", (this.m_collideConnected) ? ("true") : ("false"));
log(" jd.localAnchorA.Set(%.15f, %.15f);\n", this.m_localAnchorA.x, this.m_localAnchorA.y);
log(" jd.localAnchorB.Set(%.15f, %.15f);\n", this.m_localAnchorB.x, this.m_localAnchorB.y);
log(" jd.localAxisA.Set(%.15f, %.15f);\n", this.m_localXAxisA.x, this.m_localXAxisA.y);
log(" jd.enableMotor = %s;\n", (this.m_enableMotor) ? ("true") : ("false"));
log(" jd.motorSpeed = %.15f;\n", this.m_motorSpeed);
log(" jd.maxMotorTorque = %.15f;\n", this.m_maxMotorTorque);
log(" jd.frequencyHz = %.15f;\n", this.m_frequencyHz);
log(" jd.dampingRatio = %.15f;\n", this.m_dampingRatio);
log(" joints[%d] = this.m_world.CreateJoint(jd);\n", this.m_index);
};
b2WheelJoint.InitVelocityConstraints_s_d = new b2Vec2();
b2WheelJoint.InitVelocityConstraints_s_P = new b2Vec2();
b2WheelJoint.SolveVelocityConstraints_s_P = new b2Vec2();
b2WheelJoint.SolvePositionConstraints_s_d = new b2Vec2();
b2WheelJoint.SolvePositionConstraints_s_P = new b2Vec2();
return b2WheelJoint;
}(b2Joint));
/*
* Copyright (c) 2006-2009 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.
*/
/// Friction mixing law. The idea is to allow either fixture to drive the friction to zero.
/// For example, anything slides on ice.
function b2MixFriction(friction1, friction2) {
return b2Sqrt(friction1 * friction2);
}
/// Restitution mixing law. The idea is allow for anything to bounce off an inelastic surface.
/// For example, a superball bounces on anything.
function b2MixRestitution(restitution1, restitution2) {
return restitution1 > restitution2 ? restitution1 : restitution2;
}
var b2ContactEdge = /** @class */ (function () {
function b2ContactEdge(contact) {
this.prev = null; ///< the previous contact edge in the body's contact list
this.next = null; ///< the next contact edge in the body's contact list
this.contact = contact;
}
return b2ContactEdge;
}());
var b2Contact = /** @class */ (function () {
function b2Contact() {
this.m_islandFlag = false; /// Used when crawling contact graph when forming islands.
this.m_touchingFlag = false; /// Set when the shapes are touching.
this.m_enabledFlag = false; /// This contact can be disabled (by user)
this.m_filterFlag = false; /// This contact needs filtering because a fixture filter was changed.
this.m_bulletHitFlag = false; /// This bullet contact had a TOI event
this.m_toiFlag = false; /// This contact has a valid TOI in m_toi
this.m_prev = null;
this.m_next = null;
this.m_indexA = 0;
this.m_indexB = 0;
this.m_manifold = new b2Manifold(); // TODO: readonly
this.m_toiCount = 0;
this.m_toi = 0;
this.m_friction = 0;
this.m_restitution = 0;
this.m_tangentSpeed = 0;
this.m_oldManifold = new b2Manifold(); // TODO: readonly
this.m_nodeA = new b2ContactEdge(this);
this.m_nodeB = new b2ContactEdge(this);
}
b2Contact.prototype.GetManifold = function () {
return this.m_manifold;
};
b2Contact.prototype.GetWorldManifold = function (worldManifold) {
var bodyA = this.m_fixtureA.GetBody();
var bodyB = this.m_fixtureB.GetBody();
var shapeA = this.m_fixtureA.GetShape();
var shapeB = this.m_fixtureB.GetShape();
worldManifold.Initialize(this.m_manifold, bodyA.GetTransform(), shapeA.m_radius, bodyB.GetTransform(), shapeB.m_radius);
};
b2Contact.prototype.IsTouching = function () {
return this.m_touchingFlag;
};
b2Contact.prototype.SetEnabled = function (flag) {
this.m_enabledFlag = flag;
};
b2Contact.prototype.IsEnabled = function () {
return this.m_enabledFlag;
};
b2Contact.prototype.GetNext = function () {
return this.m_next;
};
b2Contact.prototype.GetFixtureA = function () {
return this.m_fixtureA;
};
b2Contact.prototype.GetChildIndexA = function () {
return this.m_indexA;
};
b2Contact.prototype.GetFixtureB = function () {
return this.m_fixtureB;
};
b2Contact.prototype.GetChildIndexB = function () {
return this.m_indexB;
};
b2Contact.prototype.FlagForFiltering = function () {
this.m_filterFlag = true;
};
b2Contact.prototype.SetFriction = function (friction) {
this.m_friction = friction;
};
b2Contact.prototype.GetFriction = function () {
return this.m_friction;
};
b2Contact.prototype.ResetFriction = function () {
this.m_friction = b2MixFriction(this.m_fixtureA.m_friction, this.m_fixtureB.m_friction);
};
b2Contact.prototype.SetRestitution = function (restitution) {
this.m_restitution = restitution;
};
b2Contact.prototype.GetRestitution = function () {
return this.m_restitution;
};
b2Contact.prototype.ResetRestitution = function () {
this.m_restitution = b2MixRestitution(this.m_fixtureA.m_restitution, this.m_fixtureB.m_restitution);
};
b2Contact.prototype.SetTangentSpeed = function (speed) {
this.m_tangentSpeed = speed;
};
b2Contact.prototype.GetTangentSpeed = function () {
return this.m_tangentSpeed;
};
b2Contact.prototype.Reset = function (fixtureA, indexA, fixtureB, indexB) {
this.m_islandFlag = false;
this.m_touchingFlag = false;
this.m_enabledFlag = true;
this.m_filterFlag = false;
this.m_bulletHitFlag = false;
this.m_toiFlag = false;
this.m_fixtureA = fixtureA;
this.m_fixtureB = fixtureB;
this.m_indexA = indexA;
this.m_indexB = indexB;
this.m_manifold.pointCount = 0;
this.m_prev = null;
this.m_next = null;
delete this.m_nodeA.contact; // = null;
this.m_nodeA.prev = null;
this.m_nodeA.next = null;
delete this.m_nodeA.other; // = null;
delete this.m_nodeB.contact; // = null;
this.m_nodeB.prev = null;
this.m_nodeB.next = null;
delete this.m_nodeB.other; // = null;
this.m_toiCount = 0;
this.m_friction = b2MixFriction(this.m_fixtureA.m_friction, this.m_fixtureB.m_friction);
this.m_restitution = b2MixRestitution(this.m_fixtureA.m_restitution, this.m_fixtureB.m_restitution);
};
b2Contact.prototype.Update = function (listener) {
var tManifold = this.m_oldManifold;
this.m_oldManifold = this.m_manifold;
this.m_manifold = tManifold;
// Re-enable this contact.
this.m_enabledFlag = true;
var touching = false;
var wasTouching = this.m_touchingFlag;
var sensorA = this.m_fixtureA.IsSensor();
var sensorB = this.m_fixtureB.IsSensor();
var sensor = sensorA || sensorB;
var bodyA = this.m_fixtureA.GetBody();
var bodyB = this.m_fixtureB.GetBody();
var xfA = bodyA.GetTransform();
var xfB = bodyB.GetTransform();
///const aabbOverlap = b2TestOverlapAABB(this.m_fixtureA.GetAABB(0), this.m_fixtureB.GetAABB(0));
// Is this contact a sensor?
if (sensor) {
///if (aabbOverlap)
///{
var shapeA = this.m_fixtureA.GetShape();
var shapeB = this.m_fixtureB.GetShape();
touching = b2TestOverlapShape(shapeA, this.m_indexA, shapeB, this.m_indexB, xfA, xfB);
///}
// Sensors don't generate manifolds.
this.m_manifold.pointCount = 0;
}
else {
///if (aabbOverlap)
///{
this.Evaluate(this.m_manifold, xfA, xfB);
touching = this.m_manifold.pointCount > 0;
// Match old contact ids to new contact ids and copy the
// stored impulses to warm start the solver.
for (var i = 0; i < this.m_manifold.pointCount; ++i) {
var mp2 = this.m_manifold.points[i];
mp2.normalImpulse = 0;
mp2.tangentImpulse = 0;
var id2 = mp2.id;
for (var j = 0; j < this.m_oldManifold.pointCount; ++j) {
var mp1 = this.m_oldManifold.points[j];
if (mp1.id.key === id2.key) {
mp2.normalImpulse = mp1.normalImpulse;
mp2.tangentImpulse = mp1.tangentImpulse;
break;
}
}
}
///}
///else
///{
/// this.m_manifold.pointCount = 0;
///}
if (touching !== wasTouching) {
bodyA.SetAwake(true);
bodyB.SetAwake(true);
}
}
this.m_touchingFlag = touching;
if (!wasTouching && touching && listener) {
listener.BeginContact(this);
}
if (wasTouching && !touching && listener) {
listener.EndContact(this);
}
if (!sensor && touching && listener) {
listener.PreSolve(this, this.m_oldManifold);
}
};
b2Contact.prototype.ComputeTOI = function (sweepA, sweepB) {
var input = b2Contact.ComputeTOI_s_input;
input.proxyA.SetShape(this.m_fixtureA.GetShape(), this.m_indexA);
input.proxyB.SetShape(this.m_fixtureB.GetShape(), this.m_indexB);
input.sweepA.Copy(sweepA);
input.sweepB.Copy(sweepB);
input.tMax = b2_linearSlop;
var output = b2Contact.ComputeTOI_s_output;
b2TimeOfImpact(output, input);
return output.t;
};
b2Contact.ComputeTOI_s_input = new b2TOIInput();
b2Contact.ComputeTOI_s_output = new b2TOIOutput();
return b2Contact;
}());
/*
* Copyright (c) 2006-2009 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.
*/
var b2CircleContact = /** @class */ (function (_super) {
__extends(b2CircleContact, _super);
function b2CircleContact() {
return _super.call(this) || this;
}
b2CircleContact.Create = function (allocator) {
return new b2CircleContact();
};
b2CircleContact.Destroy = function (contact, allocator) {
};
b2CircleContact.prototype.Reset = function (fixtureA, indexA, fixtureB, indexB) {
_super.prototype.Reset.call(this, fixtureA, indexA, fixtureB, indexB);
};
b2CircleContact.prototype.Evaluate = function (manifold, xfA, xfB) {
var shapeA = this.m_fixtureA.GetShape();
var shapeB = this.m_fixtureB.GetShape();
// DEBUG: b2Assert(shapeA.GetType() === b2ShapeType.e_circleShape);
// DEBUG: b2Assert(shapeB.GetType() === b2ShapeType.e_circleShape);
b2CollideCircles(manifold, shapeA, xfA, shapeB, xfB);
};
return b2CircleContact;
}(b2Contact));
/*
* Copyright (c) 2006-2009 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.
*/
var b2PolygonContact = /** @class */ (function (_super) {
__extends(b2PolygonContact, _super);
function b2PolygonContact() {
return _super.call(this) || this;
}
b2PolygonContact.Create = function (allocator) {
return new b2PolygonContact();
};
b2PolygonContact.Destroy = function (contact, allocator) {
};
b2PolygonContact.prototype.Reset = function (fixtureA, indexA, fixtureB, indexB) {
_super.prototype.Reset.call(this, fixtureA, indexA, fixtureB, indexB);
};
b2PolygonContact.prototype.Evaluate = function (manifold, xfA, xfB) {
var shapeA = this.m_fixtureA.GetShape();
var shapeB = this.m_fixtureB.GetShape();
// DEBUG: b2Assert(shapeA.GetType() === b2ShapeType.e_polygonShape);
// DEBUG: b2Assert(shapeB.GetType() === b2ShapeType.e_polygonShape);
b2CollidePolygons(manifold, shapeA, xfA, shapeB, xfB);
};
return b2PolygonContact;
}(b2Contact));
/*
* Copyright (c) 2006-2009 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.
*/
var b2PolygonAndCircleContact = /** @class */ (function (_super) {
__extends(b2PolygonAndCircleContact, _super);
function b2PolygonAndCircleContact() {
return _super.call(this) || this;
}
b2PolygonAndCircleContact.Create = function (allocator) {
return new b2PolygonAndCircleContact();
};
b2PolygonAndCircleContact.Destroy = function (contact, allocator) {
};
b2PolygonAndCircleContact.prototype.Reset = function (fixtureA, indexA, fixtureB, indexB) {
_super.prototype.Reset.call(this, fixtureA, indexA, fixtureB, indexB);
// DEBUG: b2Assert(fixtureA.GetType() === b2ShapeType.e_polygonShape);
// DEBUG: b2Assert(fixtureB.GetType() === b2ShapeType.e_circleShape);
};
b2PolygonAndCircleContact.prototype.Evaluate = function (manifold, xfA, xfB) {
var shapeA = this.m_fixtureA.GetShape();
var shapeB = this.m_fixtureB.GetShape();
// DEBUG: b2Assert(shapeA instanceof b2PolygonShape);
// DEBUG: b2Assert(shapeB instanceof b2CircleShape);
b2CollidePolygonAndCircle(manifold, shapeA, xfA, shapeB, xfB);
};
return b2PolygonAndCircleContact;
}(b2Contact));
/*
* Copyright (c) 2006-2009 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.
*/
var b2EdgeAndCircleContact = /** @class */ (function (_super) {
__extends(b2EdgeAndCircleContact, _super);
function b2EdgeAndCircleContact() {
return _super.call(this) || this;
}
b2EdgeAndCircleContact.Create = function (allocator) {
return new b2EdgeAndCircleContact();
};
b2EdgeAndCircleContact.Destroy = function (contact, allocator) {
};
b2EdgeAndCircleContact.prototype.Reset = function (fixtureA, indexA, fixtureB, indexB) {
_super.prototype.Reset.call(this, fixtureA, indexA, fixtureB, indexB);
// DEBUG: b2Assert(fixtureA.GetType() === b2ShapeType.e_edgeShape);
// DEBUG: b2Assert(fixtureB.GetType() === b2ShapeType.e_circleShape);
};
b2EdgeAndCircleContact.prototype.Evaluate = function (manifold, xfA, xfB) {
var shapeA = this.m_fixtureA.GetShape();
var shapeB = this.m_fixtureB.GetShape();
// DEBUG: b2Assert(shapeA instanceof b2EdgeShape);
// DEBUG: b2Assert(shapeB instanceof b2CircleShape);
b2CollideEdgeAndCircle(manifold, shapeA, xfA, shapeB, xfB);
};
return b2EdgeAndCircleContact;
}(b2Contact));
/*
* Copyright (c) 2006-2009 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.
*/
var b2EdgeAndPolygonContact = /** @class */ (function (_super) {
__extends(b2EdgeAndPolygonContact, _super);
function b2EdgeAndPolygonContact() {
return _super.call(this) || this;
}
b2EdgeAndPolygonContact.Create = function (allocator) {
return new b2EdgeAndPolygonContact();
};
b2EdgeAndPolygonContact.Destroy = function (contact, allocator) {
};
b2EdgeAndPolygonContact.prototype.Reset = function (fixtureA, indexA, fixtureB, indexB) {
_super.prototype.Reset.call(this, fixtureA, indexA, fixtureB, indexB);
// DEBUG: b2Assert(fixtureA.GetType() === b2ShapeType.e_edgeShape);
// DEBUG: b2Assert(fixtureB.GetType() === b2ShapeType.e_polygonShape);
};
b2EdgeAndPolygonContact.prototype.Evaluate = function (manifold, xfA, xfB) {
var shapeA = this.m_fixtureA.GetShape();
var shapeB = this.m_fixtureB.GetShape();
// DEBUG: b2Assert(shapeA instanceof b2EdgeShape);
// DEBUG: b2Assert(shapeB instanceof b2PolygonShape);
b2CollideEdgeAndPolygon(manifold, shapeA, xfA, shapeB, xfB);
};
return b2EdgeAndPolygonContact;
}(b2Contact));
/*
* Copyright (c) 2006-2009 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.
*/
var b2ChainAndCircleContact = /** @class */ (function (_super) {
__extends(b2ChainAndCircleContact, _super);
function b2ChainAndCircleContact() {
return _super.call(this) || this;
}
b2ChainAndCircleContact.Create = function (allocator) {
return new b2ChainAndCircleContact();
};
b2ChainAndCircleContact.Destroy = function (contact, allocator) {
};
b2ChainAndCircleContact.prototype.Reset = function (fixtureA, indexA, fixtureB, indexB) {
_super.prototype.Reset.call(this, fixtureA, indexA, fixtureB, indexB);
// DEBUG: b2Assert(fixtureA.GetType() === b2ShapeType.e_chainShape);
// DEBUG: b2Assert(fixtureB.GetType() === b2ShapeType.e_circleShape);
};
b2ChainAndCircleContact.prototype.Evaluate = function (manifold, xfA, xfB) {
var shapeA = this.m_fixtureA.GetShape();
var shapeB = this.m_fixtureB.GetShape();
// DEBUG: b2Assert(shapeA instanceof b2ChainShape);
// DEBUG: b2Assert(shapeB instanceof b2CircleShape);
var chain = shapeA;
var edge = b2ChainAndCircleContact.Evaluate_s_edge;
chain.GetChildEdge(edge, this.m_indexA);
b2CollideEdgeAndCircle(manifold, edge, xfA, shapeB, xfB);
};
b2ChainAndCircleContact.Evaluate_s_edge = new b2EdgeShape();
return b2ChainAndCircleContact;
}(b2Contact));
/*
* Copyright (c) 2006-2009 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.
*/
var b2ChainAndPolygonContact = /** @class */ (function (_super) {
__extends(b2ChainAndPolygonContact, _super);
function b2ChainAndPolygonContact() {
return _super.call(this) || this;
}
b2ChainAndPolygonContact.Create = function (allocator) {
return new b2ChainAndPolygonContact();
};
b2ChainAndPolygonContact.Destroy = function (contact, allocator) {
};
b2ChainAndPolygonContact.prototype.Reset = function (fixtureA, indexA, fixtureB, indexB) {
_super.prototype.Reset.call(this, fixtureA, indexA, fixtureB, indexB);
// DEBUG: b2Assert(fixtureA.GetType() === b2ShapeType.e_chainShape);
// DEBUG: b2Assert(fixtureB.GetType() === b2ShapeType.e_polygonShape);
};
b2ChainAndPolygonContact.prototype.Evaluate = function (manifold, xfA, xfB) {
var shapeA = this.m_fixtureA.GetShape();
var shapeB = this.m_fixtureB.GetShape();
// DEBUG: b2Assert(shapeA instanceof b2ChainShape);
// DEBUG: b2Assert(shapeB instanceof b2PolygonShape);
var chain = shapeA;
var edge = b2ChainAndPolygonContact.Evaluate_s_edge;
chain.GetChildEdge(edge, this.m_indexA);
b2CollideEdgeAndPolygon(manifold, edge, xfA, shapeB, xfB);
};
b2ChainAndPolygonContact.Evaluate_s_edge = new b2EdgeShape();
return b2ChainAndPolygonContact;
}(b2Contact));
// DEBUG: import { b2Assert } from "../../Common/b2Settings";
var b2ContactRegister = /** @class */ (function () {
function b2ContactRegister() {
// public pool: b2Contact[];
this.createFcn = null;
this.destroyFcn = null;
this.primary = false;
}
return b2ContactRegister;
}());
var b2ContactFactory = /** @class */ (function () {
function b2ContactFactory(allocator) {
this.m_allocator = null;
this.m_allocator = allocator;
this.InitializeRegisters();
}
b2ContactFactory.prototype.AddType = function (createFcn, destroyFcn, type1, type2) {
var _this = this;
var pool = b2MakeArray(256, function (i) { return createFcn(_this.m_allocator); }); // TODO: b2Settings
function poolCreateFcn(allocator) {
// if (pool.length > 0) {
// return pool.pop();
// }
// return createFcn(allocator);
return pool.pop() || createFcn(allocator);
}
function poolDestroyFcn(contact, allocator) {
pool.push(contact);
}
// this.m_registers[type1][type2].pool = pool;
this.m_registers[type1][type2].createFcn = poolCreateFcn;
this.m_registers[type1][type2].destroyFcn = poolDestroyFcn;
this.m_registers[type1][type2].primary = true;
if (type1 !== type2) {
// this.m_registers[type2][type1].pool = pool;
this.m_registers[type2][type1].createFcn = poolCreateFcn;
this.m_registers[type2][type1].destroyFcn = poolDestroyFcn;
this.m_registers[type2][type1].primary = false;
}
/*
this.m_registers[type1][type2].createFcn = createFcn;
this.m_registers[type1][type2].destroyFcn = destroyFcn;
this.m_registers[type1][type2].primary = true;
if (type1 !== type2) {
this.m_registers[type2][type1].createFcn = createFcn;
this.m_registers[type2][type1].destroyFcn = destroyFcn;
this.m_registers[type2][type1].primary = false;
}
*/
};
b2ContactFactory.prototype.InitializeRegisters = function () {
this.m_registers = [ /*b2ShapeType.e_shapeTypeCount*/];
for (var i = 0; i < exports.b2ShapeType.e_shapeTypeCount; i++) {
this.m_registers[i] = [ /*b2ShapeType.e_shapeTypeCount*/];
for (var j = 0; j < exports.b2ShapeType.e_shapeTypeCount; j++) {
this.m_registers[i][j] = new b2ContactRegister();
}
}
this.AddType(b2CircleContact.Create, b2CircleContact.Destroy, exports.b2ShapeType.e_circleShape, exports.b2ShapeType.e_circleShape);
this.AddType(b2PolygonAndCircleContact.Create, b2PolygonAndCircleContact.Destroy, exports.b2ShapeType.e_polygonShape, exports.b2ShapeType.e_circleShape);
this.AddType(b2PolygonContact.Create, b2PolygonContact.Destroy, exports.b2ShapeType.e_polygonShape, exports.b2ShapeType.e_polygonShape);
this.AddType(b2EdgeAndCircleContact.Create, b2EdgeAndCircleContact.Destroy, exports.b2ShapeType.e_edgeShape, exports.b2ShapeType.e_circleShape);
this.AddType(b2EdgeAndPolygonContact.Create, b2EdgeAndPolygonContact.Destroy, exports.b2ShapeType.e_edgeShape, exports.b2ShapeType.e_polygonShape);
this.AddType(b2ChainAndCircleContact.Create, b2ChainAndCircleContact.Destroy, exports.b2ShapeType.e_chainShape, exports.b2ShapeType.e_circleShape);
this.AddType(b2ChainAndPolygonContact.Create, b2ChainAndPolygonContact.Destroy, exports.b2ShapeType.e_chainShape, exports.b2ShapeType.e_polygonShape);
};
b2ContactFactory.prototype.Create = function (fixtureA, indexA, fixtureB, indexB) {
var type1 = fixtureA.GetType();
var type2 = fixtureB.GetType();
// DEBUG: b2Assert(0 <= type1 && type1 < b2ShapeType.e_shapeTypeCount);
// DEBUG: b2Assert(0 <= type2 && type2 < b2ShapeType.e_shapeTypeCount);
var reg = this.m_registers[type1][type2];
if (reg.createFcn) {
var c = reg.createFcn(this.m_allocator);
if (reg.primary) {
c.Reset(fixtureA, indexA, fixtureB, indexB);
}
else {
c.Reset(fixtureB, indexB, fixtureA, indexA);
}
return c;
}
else {
return null;
}
};
b2ContactFactory.prototype.Destroy = function (contact) {
var fixtureA = contact.m_fixtureA;
var fixtureB = contact.m_fixtureB;
if (contact.m_manifold.pointCount > 0 &&
!fixtureA.IsSensor() &&
!fixtureB.IsSensor()) {
fixtureA.GetBody().SetAwake(true);
fixtureB.GetBody().SetAwake(true);
}
var typeA = fixtureA.GetType();
var typeB = fixtureB.GetType();
// DEBUG: b2Assert(0 <= typeA && typeB < b2ShapeType.e_shapeTypeCount);
// DEBUG: b2Assert(0 <= typeA && typeB < b2ShapeType.e_shapeTypeCount);
var reg = this.m_registers[typeA][typeB];
if (reg.destroyFcn) {
reg.destroyFcn(contact, this.m_allocator);
}
};
return b2ContactFactory;
}());
/*
* Copyright (c) 2006-2009 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.
*/
// #endif
/// Joints and fixtures are destroyed when their associated
/// body is destroyed. Implement this listener so that you
/// may nullify references to these joints and shapes.
var b2DestructionListener = /** @class */ (function () {
function b2DestructionListener() {
}
/// Called when any joint is about to be destroyed due
/// to the destruction of one of its attached bodies.
b2DestructionListener.prototype.SayGoodbyeJoint = function (joint) { };
/// Called when any fixture is about to be destroyed due
/// to the destruction of its parent body.
b2DestructionListener.prototype.SayGoodbyeFixture = function (fixture) { };
// #if B2_ENABLE_PARTICLE
/// Called when any particle group is about to be destroyed.
b2DestructionListener.prototype.SayGoodbyeParticleGroup = function (group) { };
/// Called when a particle is about to be destroyed.
/// The index can be used in conjunction with
/// b2ParticleSystem::GetUserDataBuffer() or
/// b2ParticleSystem::GetParticleHandleFromIndex() to determine which
/// particle has been destroyed.
b2DestructionListener.prototype.SayGoodbyeParticle = function (system, index) { };
return b2DestructionListener;
}());
/// Implement this class to provide collision filtering. In other words, you can implement
/// this class if you want finer control over contact creation.
var b2ContactFilter = /** @class */ (function () {
function b2ContactFilter() {
}
/// Return true if contact calculations should be performed between these two shapes.
/// @warning for performance reasons this is only called when the AABBs begin to overlap.
b2ContactFilter.prototype.ShouldCollide = function (fixtureA, fixtureB) {
var bodyA = fixtureA.GetBody();
var bodyB = fixtureB.GetBody();
// At least one body should be dynamic or kinematic.
if (bodyB.GetType() === exports.b2BodyType.b2_staticBody && bodyA.GetType() === exports.b2BodyType.b2_staticBody) {
return false;
}
// Does a joint prevent collision?
if (!bodyB.ShouldCollideConnected(bodyA)) {
return false;
}
var filter1 = fixtureA.GetFilterData();
var filter2 = fixtureB.GetFilterData();
if (filter1.groupIndex === filter2.groupIndex && filter1.groupIndex !== 0) {
return (filter1.groupIndex > 0);
}
var collide = (((filter1.maskBits & filter2.categoryBits) !== 0) && ((filter1.categoryBits & filter2.maskBits) !== 0));
return collide;
};
// #if B2_ENABLE_PARTICLE
b2ContactFilter.prototype.ShouldCollideFixtureParticle = function (fixture, system, index) {
return true;
};
b2ContactFilter.prototype.ShouldCollideParticleParticle = function (system, indexA, indexB) {
return true;
};
// #endif
b2ContactFilter.b2_defaultFilter = new b2ContactFilter();
return b2ContactFilter;
}());
/// Contact impulses for reporting. Impulses are used instead of forces because
/// sub-step forces may approach infinity for rigid body collisions. These
/// match up one-to-one with the contact points in b2Manifold.
var b2ContactImpulse = /** @class */ (function () {
function b2ContactImpulse() {
this.normalImpulses = b2MakeNumberArray(b2_maxManifoldPoints);
this.tangentImpulses = b2MakeNumberArray(b2_maxManifoldPoints);
this.count = 0;
}
return b2ContactImpulse;
}());
/// Implement this class to get contact information. You can use these results for
/// things like sounds and game logic. You can also get contact results by
/// traversing the contact lists after the time step. However, you might miss
/// some contacts because continuous physics leads to sub-stepping.
/// Additionally you may receive multiple callbacks for the same contact in a
/// single time step.
/// You should strive to make your callbacks efficient because there may be
/// many callbacks per time step.
/// @warning You cannot create/destroy Box2D entities inside these callbacks.
var b2ContactListener = /** @class */ (function () {
function b2ContactListener() {
}
/// Called when two fixtures begin to touch.
b2ContactListener.prototype.BeginContact = function (contact) { };
/// Called when two fixtures cease to touch.
b2ContactListener.prototype.EndContact = function (contact) { };
// #if B2_ENABLE_PARTICLE
b2ContactListener.prototype.BeginContactFixtureParticle = function (system, contact) { };
b2ContactListener.prototype.EndContactFixtureParticle = function (system, contact) { };
b2ContactListener.prototype.BeginContactParticleParticle = function (system, contact) { };
b2ContactListener.prototype.EndContactParticleParticle = function (system, contact) { };
// #endif
/// This is called after a contact is updated. This allows you to inspect a
/// contact before it goes to the solver. If you are careful, you can modify the
/// contact manifold (e.g. disable contact).
/// A copy of the old manifold is provided so that you can detect changes.
/// Note: this is called only for awake bodies.
/// Note: this is called even when the number of contact points is zero.
/// Note: this is not called for sensors.
/// Note: if you set the number of contact points to zero, you will not
/// get an EndContact callback. However, you may get a BeginContact callback
/// the next step.
b2ContactListener.prototype.PreSolve = function (contact, oldManifold) { };
/// This lets you inspect a contact after the solver is finished. This is useful
/// for inspecting impulses.
/// Note: the contact manifold does not include time of impact impulses, which can be
/// arbitrarily large if the sub-step is small. Hence the impulse is provided explicitly
/// in a separate data structure.
/// Note: this is only called for contacts that are touching, solid, and awake.
b2ContactListener.prototype.PostSolve = function (contact, impulse) { };
b2ContactListener.b2_defaultListener = new b2ContactListener();
return b2ContactListener;
}());
/// Callback class for AABB queries.
/// See b2World::Query
var b2QueryCallback = /** @class */ (function () {
function b2QueryCallback() {
}
/// Called for each fixture found in the query AABB.
/// @return false to terminate the query.
b2QueryCallback.prototype.ReportFixture = function (fixture) {
return true;
};
// #if B2_ENABLE_PARTICLE
b2QueryCallback.prototype.ReportParticle = function (system, index) {
return false;
};
b2QueryCallback.prototype.ShouldQueryParticleSystem = function (system) {
return true;
};
return b2QueryCallback;
}());
/// Callback class for ray casts.
/// See b2World::RayCast
var b2RayCastCallback = /** @class */ (function () {
function b2RayCastCallback() {
}
/// Called for each fixture found in the query. You control how the ray cast
/// proceeds by returning a float:
/// return -1: ignore this fixture and continue
/// return 0: terminate the ray cast
/// return fraction: clip the ray to this point
/// return 1: don't clip the ray and continue
/// @param fixture the fixture hit by the ray
/// @param point the point of initial intersection
/// @param normal the normal vector at the point of intersection
/// @return -1 to filter, 0 to terminate, fraction to clip the ray for
/// closest hit, 1 to continue
b2RayCastCallback.prototype.ReportFixture = function (fixture, point, normal, fraction) {
return fraction;
};
// #if B2_ENABLE_PARTICLE
b2RayCastCallback.prototype.ReportParticle = function (system, index, point, normal, fraction) {
return 0;
};
b2RayCastCallback.prototype.ShouldQueryParticleSystem = function (system) {
return true;
};
return b2RayCastCallback;
}());
/*
* Copyright (c) 2006-2009 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.
*/
// Delegate of b2World.
var b2ContactManager = /** @class */ (function () {
function b2ContactManager() {
this.m_broadPhase = new b2BroadPhase();
this.m_contactList = null;
this.m_contactCount = 0;
this.m_contactFilter = b2ContactFilter.b2_defaultFilter;
this.m_contactListener = b2ContactListener.b2_defaultListener;
this.m_allocator = null;
this.m_contactFactory = new b2ContactFactory(this.m_allocator);
}
// Broad-phase callback.
b2ContactManager.prototype.AddPair = function (proxyA, proxyB) {
// DEBUG: b2Assert(proxyA instanceof b2FixtureProxy);
// DEBUG: b2Assert(proxyB instanceof b2FixtureProxy);
var fixtureA = proxyA.fixture;
var fixtureB = proxyB.fixture;
var indexA = proxyA.childIndex;
var indexB = proxyB.childIndex;
var bodyA = fixtureA.GetBody();
var bodyB = fixtureB.GetBody();
// Are the fixtures on the same body?
if (bodyA === bodyB) {
return;
}
// TODO_ERIN use a hash table to remove a potential bottleneck when both
// bodies have a lot of contacts.
// Does a contact already exist?
var edge = bodyB.GetContactList();
while (edge) {
if (edge.other === bodyA) {
var fA = edge.contact.GetFixtureA();
var fB = edge.contact.GetFixtureB();
var iA = edge.contact.GetChildIndexA();
var iB = edge.contact.GetChildIndexB();
if (fA === fixtureA && fB === fixtureB && iA === indexA && iB === indexB) {
// A contact already exists.
return;
}
if (fA === fixtureB && fB === fixtureA && iA === indexB && iB === indexA) {
// A contact already exists.
return;
}
}
edge = edge.next;
}
// Check user filtering.
if (this.m_contactFilter && !this.m_contactFilter.ShouldCollide(fixtureA, fixtureB)) {
return;
}
// Call the factory.
var c = this.m_contactFactory.Create(fixtureA, indexA, fixtureB, indexB);
if (c === null) {
return;
}
// Contact creation may swap fixtures.
fixtureA = c.GetFixtureA();
fixtureB = c.GetFixtureB();
indexA = c.GetChildIndexA();
indexB = c.GetChildIndexB();
bodyA = fixtureA.m_body;
bodyB = fixtureB.m_body;
// Insert into the world.
c.m_prev = null;
c.m_next = this.m_contactList;
if (this.m_contactList !== null) {
this.m_contactList.m_prev = c;
}
this.m_contactList = c;
// Connect to island graph.
// Connect to body A
c.m_nodeA.contact = c;
c.m_nodeA.other = bodyB;
c.m_nodeA.prev = null;
c.m_nodeA.next = bodyA.m_contactList;
if (bodyA.m_contactList !== null) {
bodyA.m_contactList.prev = c.m_nodeA;
}
bodyA.m_contactList = c.m_nodeA;
// Connect to body B
c.m_nodeB.contact = c;
c.m_nodeB.other = bodyA;
c.m_nodeB.prev = null;
c.m_nodeB.next = bodyB.m_contactList;
if (bodyB.m_contactList !== null) {
bodyB.m_contactList.prev = c.m_nodeB;
}
bodyB.m_contactList = c.m_nodeB;
// Wake up the bodies
if (!fixtureA.IsSensor() && !fixtureB.IsSensor()) {
bodyA.SetAwake(true);
bodyB.SetAwake(true);
}
++this.m_contactCount;
};
b2ContactManager.prototype.FindNewContacts = function () {
var _this = this;
this.m_broadPhase.UpdatePairs(function (proxyA, proxyB) {
_this.AddPair(proxyA, proxyB);
});
};
b2ContactManager.prototype.Destroy = function (c) {
var fixtureA = c.GetFixtureA();
var fixtureB = c.GetFixtureB();
var bodyA = fixtureA.GetBody();
var bodyB = fixtureB.GetBody();
if (this.m_contactListener && c.IsTouching()) {
this.m_contactListener.EndContact(c);
}
// Remove from the world.
if (c.m_prev) {
c.m_prev.m_next = c.m_next;
}
if (c.m_next) {
c.m_next.m_prev = c.m_prev;
}
if (c === this.m_contactList) {
this.m_contactList = c.m_next;
}
// Remove from body 1
if (c.m_nodeA.prev) {
c.m_nodeA.prev.next = c.m_nodeA.next;
}
if (c.m_nodeA.next) {
c.m_nodeA.next.prev = c.m_nodeA.prev;
}
if (c.m_nodeA === bodyA.m_contactList) {
bodyA.m_contactList = c.m_nodeA.next;
}
// Remove from body 2
if (c.m_nodeB.prev) {
c.m_nodeB.prev.next = c.m_nodeB.next;
}
if (c.m_nodeB.next) {
c.m_nodeB.next.prev = c.m_nodeB.prev;
}
if (c.m_nodeB === bodyB.m_contactList) {
bodyB.m_contactList = c.m_nodeB.next;
}
// Call the factory.
this.m_contactFactory.Destroy(c);
--this.m_contactCount;
};
// This is the top level collision call for the time step. Here
// all the narrow phase collision is processed for the world
// contact list.
b2ContactManager.prototype.Collide = function () {
// Update awake contacts.
var c = this.m_contactList;
while (c) {
var fixtureA = c.GetFixtureA();
var fixtureB = c.GetFixtureB();
var indexA = c.GetChildIndexA();
var indexB = c.GetChildIndexB();
var bodyA = fixtureA.GetBody();
var bodyB = fixtureB.GetBody();
// Is this contact flagged for filtering?
if (c.m_filterFlag) {
// Check user filtering.
if (this.m_contactFilter && !this.m_contactFilter.ShouldCollide(fixtureA, fixtureB)) {
var cNuke = c;
c = cNuke.m_next;
this.Destroy(cNuke);
continue;
}
// Clear the filtering flag.
c.m_filterFlag = false;
}
var activeA = bodyA.IsAwake() && bodyA.m_type !== exports.b2BodyType.b2_staticBody;
var activeB = bodyB.IsAwake() && bodyB.m_type !== exports.b2BodyType.b2_staticBody;
// At least one body must be awake and it must be dynamic or kinematic.
if (!activeA && !activeB) {
c = c.m_next;
continue;
}
var proxyA = fixtureA.m_proxies[indexA].treeNode;
var proxyB = fixtureB.m_proxies[indexB].treeNode;
var overlap = b2TestOverlapAABB(proxyA.aabb, proxyB.aabb);
// Here we destroy contacts that cease to overlap in the broad-phase.
if (!overlap) {
var cNuke = c;
c = cNuke.m_next;
this.Destroy(cNuke);
continue;
}
// The contact persists.
c.Update(this.m_contactListener);
c = c.m_next;
}
};
return b2ContactManager;
}());
/*
* 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.
*/
/// Profiling data. Times are in milliseconds.
var b2Profile = /** @class */ (function () {
function b2Profile() {
this.step = 0;
this.collide = 0;
this.solve = 0;
this.solveInit = 0;
this.solveVelocity = 0;
this.solvePosition = 0;
this.broadphase = 0;
this.solveTOI = 0;
}
b2Profile.prototype.Reset = function () {
this.step = 0;
this.collide = 0;
this.solve = 0;
this.solveInit = 0;
this.solveVelocity = 0;
this.solvePosition = 0;
this.broadphase = 0;
this.solveTOI = 0;
return this;
};
return b2Profile;
}());
/// This is an internal structure.
var b2TimeStep = /** @class */ (function () {
function b2TimeStep() {
this.dt = 0; // time step
this.inv_dt = 0; // inverse time step (0 if dt == 0).
this.dtRatio = 0; // dt * inv_dt0
this.velocityIterations = 0;
this.positionIterations = 0;
// #if B2_ENABLE_PARTICLE
this.particleIterations = 0;
// #endif
this.warmStarting = false;
}
b2TimeStep.prototype.Copy = function (step) {
this.dt = step.dt;
this.inv_dt = step.inv_dt;
this.dtRatio = step.dtRatio;
this.positionIterations = step.positionIterations;
this.velocityIterations = step.velocityIterations;
// #if B2_ENABLE_PARTICLE
this.particleIterations = step.particleIterations;
// #endif
this.warmStarting = step.warmStarting;
return this;
};
return b2TimeStep;
}());
var b2Position = /** @class */ (function () {
function b2Position() {
this.c = new b2Vec2();
this.a = 0;
}
b2Position.MakeArray = function (length) {
return b2MakeArray(length, function (i) { return new b2Position(); });
};
return b2Position;
}());
var b2Velocity = /** @class */ (function () {
function b2Velocity() {
this.v = new b2Vec2();
this.w = 0;
}
b2Velocity.MakeArray = function (length) {
return b2MakeArray(length, function (i) { return new b2Velocity(); });
};
return b2Velocity;
}());
var b2SolverData = /** @class */ (function () {
function b2SolverData() {
this.step = new b2TimeStep();
}
return b2SolverData;
}());
/*
* Copyright (c) 2006-2009 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.
*/
// Solver debugging is normally disabled because the block solver sometimes has to deal with a poorly conditioned effective mass matrix.
// #define B2_DEBUG_SOLVER 0
var g_blockSolve = false;
var b2VelocityConstraintPoint = /** @class */ (function () {
function b2VelocityConstraintPoint() {
this.rA = new b2Vec2();
this.rB = new b2Vec2();
this.normalImpulse = 0;
this.tangentImpulse = 0;
this.normalMass = 0;
this.tangentMass = 0;
this.velocityBias = 0;
}
b2VelocityConstraintPoint.MakeArray = function (length) {
return b2MakeArray(length, function (i) { return new b2VelocityConstraintPoint(); });
};
return b2VelocityConstraintPoint;
}());
var b2ContactVelocityConstraint = /** @class */ (function () {
function b2ContactVelocityConstraint() {
this.points = b2VelocityConstraintPoint.MakeArray(b2_maxManifoldPoints);
this.normal = new b2Vec2();
this.tangent = new b2Vec2();
this.normalMass = new b2Mat22();
this.K = new b2Mat22();
this.indexA = 0;
this.indexB = 0;
this.invMassA = 0;
this.invMassB = 0;
this.invIA = 0;
this.invIB = 0;
this.friction = 0;
this.restitution = 0;
this.tangentSpeed = 0;
this.pointCount = 0;
this.contactIndex = 0;
}
b2ContactVelocityConstraint.MakeArray = function (length) {
return b2MakeArray(length, function (i) { return new b2ContactVelocityConstraint(); });
};
return b2ContactVelocityConstraint;
}());
var b2ContactPositionConstraint = /** @class */ (function () {
function b2ContactPositionConstraint() {
this.localPoints = b2Vec2.MakeArray(b2_maxManifoldPoints);
this.localNormal = new b2Vec2();
this.localPoint = new b2Vec2();
this.indexA = 0;
this.indexB = 0;
this.invMassA = 0;
this.invMassB = 0;
this.localCenterA = new b2Vec2();
this.localCenterB = new b2Vec2();
this.invIA = 0;
this.invIB = 0;
this.type = exports.b2ManifoldType.e_unknown;
this.radiusA = 0;
this.radiusB = 0;
this.pointCount = 0;
}
b2ContactPositionConstraint.MakeArray = function (length) {
return b2MakeArray(length, function (i) { return new b2ContactPositionConstraint(); });
};
return b2ContactPositionConstraint;
}());
var b2ContactSolverDef = /** @class */ (function () {
function b2ContactSolverDef() {
this.step = new b2TimeStep();
this.count = 0;
this.allocator = null;
}
return b2ContactSolverDef;
}());
var b2PositionSolverManifold = /** @class */ (function () {
function b2PositionSolverManifold() {
this.normal = new b2Vec2();
this.point = new b2Vec2();
this.separation = 0;
}
b2PositionSolverManifold.prototype.Initialize = function (pc, xfA, xfB, index) {
var pointA = b2PositionSolverManifold.Initialize_s_pointA;
var pointB = b2PositionSolverManifold.Initialize_s_pointB;
var planePoint = b2PositionSolverManifold.Initialize_s_planePoint;
var clipPoint = b2PositionSolverManifold.Initialize_s_clipPoint;
// DEBUG: b2Assert(pc.pointCount > 0);
switch (pc.type) {
case exports.b2ManifoldType.e_circles: {
// b2Vec2 pointA = b2Mul(xfA, pc->localPoint);
b2Transform.MulXV(xfA, pc.localPoint, pointA);
// b2Vec2 pointB = b2Mul(xfB, pc->localPoints[0]);
b2Transform.MulXV(xfB, pc.localPoints[0], pointB);
// normal = pointB - pointA;
// normal.Normalize();
b2Vec2.SubVV(pointB, pointA, this.normal).SelfNormalize();
// point = 0.5f * (pointA + pointB);
b2Vec2.MidVV(pointA, pointB, this.point);
// separation = b2Dot(pointB - pointA, normal) - pc->radius;
this.separation = b2Vec2.DotVV(b2Vec2.SubVV(pointB, pointA, b2Vec2.s_t0), this.normal) - pc.radiusA - pc.radiusB;
break;
}
case exports.b2ManifoldType.e_faceA: {
// normal = b2Mul(xfA.q, pc->localNormal);
b2Rot.MulRV(xfA.q, pc.localNormal, this.normal);
// b2Vec2 planePoint = b2Mul(xfA, pc->localPoint);
b2Transform.MulXV(xfA, pc.localPoint, planePoint);
// b2Vec2 clipPoint = b2Mul(xfB, pc->localPoints[index]);
b2Transform.MulXV(xfB, pc.localPoints[index], clipPoint);
// separation = b2Dot(clipPoint - planePoint, normal) - pc->radius;
this.separation = b2Vec2.DotVV(b2Vec2.SubVV(clipPoint, planePoint, b2Vec2.s_t0), this.normal) - pc.radiusA - pc.radiusB;
// point = clipPoint;
this.point.Copy(clipPoint);
break;
}
case exports.b2ManifoldType.e_faceB: {
// normal = b2Mul(xfB.q, pc->localNormal);
b2Rot.MulRV(xfB.q, pc.localNormal, this.normal);
// b2Vec2 planePoint = b2Mul(xfB, pc->localPoint);
b2Transform.MulXV(xfB, pc.localPoint, planePoint);
// b2Vec2 clipPoint = b2Mul(xfA, pc->localPoints[index]);
b2Transform.MulXV(xfA, pc.localPoints[index], clipPoint);
// separation = b2Dot(clipPoint - planePoint, normal) - pc->radius;
this.separation = b2Vec2.DotVV(b2Vec2.SubVV(clipPoint, planePoint, b2Vec2.s_t0), this.normal) - pc.radiusA - pc.radiusB;
// point = clipPoint;
this.point.Copy(clipPoint);
// Ensure normal points from A to B
// normal = -normal;
this.normal.SelfNeg();
break;
}
}
};
b2PositionSolverManifold.Initialize_s_pointA = new b2Vec2();
b2PositionSolverManifold.Initialize_s_pointB = new b2Vec2();
b2PositionSolverManifold.Initialize_s_planePoint = new b2Vec2();
b2PositionSolverManifold.Initialize_s_clipPoint = new b2Vec2();
return b2PositionSolverManifold;
}());
var b2ContactSolver = /** @class */ (function () {
function b2ContactSolver() {
this.m_step = new b2TimeStep();
this.m_allocator = null;
this.m_positionConstraints = b2ContactPositionConstraint.MakeArray(1024); // TODO: b2Settings
this.m_velocityConstraints = b2ContactVelocityConstraint.MakeArray(1024); // TODO: b2Settings
this.m_count = 0;
}
b2ContactSolver.prototype.Initialize = function (def) {
this.m_step.Copy(def.step);
this.m_allocator = def.allocator;
this.m_count = def.count;
// TODO:
if (this.m_positionConstraints.length < this.m_count) {
var new_length = b2Max(this.m_positionConstraints.length * 2, this.m_count);
while (this.m_positionConstraints.length < new_length) {
this.m_positionConstraints[this.m_positionConstraints.length] = new b2ContactPositionConstraint();
}
}
// TODO:
if (this.m_velocityConstraints.length < this.m_count) {
var new_length = b2Max(this.m_velocityConstraints.length * 2, this.m_count);
while (this.m_velocityConstraints.length < new_length) {
this.m_velocityConstraints[this.m_velocityConstraints.length] = new b2ContactVelocityConstraint();
}
}
this.m_positions = def.positions;
this.m_velocities = def.velocities;
this.m_contacts = def.contacts;
// Initialize position independent portions of the constraints.
for (var i = 0; i < this.m_count; ++i) {
var contact = this.m_contacts[i];
var fixtureA = contact.m_fixtureA;
var fixtureB = contact.m_fixtureB;
var shapeA = fixtureA.GetShape();
var shapeB = fixtureB.GetShape();
var radiusA = shapeA.m_radius;
var radiusB = shapeB.m_radius;
var bodyA = fixtureA.GetBody();
var bodyB = fixtureB.GetBody();
var manifold = contact.GetManifold();
var pointCount = manifold.pointCount;
// DEBUG: b2Assert(pointCount > 0);
var vc = this.m_velocityConstraints[i];
vc.friction = contact.m_friction;
vc.restitution = contact.m_restitution;
vc.tangentSpeed = contact.m_tangentSpeed;
vc.indexA = bodyA.m_islandIndex;
vc.indexB = bodyB.m_islandIndex;
vc.invMassA = bodyA.m_invMass;
vc.invMassB = bodyB.m_invMass;
vc.invIA = bodyA.m_invI;
vc.invIB = bodyB.m_invI;
vc.contactIndex = i;
vc.pointCount = pointCount;
vc.K.SetZero();
vc.normalMass.SetZero();
var pc = this.m_positionConstraints[i];
pc.indexA = bodyA.m_islandIndex;
pc.indexB = bodyB.m_islandIndex;
pc.invMassA = bodyA.m_invMass;
pc.invMassB = bodyB.m_invMass;
pc.localCenterA.Copy(bodyA.m_sweep.localCenter);
pc.localCenterB.Copy(bodyB.m_sweep.localCenter);
pc.invIA = bodyA.m_invI;
pc.invIB = bodyB.m_invI;
pc.localNormal.Copy(manifold.localNormal);
pc.localPoint.Copy(manifold.localPoint);
pc.pointCount = pointCount;
pc.radiusA = radiusA;
pc.radiusB = radiusB;
pc.type = manifold.type;
for (var j = 0; j < pointCount; ++j) {
var cp = manifold.points[j];
var vcp = vc.points[j];
if (this.m_step.warmStarting) {
vcp.normalImpulse = this.m_step.dtRatio * cp.normalImpulse;
vcp.tangentImpulse = this.m_step.dtRatio * cp.tangentImpulse;
}
else {
vcp.normalImpulse = 0;
vcp.tangentImpulse = 0;
}
vcp.rA.SetZero();
vcp.rB.SetZero();
vcp.normalMass = 0;
vcp.tangentMass = 0;
vcp.velocityBias = 0;
pc.localPoints[j].Copy(cp.localPoint);
}
}
return this;
};
b2ContactSolver.prototype.InitializeVelocityConstraints = function () {
var xfA = b2ContactSolver.InitializeVelocityConstraints_s_xfA;
var xfB = b2ContactSolver.InitializeVelocityConstraints_s_xfB;
var worldManifold = b2ContactSolver.InitializeVelocityConstraints_s_worldManifold;
var k_maxConditionNumber = 1000;
for (var i = 0; i < this.m_count; ++i) {
var vc = this.m_velocityConstraints[i];
var pc = this.m_positionConstraints[i];
var radiusA = pc.radiusA;
var radiusB = pc.radiusB;
var manifold = this.m_contacts[vc.contactIndex].GetManifold();
var indexA = vc.indexA;
var indexB = vc.indexB;
var mA = vc.invMassA;
var mB = vc.invMassB;
var iA = vc.invIA;
var iB = vc.invIB;
var localCenterA = pc.localCenterA;
var localCenterB = pc.localCenterB;
var cA = this.m_positions[indexA].c;
var aA = this.m_positions[indexA].a;
var vA = this.m_velocities[indexA].v;
var wA = this.m_velocities[indexA].w;
var cB = this.m_positions[indexB].c;
var aB = this.m_positions[indexB].a;
var vB = this.m_velocities[indexB].v;
var wB = this.m_velocities[indexB].w;
// DEBUG: b2Assert(manifold.pointCount > 0);
xfA.q.SetAngle(aA);
xfB.q.SetAngle(aB);
b2Vec2.SubVV(cA, b2Rot.MulRV(xfA.q, localCenterA, b2Vec2.s_t0), xfA.p);
b2Vec2.SubVV(cB, b2Rot.MulRV(xfB.q, localCenterB, b2Vec2.s_t0), xfB.p);
worldManifold.Initialize(manifold, xfA, radiusA, xfB, radiusB);
vc.normal.Copy(worldManifold.normal);
b2Vec2.CrossVOne(vc.normal, vc.tangent); // compute from normal
var pointCount = vc.pointCount;
for (var j = 0; j < pointCount; ++j) {
var vcp = vc.points[j];
// vcp->rA = worldManifold.points[j] - cA;
b2Vec2.SubVV(worldManifold.points[j], cA, vcp.rA);
// vcp->rB = worldManifold.points[j] - cB;
b2Vec2.SubVV(worldManifold.points[j], cB, vcp.rB);
var rnA = b2Vec2.CrossVV(vcp.rA, vc.normal);
var rnB = b2Vec2.CrossVV(vcp.rB, vc.normal);
var kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
vcp.normalMass = kNormal > 0 ? 1 / kNormal : 0;
// b2Vec2 tangent = b2Cross(vc->normal, 1.0f);
var tangent = vc.tangent; // precomputed from normal
var rtA = b2Vec2.CrossVV(vcp.rA, tangent);
var rtB = b2Vec2.CrossVV(vcp.rB, tangent);
var kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB;
vcp.tangentMass = kTangent > 0 ? 1 / kTangent : 0;
// Setup a velocity bias for restitution.
vcp.velocityBias = 0;
// float32 vRel = b2Dot(vc->normal, vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA));
var vRel = b2Vec2.DotVV(vc.normal, b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, vcp.rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, vcp.rA, b2Vec2.s_t1), b2Vec2.s_t0));
if (vRel < (-b2_velocityThreshold)) {
vcp.velocityBias += (-vc.restitution * vRel);
}
}
// If we have two points, then prepare the block solver.
if (vc.pointCount === 2 && g_blockSolve) {
var vcp1 = vc.points[0];
var vcp2 = vc.points[1];
var rn1A = b2Vec2.CrossVV(vcp1.rA, vc.normal);
var rn1B = b2Vec2.CrossVV(vcp1.rB, vc.normal);
var rn2A = b2Vec2.CrossVV(vcp2.rA, vc.normal);
var rn2B = b2Vec2.CrossVV(vcp2.rB, vc.normal);
var k11 = mA + mB + iA * rn1A * rn1A + iB * rn1B * rn1B;
var k22 = mA + mB + iA * rn2A * rn2A + iB * rn2B * rn2B;
var k12 = mA + mB + iA * rn1A * rn2A + iB * rn1B * rn2B;
// Ensure a reasonable condition number.
// float32 k_maxConditionNumber = 1000.0f;
if (k11 * k11 < k_maxConditionNumber * (k11 * k22 - k12 * k12)) {
// K is safe to invert.
vc.K.ex.Set(k11, k12);
vc.K.ey.Set(k12, k22);
vc.K.GetInverse(vc.normalMass);
}
else {
// The constraints are redundant, just use one.
// TODO_ERIN use deepest?
vc.pointCount = 1;
}
}
}
};
b2ContactSolver.prototype.WarmStart = function () {
var P = b2ContactSolver.WarmStart_s_P;
// Warm start.
for (var i = 0; i < this.m_count; ++i) {
var vc = this.m_velocityConstraints[i];
var indexA = vc.indexA;
var indexB = vc.indexB;
var mA = vc.invMassA;
var iA = vc.invIA;
var mB = vc.invMassB;
var iB = vc.invIB;
var pointCount = vc.pointCount;
var vA = this.m_velocities[indexA].v;
var wA = this.m_velocities[indexA].w;
var vB = this.m_velocities[indexB].v;
var wB = this.m_velocities[indexB].w;
var normal = vc.normal;
// b2Vec2 tangent = b2Cross(normal, 1.0f);
var tangent = vc.tangent; // precomputed from normal
for (var j = 0; j < pointCount; ++j) {
var vcp = vc.points[j];
// b2Vec2 P = vcp->normalImpulse * normal + vcp->tangentImpulse * tangent;
b2Vec2.AddVV(b2Vec2.MulSV(vcp.normalImpulse, normal, b2Vec2.s_t0), b2Vec2.MulSV(vcp.tangentImpulse, tangent, b2Vec2.s_t1), P);
// wA -= iA * b2Cross(vcp->rA, P);
wA -= iA * b2Vec2.CrossVV(vcp.rA, P);
// vA -= mA * P;
vA.SelfMulSub(mA, P);
// wB += iB * b2Cross(vcp->rB, P);
wB += iB * b2Vec2.CrossVV(vcp.rB, P);
// vB += mB * P;
vB.SelfMulAdd(mB, P);
}
// this.m_velocities[indexA].v = vA;
this.m_velocities[indexA].w = wA;
// this.m_velocities[indexB].v = vB;
this.m_velocities[indexB].w = wB;
}
};
b2ContactSolver.prototype.SolveVelocityConstraints = function () {
var dv = b2ContactSolver.SolveVelocityConstraints_s_dv;
var dv1 = b2ContactSolver.SolveVelocityConstraints_s_dv1;
var dv2 = b2ContactSolver.SolveVelocityConstraints_s_dv2;
var P = b2ContactSolver.SolveVelocityConstraints_s_P;
var a = b2ContactSolver.SolveVelocityConstraints_s_a;
var b = b2ContactSolver.SolveVelocityConstraints_s_b;
var x = b2ContactSolver.SolveVelocityConstraints_s_x;
var d = b2ContactSolver.SolveVelocityConstraints_s_d;
var P1 = b2ContactSolver.SolveVelocityConstraints_s_P1;
var P2 = b2ContactSolver.SolveVelocityConstraints_s_P2;
var P1P2 = b2ContactSolver.SolveVelocityConstraints_s_P1P2;
for (var i = 0; i < this.m_count; ++i) {
var vc = this.m_velocityConstraints[i];
var indexA = vc.indexA;
var indexB = vc.indexB;
var mA = vc.invMassA;
var iA = vc.invIA;
var mB = vc.invMassB;
var iB = vc.invIB;
var pointCount = vc.pointCount;
var vA = this.m_velocities[indexA].v;
var wA = this.m_velocities[indexA].w;
var vB = this.m_velocities[indexB].v;
var wB = this.m_velocities[indexB].w;
// b2Vec2 normal = vc->normal;
var normal = vc.normal;
// b2Vec2 tangent = b2Cross(normal, 1.0f);
var tangent = vc.tangent; // precomputed from normal
var friction = vc.friction;
// DEBUG: b2Assert(pointCount === 1 || pointCount === 2);
// Solve tangent constraints first because non-penetration is more important
// than friction.
for (var j = 0; j < pointCount; ++j) {
var vcp = vc.points[j];
// Relative velocity at contact
// b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA);
b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, vcp.rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, vcp.rA, b2Vec2.s_t1), dv);
// Compute tangent force
// float32 vt = b2Dot(dv, tangent) - vc->tangentSpeed;
var vt = b2Vec2.DotVV(dv, tangent) - vc.tangentSpeed;
var lambda = vcp.tangentMass * (-vt);
// b2Clamp the accumulated force
var maxFriction = friction * vcp.normalImpulse;
var newImpulse = b2Clamp(vcp.tangentImpulse + lambda, (-maxFriction), maxFriction);
lambda = newImpulse - vcp.tangentImpulse;
vcp.tangentImpulse = newImpulse;
// Apply contact impulse
// b2Vec2 P = lambda * tangent;
b2Vec2.MulSV(lambda, tangent, P);
// vA -= mA * P;
vA.SelfMulSub(mA, P);
// wA -= iA * b2Cross(vcp->rA, P);
wA -= iA * b2Vec2.CrossVV(vcp.rA, P);
// vB += mB * P;
vB.SelfMulAdd(mB, P);
// wB += iB * b2Cross(vcp->rB, P);
wB += iB * b2Vec2.CrossVV(vcp.rB, P);
}
// Solve normal constraints
if (vc.pointCount === 1 || g_blockSolve === false) {
for (var j = 0; j < pointCount; ++j) {
var vcp = vc.points[j];
// Relative velocity at contact
// b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA);
b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, vcp.rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, vcp.rA, b2Vec2.s_t1), dv);
// Compute normal impulse
// float32 vn = b2Dot(dv, normal);
var vn = b2Vec2.DotVV(dv, normal);
var lambda = (-vcp.normalMass * (vn - vcp.velocityBias));
// b2Clamp the accumulated impulse
// float32 newImpulse = b2Max(vcp->normalImpulse + lambda, 0.0f);
var newImpulse = b2Max(vcp.normalImpulse + lambda, 0);
lambda = newImpulse - vcp.normalImpulse;
vcp.normalImpulse = newImpulse;
// Apply contact impulse
// b2Vec2 P = lambda * normal;
b2Vec2.MulSV(lambda, normal, P);
// vA -= mA * P;
vA.SelfMulSub(mA, P);
// wA -= iA * b2Cross(vcp->rA, P);
wA -= iA * b2Vec2.CrossVV(vcp.rA, P);
// vB += mB * P;
vB.SelfMulAdd(mB, P);
// wB += iB * b2Cross(vcp->rB, P);
wB += iB * b2Vec2.CrossVV(vcp.rB, P);
}
}
else {
// Block solver developed in collaboration with Dirk Gregorius (back in 01/07 on Box2D_Lite).
// Build the mini LCP for this contact patch
//
// vn = A * x + b, vn >= 0, x >= 0 and vn_i * x_i = 0 with i = 1..2
//
// A = J * W * JT and J = ( -n, -r1 x n, n, r2 x n )
// b = vn0 - velocityBias
//
// The system is solved using the "Total enumeration method" (s. Murty). The complementary constraint vn_i * x_i
// implies that we must have in any solution either vn_i = 0 or x_i = 0. So for the 2D contact problem the cases
// vn1 = 0 and vn2 = 0, x1 = 0 and x2 = 0, x1 = 0 and vn2 = 0, x2 = 0 and vn1 = 0 need to be tested. The first valid
// solution that satisfies the problem is chosen.
//
// In order to account of the accumulated impulse 'a' (because of the iterative nature of the solver which only requires
// that the accumulated impulse is clamped and not the incremental impulse) we change the impulse variable (x_i).
//
// Substitute:
//
// x = a + d
//
// a := old total impulse
// x := new total impulse
// d := incremental impulse
//
// For the current iteration we extend the formula for the incremental impulse
// to compute the new total impulse:
//
// vn = A * d + b
// = A * (x - a) + b
// = A * x + b - A * a
// = A * x + b'
// b' = b - A * a;
var cp1 = vc.points[0];
var cp2 = vc.points[1];
// b2Vec2 a(cp1->normalImpulse, cp2->normalImpulse);
a.Set(cp1.normalImpulse, cp2.normalImpulse);
// DEBUG: b2Assert(a.x >= 0 && a.y >= 0);
// Relative velocity at contact
// b2Vec2 dv1 = vB + b2Cross(wB, cp1->rB) - vA - b2Cross(wA, cp1->rA);
b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, cp1.rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, cp1.rA, b2Vec2.s_t1), dv1);
// b2Vec2 dv2 = vB + b2Cross(wB, cp2->rB) - vA - b2Cross(wA, cp2->rA);
b2Vec2.SubVV(b2Vec2.AddVCrossSV(vB, wB, cp2.rB, b2Vec2.s_t0), b2Vec2.AddVCrossSV(vA, wA, cp2.rA, b2Vec2.s_t1), dv2);
// Compute normal velocity
// float32 vn1 = b2Dot(dv1, normal);
var vn1 = b2Vec2.DotVV(dv1, normal);
// float32 vn2 = b2Dot(dv2, normal);
var vn2 = b2Vec2.DotVV(dv2, normal);
// b2Vec2 b;
b.x = vn1 - cp1.velocityBias;
b.y = vn2 - cp2.velocityBias;
// Compute b'
// b -= b2Mul(vc->K, a);
b.SelfSub(b2Mat22.MulMV(vc.K, a, b2Vec2.s_t0));
/*
#if B2_DEBUG_SOLVER === 1
const k_errorTol: number = 0.001;
#endif
*/
for (;;) {
//
// Case 1: vn = 0
//
// 0 = A * x + b'
//
// Solve for x:
//
// x = - inv(A) * b'
//
// b2Vec2 x = - b2Mul(vc->normalMass, b);
b2Mat22.MulMV(vc.normalMass, b, x).SelfNeg();
if (x.x >= 0 && x.y >= 0) {
// Get the incremental impulse
// b2Vec2 d = x - a;
b2Vec2.SubVV(x, a, d);
// Apply incremental impulse
// b2Vec2 P1 = d.x * normal;
b2Vec2.MulSV(d.x, normal, P1);
// b2Vec2 P2 = d.y * normal;
b2Vec2.MulSV(d.y, normal, P2);
b2Vec2.AddVV(P1, P2, P1P2);
// vA -= mA * (P1 + P2);
vA.SelfMulSub(mA, P1P2);
// wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2));
wA -= iA * (b2Vec2.CrossVV(cp1.rA, P1) + b2Vec2.CrossVV(cp2.rA, P2));
// vB += mB * (P1 + P2);
vB.SelfMulAdd(mB, P1P2);
// wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2));
wB += iB * (b2Vec2.CrossVV(cp1.rB, P1) + b2Vec2.CrossVV(cp2.rB, P2));
// Accumulate
cp1.normalImpulse = x.x;
cp2.normalImpulse = x.y;
/*
#if B2_DEBUG_SOLVER === 1
// Postconditions
dv1 = vB + b2Cross(wB, cp1->rB) - vA - b2Cross(wA, cp1->rA);
dv2 = vB + b2Cross(wB, cp2->rB) - vA - b2Cross(wA, cp2->rA);
// Compute normal velocity
vn1 = b2Dot(dv1, normal);
vn2 = b2Dot(dv2, normal);
b2Assert(b2Abs(vn1 - cp1->velocityBias) < k_errorTol);
b2Assert(b2Abs(vn2 - cp2->velocityBias) < k_errorTol);
#endif
*/
break;
}
//
// Case 2: vn1 = 0 and x2 = 0
//
// 0 = a11 * x1 + a12 * 0 + b1'
// vn2 = a21 * x1 + a22 * 0 + b2'
//
x.x = (-cp1.normalMass * b.x);
x.y = 0;
vn1 = 0;
vn2 = vc.K.ex.y * x.x + b.y;
if (x.x >= 0 && vn2 >= 0) {
// Get the incremental impulse
// b2Vec2 d = x - a;
b2Vec2.SubVV(x, a, d);
// Apply incremental impulse
// b2Vec2 P1 = d.x * normal;
b2Vec2.MulSV(d.x, normal, P1);
// b2Vec2 P2 = d.y * normal;
b2Vec2.MulSV(d.y, normal, P2);
b2Vec2.AddVV(P1, P2, P1P2);
// vA -= mA * (P1 + P2);
vA.SelfMulSub(mA, P1P2);
// wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2));
wA -= iA * (b2Vec2.CrossVV(cp1.rA, P1) + b2Vec2.CrossVV(cp2.rA, P2));
// vB += mB * (P1 + P2);
vB.SelfMulAdd(mB, P1P2);
// wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2));
wB += iB * (b2Vec2.CrossVV(cp1.rB, P1) + b2Vec2.CrossVV(cp2.rB, P2));
// Accumulate
cp1.normalImpulse = x.x;
cp2.normalImpulse = x.y;
/*
#if B2_DEBUG_SOLVER === 1
// Postconditions
dv1 = vB + b2Cross(wB, cp1->rB) - vA - b2Cross(wA, cp1->rA);
// Compute normal velocity
vn1 = b2Dot(dv1, normal);
b2Assert(b2Abs(vn1 - cp1->velocityBias) < k_errorTol);
#endif
*/
break;
}
//
// Case 3: vn2 = 0 and x1 = 0
//
// vn1 = a11 * 0 + a12 * x2 + b1'
// 0 = a21 * 0 + a22 * x2 + b2'
//
x.x = 0;
x.y = (-cp2.normalMass * b.y);
vn1 = vc.K.ey.x * x.y + b.x;
vn2 = 0;
if (x.y >= 0 && vn1 >= 0) {
// Resubstitute for the incremental impulse
// b2Vec2 d = x - a;
b2Vec2.SubVV(x, a, d);
// Apply incremental impulse
// b2Vec2 P1 = d.x * normal;
b2Vec2.MulSV(d.x, normal, P1);
// b2Vec2 P2 = d.y * normal;
b2Vec2.MulSV(d.y, normal, P2);
b2Vec2.AddVV(P1, P2, P1P2);
// vA -= mA * (P1 + P2);
vA.SelfMulSub(mA, P1P2);
// wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2));
wA -= iA * (b2Vec2.CrossVV(cp1.rA, P1) + b2Vec2.CrossVV(cp2.rA, P2));
// vB += mB * (P1 + P2);
vB.SelfMulAdd(mB, P1P2);
// wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2));
wB += iB * (b2Vec2.CrossVV(cp1.rB, P1) + b2Vec2.CrossVV(cp2.rB, P2));
// Accumulate
cp1.normalImpulse = x.x;
cp2.normalImpulse = x.y;
/*
#if B2_DEBUG_SOLVER === 1
// Postconditions
dv2 = vB + b2Cross(wB, cp2->rB) - vA - b2Cross(wA, cp2->rA);
// Compute normal velocity
vn2 = b2Dot(dv2, normal);
b2Assert(b2Abs(vn2 - cp2->velocityBias) < k_errorTol);
#endif
*/
break;
}
//
// Case 4: x1 = 0 and x2 = 0
//
// vn1 = b1
// vn2 = b2;
x.x = 0;
x.y = 0;
vn1 = b.x;
vn2 = b.y;
if (vn1 >= 0 && vn2 >= 0) {
// Resubstitute for the incremental impulse
// b2Vec2 d = x - a;
b2Vec2.SubVV(x, a, d);
// Apply incremental impulse
// b2Vec2 P1 = d.x * normal;
b2Vec2.MulSV(d.x, normal, P1);
// b2Vec2 P2 = d.y * normal;
b2Vec2.MulSV(d.y, normal, P2);
b2Vec2.AddVV(P1, P2, P1P2);
// vA -= mA * (P1 + P2);
vA.SelfMulSub(mA, P1P2);
// wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2));
wA -= iA * (b2Vec2.CrossVV(cp1.rA, P1) + b2Vec2.CrossVV(cp2.rA, P2));
// vB += mB * (P1 + P2);
vB.SelfMulAdd(mB, P1P2);
// wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2));
wB += iB * (b2Vec2.CrossVV(cp1.rB, P1) + b2Vec2.CrossVV(cp2.rB, P2));
// Accumulate
cp1.normalImpulse = x.x;
cp2.normalImpulse = x.y;
break;
}
// No solution, give up. This is hit sometimes, but it doesn't seem to matter.
break;
}
}
// this.m_velocities[indexA].v = vA;
this.m_velocities[indexA].w = wA;
// this.m_velocities[indexB].v = vB;
this.m_velocities[indexB].w = wB;
}
};
b2ContactSolver.prototype.StoreImpulses = function () {
for (var i = 0; i < this.m_count; ++i) {
var vc = this.m_velocityConstraints[i];
var manifold = this.m_contacts[vc.contactIndex].GetManifold();
for (var j = 0; j < vc.pointCount; ++j) {
manifold.points[j].normalImpulse = vc.points[j].normalImpulse;
manifold.points[j].tangentImpulse = vc.points[j].tangentImpulse;
}
}
};
b2ContactSolver.prototype.SolvePositionConstraints = function () {
var xfA = b2ContactSolver.SolvePositionConstraints_s_xfA;
var xfB = b2ContactSolver.SolvePositionConstraints_s_xfB;
var psm = b2ContactSolver.SolvePositionConstraints_s_psm;
var rA = b2ContactSolver.SolvePositionConstraints_s_rA;
var rB = b2ContactSolver.SolvePositionConstraints_s_rB;
var P = b2ContactSolver.SolvePositionConstraints_s_P;
var minSeparation = 0;
for (var i = 0; i < this.m_count; ++i) {
var pc = this.m_positionConstraints[i];
var indexA = pc.indexA;
var indexB = pc.indexB;
var localCenterA = pc.localCenterA;
var mA = pc.invMassA;
var iA = pc.invIA;
var localCenterB = pc.localCenterB;
var mB = pc.invMassB;
var iB = pc.invIB;
var pointCount = pc.pointCount;
var cA = this.m_positions[indexA].c;
var aA = this.m_positions[indexA].a;
var cB = this.m_positions[indexB].c;
var aB = this.m_positions[indexB].a;
// Solve normal constraints
for (var j = 0; j < pointCount; ++j) {
xfA.q.SetAngle(aA);
xfB.q.SetAngle(aB);
b2Vec2.SubVV(cA, b2Rot.MulRV(xfA.q, localCenterA, b2Vec2.s_t0), xfA.p);
b2Vec2.SubVV(cB, b2Rot.MulRV(xfB.q, localCenterB, b2Vec2.s_t0), xfB.p);
psm.Initialize(pc, xfA, xfB, j);
var normal = psm.normal;
var point = psm.point;
var separation = psm.separation;
// b2Vec2 rA = point - cA;
b2Vec2.SubVV(point, cA, rA);
// b2Vec2 rB = point - cB;
b2Vec2.SubVV(point, cB, rB);
// Track max constraint error.
minSeparation = b2Min(minSeparation, separation);
// Prevent large corrections and allow slop.
var C = b2Clamp(b2_baumgarte * (separation + b2_linearSlop), (-b2_maxLinearCorrection), 0);
// Compute the effective mass.
// float32 rnA = b2Cross(rA, normal);
var rnA = b2Vec2.CrossVV(rA, normal);
// float32 rnB = b2Cross(rB, normal);
var rnB = b2Vec2.CrossVV(rB, normal);
// float32 K = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
var K = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
// Compute normal impulse
var impulse = K > 0 ? -C / K : 0;
// b2Vec2 P = impulse * normal;
b2Vec2.MulSV(impulse, normal, P);
// cA -= mA * P;
cA.SelfMulSub(mA, P);
// aA -= iA * b2Cross(rA, P);
aA -= iA * b2Vec2.CrossVV(rA, P);
// cB += mB * P;
cB.SelfMulAdd(mB, P);
// aB += iB * b2Cross(rB, P);
aB += iB * b2Vec2.CrossVV(rB, P);
}
// this.m_positions[indexA].c = cA;
this.m_positions[indexA].a = aA;
// this.m_positions[indexB].c = cB;
this.m_positions[indexB].a = aB;
}
// We can't expect minSpeparation >= -b2_linearSlop because we don't
// push the separation above -b2_linearSlop.
return minSeparation > (-3 * b2_linearSlop);
};
b2ContactSolver.prototype.SolveTOIPositionConstraints = function (toiIndexA, toiIndexB) {
var xfA = b2ContactSolver.SolveTOIPositionConstraints_s_xfA;
var xfB = b2ContactSolver.SolveTOIPositionConstraints_s_xfB;
var psm = b2ContactSolver.SolveTOIPositionConstraints_s_psm;
var rA = b2ContactSolver.SolveTOIPositionConstraints_s_rA;
var rB = b2ContactSolver.SolveTOIPositionConstraints_s_rB;
var P = b2ContactSolver.SolveTOIPositionConstraints_s_P;
var minSeparation = 0;
for (var i = 0; i < this.m_count; ++i) {
var pc = this.m_positionConstraints[i];
var indexA = pc.indexA;
var indexB = pc.indexB;
var localCenterA = pc.localCenterA;
var localCenterB = pc.localCenterB;
var pointCount = pc.pointCount;
var mA = 0;
var iA = 0;
if (indexA === toiIndexA || indexA === toiIndexB) {
mA = pc.invMassA;
iA = pc.invIA;
}
var mB = 0;
var iB = 0;
if (indexB === toiIndexA || indexB === toiIndexB) {
mB = pc.invMassB;
iB = pc.invIB;
}
var cA = this.m_positions[indexA].c;
var aA = this.m_positions[indexA].a;
var cB = this.m_positions[indexB].c;
var aB = this.m_positions[indexB].a;
// Solve normal constraints
for (var j = 0; j < pointCount; ++j) {
xfA.q.SetAngle(aA);
xfB.q.SetAngle(aB);
b2Vec2.SubVV(cA, b2Rot.MulRV(xfA.q, localCenterA, b2Vec2.s_t0), xfA.p);
b2Vec2.SubVV(cB, b2Rot.MulRV(xfB.q, localCenterB, b2Vec2.s_t0), xfB.p);
psm.Initialize(pc, xfA, xfB, j);
var normal = psm.normal;
var point = psm.point;
var separation = psm.separation;
// b2Vec2 rA = point - cA;
b2Vec2.SubVV(point, cA, rA);
// b2Vec2 rB = point - cB;
b2Vec2.SubVV(point, cB, rB);
// Track max constraint error.
minSeparation = b2Min(minSeparation, separation);
// Prevent large corrections and allow slop.
var C = b2Clamp(b2_toiBaumgarte * (separation + b2_linearSlop), (-b2_maxLinearCorrection), 0);
// Compute the effective mass.
// float32 rnA = b2Cross(rA, normal);
var rnA = b2Vec2.CrossVV(rA, normal);
// float32 rnB = b2Cross(rB, normal);
var rnB = b2Vec2.CrossVV(rB, normal);
// float32 K = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
var K = mA + mB + iA * rnA * rnA + iB * rnB * rnB;
// Compute normal impulse
var impulse = K > 0 ? -C / K : 0;
// b2Vec2 P = impulse * normal;
b2Vec2.MulSV(impulse, normal, P);
// cA -= mA * P;
cA.SelfMulSub(mA, P);
// aA -= iA * b2Cross(rA, P);
aA -= iA * b2Vec2.CrossVV(rA, P);
// cB += mB * P;
cB.SelfMulAdd(mB, P);
// aB += iB * b2Cross(rB, P);
aB += iB * b2Vec2.CrossVV(rB, P);
}
// this.m_positions[indexA].c = cA;
this.m_positions[indexA].a = aA;
// this.m_positions[indexB].c = cB;
this.m_positions[indexB].a = aB;
}
// We can't expect minSpeparation >= -b2_linearSlop because we don't
// push the separation above -b2_linearSlop.
return minSeparation >= -1.5 * b2_linearSlop;
};
b2ContactSolver.InitializeVelocityConstraints_s_xfA = new b2Transform();
b2ContactSolver.InitializeVelocityConstraints_s_xfB = new b2Transform();
b2ContactSolver.InitializeVelocityConstraints_s_worldManifold = new b2WorldManifold();
b2ContactSolver.WarmStart_s_P = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_dv = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_dv1 = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_dv2 = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_P = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_a = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_b = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_x = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_d = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_P1 = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_P2 = new b2Vec2();
b2ContactSolver.SolveVelocityConstraints_s_P1P2 = new b2Vec2();
b2ContactSolver.SolvePositionConstraints_s_xfA = new b2Transform();
b2ContactSolver.SolvePositionConstraints_s_xfB = new b2Transform();
b2ContactSolver.SolvePositionConstraints_s_psm = new b2PositionSolverManifold();
b2ContactSolver.SolvePositionConstraints_s_rA = new b2Vec2();
b2ContactSolver.SolvePositionConstraints_s_rB = new b2Vec2();
b2ContactSolver.SolvePositionConstraints_s_P = new b2Vec2();
b2ContactSolver.SolveTOIPositionConstraints_s_xfA = new b2Transform();
b2ContactSolver.SolveTOIPositionConstraints_s_xfB = new b2Transform();
b2ContactSolver.SolveTOIPositionConstraints_s_psm = new b2PositionSolverManifold();
b2ContactSolver.SolveTOIPositionConstraints_s_rA = new b2Vec2();
b2ContactSolver.SolveTOIPositionConstraints_s_rB = new b2Vec2();
b2ContactSolver.SolveTOIPositionConstraints_s_P = new b2Vec2();
return b2ContactSolver;
}());
/*
* Copyright (c) 2006-2009 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.
*/
/*
Position Correction Notes
=========================
I tried the several algorithms for position correction of the 2D revolute joint.
I looked at these systems:
- simple pendulum (1m diameter sphere on massless 5m stick) with initial angular velocity of 100 rad/s.
- suspension bridge with 30 1m long planks of length 1m.
- multi-link chain with 30 1m long links.
Here are the algorithms:
Baumgarte - A fraction of the position error is added to the velocity error. There is no
separate position solver.
Pseudo Velocities - After the velocity solver and position integration,
the position error, Jacobian, and effective mass are recomputed. Then
the velocity constraints are solved with pseudo velocities and a fraction
of the position error is added to the pseudo velocity error. The pseudo
velocities are initialized to zero and there is no warm-starting. After
the position solver, the pseudo velocities are added to the positions.
This is also called the First Order World method or the Position LCP method.
Modified Nonlinear Gauss-Seidel (NGS) - Like Pseudo Velocities except the
position error is re-computed for each constraint and the positions are updated
after the constraint is solved. The radius vectors (aka Jacobians) are
re-computed too (otherwise the algorithm has horrible instability). The pseudo
velocity states are not needed because they are effectively zero at the beginning
of each iteration. Since we have the current position error, we allow the
iterations to terminate early if the error becomes smaller than b2_linearSlop.
Full NGS or just NGS - Like Modified NGS except the effective mass are re-computed
each time a constraint is solved.
Here are the results:
Baumgarte - this is the cheapest algorithm but it has some stability problems,
especially with the bridge. The chain links separate easily close to the root
and they jitter as they struggle to pull together. This is one of the most common
methods in the field. The big drawback is that the position correction artificially
affects the momentum, thus leading to instabilities and false bounce. I used a
bias factor of 0.2. A larger bias factor makes the bridge less stable, a smaller
factor makes joints and contacts more spongy.
Pseudo Velocities - the is more stable than the Baumgarte method. The bridge is
stable. However, joints still separate with large angular velocities. Drag the
simple pendulum in a circle quickly and the joint will separate. The chain separates
easily and does not recover. I used a bias factor of 0.2. A larger value lead to
the bridge collapsing when a heavy cube drops on it.
Modified NGS - this algorithm is better in some ways than Baumgarte and Pseudo
Velocities, but in other ways it is worse. The bridge and chain are much more
stable, but the simple pendulum goes unstable at high angular velocities.
Full NGS - stable in all tests. The joints display good stiffness. The bridge
still sags, but this is better than infinite forces.
Recommendations
Pseudo Velocities are not really worthwhile because the bridge and chain cannot
recover from joint separation. In other cases the benefit over Baumgarte is small.
Modified NGS is not a robust method for the revolute joint due to the violent
instability seen in the simple pendulum. Perhaps it is viable with other constraint
types, especially scalar constraints where the effective mass is a scalar.
This leaves Baumgarte and Full NGS. Baumgarte has small, but manageable instabilities
and is very fast. I don't think we can escape Baumgarte, especially in highly
demanding cases where high constraint fidelity is not needed.
Full NGS is robust and easy on the eyes. I recommend this as an option for
higher fidelity simulation and certainly for suspension bridges and long chains.
Full NGS might be a good choice for ragdolls, especially motorized ragdolls where
joint separation can be problematic. The number of NGS iterations can be reduced
for better performance without harming robustness much.
Each joint in a can be handled differently in the position solver. So I recommend
a system where the user can select the algorithm on a per joint basis. I would
probably default to the slower Full NGS and let the user select the faster
Baumgarte method in performance critical scenarios.
*/
/*
Cache Performance
The Box2D solvers are dominated by cache misses. Data structures are designed
to increase the number of cache hits. Much of misses are due to random access
to body data. The constraint structures are iterated over linearly, which leads
to few cache misses.
The bodies are not accessed during iteration. Instead read only data, such as
the mass values are stored with the constraints. The mutable data are the constraint
impulses and the bodies velocities/positions. The impulses are held inside the
constraint structures. The body velocities/positions are held in compact, temporary
arrays to increase the number of cache hits. Linear and angular velocity are
stored in a single array since multiple arrays lead to multiple misses.
*/
/*
2D Rotation
R = [cos(theta) -sin(theta)]
[sin(theta) cos(theta) ]
thetaDot = omega
Let q1 = cos(theta), q2 = sin(theta).
R = [q1 -q2]
[q2 q1]
q1Dot = -thetaDot * q2
q2Dot = thetaDot * q1
q1_new = q1_old - dt * w * q2
q2_new = q2_old + dt * w * q1
then normalize.
This might be faster than computing sin+cos.
However, we can compute sin+cos of the same angle fast.
*/
var b2Island = /** @class */ (function () {
function b2Island() {
this.m_allocator = null;
this.m_bodies = [ /*1024*/]; // TODO: b2Settings
this.m_contacts = [ /*1024*/]; // TODO: b2Settings
this.m_joints = [ /*1024*/]; // TODO: b2Settings
this.m_positions = b2Position.MakeArray(1024); // TODO: b2Settings
this.m_velocities = b2Velocity.MakeArray(1024); // TODO: b2Settings
this.m_bodyCount = 0;
this.m_jointCount = 0;
this.m_contactCount = 0;
this.m_bodyCapacity = 0;
this.m_contactCapacity = 0;
this.m_jointCapacity = 0;
}
b2Island.prototype.Initialize = function (bodyCapacity, contactCapacity, jointCapacity, allocator, listener) {
this.m_bodyCapacity = bodyCapacity;
this.m_contactCapacity = contactCapacity;
this.m_jointCapacity = jointCapacity;
this.m_bodyCount = 0;
this.m_contactCount = 0;
this.m_jointCount = 0;
this.m_allocator = allocator;
this.m_listener = listener;
// TODO:
// while (this.m_bodies.length < bodyCapacity) {
// this.m_bodies[this.m_bodies.length] = null;
// }
// TODO:
// while (this.m_contacts.length < contactCapacity) {
// this.m_contacts[this.m_contacts.length] = null;
// }
// TODO:
// while (this.m_joints.length < jointCapacity) {
// this.m_joints[this.m_joints.length] = null;
// }
// TODO:
if (this.m_positions.length < bodyCapacity) {
var new_length = b2Max(this.m_positions.length * 2, bodyCapacity);
while (this.m_positions.length < new_length) {
this.m_positions[this.m_positions.length] = new b2Position();
}
}
// TODO:
if (this.m_velocities.length < bodyCapacity) {
var new_length = b2Max(this.m_velocities.length * 2, bodyCapacity);
while (this.m_velocities.length < new_length) {
this.m_velocities[this.m_velocities.length] = new b2Velocity();
}
}
};
b2Island.prototype.Clear = function () {
this.m_bodyCount = 0;
this.m_contactCount = 0;
this.m_jointCount = 0;
};
b2Island.prototype.AddBody = function (body) {
// DEBUG: b2Assert(this.m_bodyCount < this.m_bodyCapacity);
body.m_islandIndex = this.m_bodyCount;
this.m_bodies[this.m_bodyCount++] = body;
};
b2Island.prototype.AddContact = function (contact) {
// DEBUG: b2Assert(this.m_contactCount < this.m_contactCapacity);
this.m_contacts[this.m_contactCount++] = contact;
};
b2Island.prototype.AddJoint = function (joint) {
// DEBUG: b2Assert(this.m_jointCount < this.m_jointCapacity);
this.m_joints[this.m_jointCount++] = joint;
};
b2Island.prototype.Solve = function (profile, step, gravity, allowSleep) {
var timer = b2Island.s_timer.Reset();
var h = step.dt;
// Integrate velocities and apply damping. Initialize the body state.
for (var i = 0; i < this.m_bodyCount; ++i) {
var b = this.m_bodies[i];
// const c: b2Vec2 =
this.m_positions[i].c.Copy(b.m_sweep.c);
var a = b.m_sweep.a;
var v = this.m_velocities[i].v.Copy(b.m_linearVelocity);
var w = b.m_angularVelocity;
// Store positions for continuous collision.
b.m_sweep.c0.Copy(b.m_sweep.c);
b.m_sweep.a0 = b.m_sweep.a;
if (b.m_type === exports.b2BodyType.b2_dynamicBody) {
// Integrate velocities.
v.x += h * (b.m_gravityScale * gravity.x + b.m_invMass * b.m_force.x);
v.y += h * (b.m_gravityScale * gravity.y + b.m_invMass * b.m_force.y);
w += h * b.m_invI * b.m_torque;
// Apply damping.
// ODE: dv/dt + c * v = 0
// Solution: v(t) = v0 * exp(-c * t)
// Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt)
// v2 = exp(-c * dt) * v1
// Pade approximation:
// v2 = v1 * 1 / (1 + c * dt)
v.SelfMul(1.0 / (1.0 + h * b.m_linearDamping));
w *= 1.0 / (1.0 + h * b.m_angularDamping);
}
// this.m_positions[i].c = c;
this.m_positions[i].a = a;
// this.m_velocities[i].v = v;
this.m_velocities[i].w = w;
}
timer.Reset();
// Solver data
var solverData = b2Island.s_solverData;
solverData.step.Copy(step);
solverData.positions = this.m_positions;
solverData.velocities = this.m_velocities;
// Initialize velocity constraints.
var contactSolverDef = b2Island.s_contactSolverDef;
contactSolverDef.step.Copy(step);
contactSolverDef.contacts = this.m_contacts;
contactSolverDef.count = this.m_contactCount;
contactSolverDef.positions = this.m_positions;
contactSolverDef.velocities = this.m_velocities;
contactSolverDef.allocator = this.m_allocator;
var contactSolver = b2Island.s_contactSolver.Initialize(contactSolverDef);
contactSolver.InitializeVelocityConstraints();
if (step.warmStarting) {
contactSolver.WarmStart();
}
for (var i = 0; i < this.m_jointCount; ++i) {
this.m_joints[i].InitVelocityConstraints(solverData);
}
profile.solveInit = timer.GetMilliseconds();
// Solve velocity constraints.
timer.Reset();
for (var i = 0; i < step.velocityIterations; ++i) {
for (var j = 0; j < this.m_jointCount; ++j) {
this.m_joints[j].SolveVelocityConstraints(solverData);
}
contactSolver.SolveVelocityConstraints();
}
// Store impulses for warm starting
contactSolver.StoreImpulses();
profile.solveVelocity = timer.GetMilliseconds();
// Integrate positions.
for (var i = 0; i < this.m_bodyCount; ++i) {
var c = this.m_positions[i].c;
var a = this.m_positions[i].a;
var v = this.m_velocities[i].v;
var w = this.m_velocities[i].w;
// Check for large velocities
var translation = b2Vec2.MulSV(h, v, b2Island.s_translation);
if (b2Vec2.DotVV(translation, translation) > b2_maxTranslationSquared) {
var ratio = b2_maxTranslation / translation.Length();
v.SelfMul(ratio);
}
var rotation = h * w;
if (rotation * rotation > b2_maxRotationSquared) {
var ratio = b2_maxRotation / b2Abs(rotation);
w *= ratio;
}
// Integrate
c.x += h * v.x;
c.y += h * v.y;
a += h * w;
// this.m_positions[i].c = c;
this.m_positions[i].a = a;
// this.m_velocities[i].v = v;
this.m_velocities[i].w = w;
}
// Solve position constraints
timer.Reset();
var positionSolved = false;
for (var i = 0; i < step.positionIterations; ++i) {
var contactsOkay = contactSolver.SolvePositionConstraints();
var jointsOkay = true;
for (var j = 0; j < this.m_jointCount; ++j) {
var jointOkay = this.m_joints[j].SolvePositionConstraints(solverData);
jointsOkay = jointsOkay && jointOkay;
}
if (contactsOkay && jointsOkay) {
// Exit early if the position errors are small.
positionSolved = true;
break;
}
}
// Copy state buffers back to the bodies
for (var i = 0; i < this.m_bodyCount; ++i) {
var body = this.m_bodies[i];
body.m_sweep.c.Copy(this.m_positions[i].c);
body.m_sweep.a = this.m_positions[i].a;
body.m_linearVelocity.Copy(this.m_velocities[i].v);
body.m_angularVelocity = this.m_velocities[i].w;
body.SynchronizeTransform();
}
profile.solvePosition = timer.GetMilliseconds();
this.Report(contactSolver.m_velocityConstraints);
if (allowSleep) {
var minSleepTime = b2_maxFloat;
var linTolSqr = b2_linearSleepTolerance * b2_linearSleepTolerance;
var angTolSqr = b2_angularSleepTolerance * b2_angularSleepTolerance;
for (var i = 0; i < this.m_bodyCount; ++i) {
var b = this.m_bodies[i];
if (b.GetType() === exports.b2BodyType.b2_staticBody) {
continue;
}
if (!b.m_autoSleepFlag ||
b.m_angularVelocity * b.m_angularVelocity > angTolSqr ||
b2Vec2.DotVV(b.m_linearVelocity, b.m_linearVelocity) > linTolSqr) {
b.m_sleepTime = 0;
minSleepTime = 0;
}
else {
b.m_sleepTime += h;
minSleepTime = b2Min(minSleepTime, b.m_sleepTime);
}
}
if (minSleepTime >= b2_timeToSleep && positionSolved) {
for (var i = 0; i < this.m_bodyCount; ++i) {
var b = this.m_bodies[i];
b.SetAwake(false);
}
}
}
};
b2Island.prototype.SolveTOI = function (subStep, toiIndexA, toiIndexB) {
// DEBUG: b2Assert(toiIndexA < this.m_bodyCount);
// DEBUG: b2Assert(toiIndexB < this.m_bodyCount);
// Initialize the body state.
for (var i = 0; i < this.m_bodyCount; ++i) {
var b = this.m_bodies[i];
this.m_positions[i].c.Copy(b.m_sweep.c);
this.m_positions[i].a = b.m_sweep.a;
this.m_velocities[i].v.Copy(b.m_linearVelocity);
this.m_velocities[i].w = b.m_angularVelocity;
}
var contactSolverDef = b2Island.s_contactSolverDef;
contactSolverDef.contacts = this.m_contacts;
contactSolverDef.count = this.m_contactCount;
contactSolverDef.allocator = this.m_allocator;
contactSolverDef.step.Copy(subStep);
contactSolverDef.positions = this.m_positions;
contactSolverDef.velocities = this.m_velocities;
var contactSolver = b2Island.s_contactSolver.Initialize(contactSolverDef);
// Solve position constraints.
for (var i = 0; i < subStep.positionIterations; ++i) {
var contactsOkay = contactSolver.SolveTOIPositionConstraints(toiIndexA, toiIndexB);
if (contactsOkay) {
break;
}
}
/*
#if 0
// Is the new position really safe?
for (int32 i = 0; i < this.m_contactCount; ++i) {
b2Contact* c = this.m_contacts[i];
b2Fixture* fA = c.GetFixtureA();
b2Fixture* fB = c.GetFixtureB();
b2Body* bA = fA.GetBody();
b2Body* bB = fB.GetBody();
int32 indexA = c.GetChildIndexA();
int32 indexB = c.GetChildIndexB();
b2DistanceInput input;
input.proxyA.Set(fA.GetShape(), indexA);
input.proxyB.Set(fB.GetShape(), indexB);
input.transformA = bA.GetTransform();
input.transformB = bB.GetTransform();
input.useRadii = false;
b2DistanceOutput output;
b2SimplexCache cache;
cache.count = 0;
b2Distance(&output, &cache, &input);
if (output.distance === 0 || cache.count === 3) {
cache.count += 0;
}
}
#endif
*/
// Leap of faith to new safe state.
this.m_bodies[toiIndexA].m_sweep.c0.Copy(this.m_positions[toiIndexA].c);
this.m_bodies[toiIndexA].m_sweep.a0 = this.m_positions[toiIndexA].a;
this.m_bodies[toiIndexB].m_sweep.c0.Copy(this.m_positions[toiIndexB].c);
this.m_bodies[toiIndexB].m_sweep.a0 = this.m_positions[toiIndexB].a;
// No warm starting is needed for TOI events because warm
// starting impulses were applied in the discrete solver.
contactSolver.InitializeVelocityConstraints();
// Solve velocity constraints.
for (var i = 0; i < subStep.velocityIterations; ++i) {
contactSolver.SolveVelocityConstraints();
}
// Don't store the TOI contact forces for warm starting
// because they can be quite large.
var h = subStep.dt;
// Integrate positions
for (var i = 0; i < this.m_bodyCount; ++i) {
var c = this.m_positions[i].c;
var a = this.m_positions[i].a;
var v = this.m_velocities[i].v;
var w = this.m_velocities[i].w;
// Check for large velocities
var translation = b2Vec2.MulSV(h, v, b2Island.s_translation);
if (b2Vec2.DotVV(translation, translation) > b2_maxTranslationSquared) {
var ratio = b2_maxTranslation / translation.Length();
v.SelfMul(ratio);
}
var rotation = h * w;
if (rotation * rotation > b2_maxRotationSquared) {
var ratio = b2_maxRotation / b2Abs(rotation);
w *= ratio;
}
// Integrate
c.SelfMulAdd(h, v);
a += h * w;
// this.m_positions[i].c = c;
this.m_positions[i].a = a;
// this.m_velocities[i].v = v;
this.m_velocities[i].w = w;
// Sync bodies
var body = this.m_bodies[i];
body.m_sweep.c.Copy(c);
body.m_sweep.a = a;
body.m_linearVelocity.Copy(v);
body.m_angularVelocity = w;
body.SynchronizeTransform();
}
this.Report(contactSolver.m_velocityConstraints);
};
b2Island.prototype.Report = function (constraints) {
if (this.m_listener === null) {
return;
}
for (var i = 0; i < this.m_contactCount; ++i) {
var c = this.m_contacts[i];
if (!c) {
continue;
}
var vc = constraints[i];
var impulse = b2Island.s_impulse;
impulse.count = vc.pointCount;
for (var j = 0; j < vc.pointCount; ++j) {
impulse.normalImpulses[j] = vc.points[j].normalImpulse;
impulse.tangentImpulses[j] = vc.points[j].tangentImpulse;
}
this.m_listener.PostSolve(c, impulse);
}
};
b2Island.s_timer = new b2Timer();
b2Island.s_solverData = new b2SolverData();
b2Island.s_contactSolverDef = new b2ContactSolverDef();
b2Island.s_contactSolver = new b2ContactSolver();
b2Island.s_translation = new b2Vec2();
b2Island.s_impulse = new b2ContactImpulse();
return b2Island;
}());
/*
* Copyright (c) 2013 Google, Inc.
*
* 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 (b2ParticleFlag) {
/// Water particle.
b2ParticleFlag[b2ParticleFlag["b2_waterParticle"] = 0] = "b2_waterParticle";
/// Removed after next simulation step.
b2ParticleFlag[b2ParticleFlag["b2_zombieParticle"] = 2] = "b2_zombieParticle";
/// Zero velocity.
b2ParticleFlag[b2ParticleFlag["b2_wallParticle"] = 4] = "b2_wallParticle";
/// With restitution from stretching.
b2ParticleFlag[b2ParticleFlag["b2_springParticle"] = 8] = "b2_springParticle";
/// With restitution from deformation.
b2ParticleFlag[b2ParticleFlag["b2_elasticParticle"] = 16] = "b2_elasticParticle";
/// With viscosity.
b2ParticleFlag[b2ParticleFlag["b2_viscousParticle"] = 32] = "b2_viscousParticle";
/// Without isotropic pressure.
b2ParticleFlag[b2ParticleFlag["b2_powderParticle"] = 64] = "b2_powderParticle";
/// With surface tension.
b2ParticleFlag[b2ParticleFlag["b2_tensileParticle"] = 128] = "b2_tensileParticle";
/// Mix color between contacting particles.
b2ParticleFlag[b2ParticleFlag["b2_colorMixingParticle"] = 256] = "b2_colorMixingParticle";
/// Call b2DestructionListener on destruction.
b2ParticleFlag[b2ParticleFlag["b2_destructionListenerParticle"] = 512] = "b2_destructionListenerParticle";
/// Prevents other particles from leaking.
b2ParticleFlag[b2ParticleFlag["b2_barrierParticle"] = 1024] = "b2_barrierParticle";
/// Less compressibility.
b2ParticleFlag[b2ParticleFlag["b2_staticPressureParticle"] = 2048] = "b2_staticPressureParticle";
/// Makes pairs or triads with other particles.
b2ParticleFlag[b2ParticleFlag["b2_reactiveParticle"] = 4096] = "b2_reactiveParticle";
/// With high repulsive force.
b2ParticleFlag[b2ParticleFlag["b2_repulsiveParticle"] = 8192] = "b2_repulsiveParticle";
/// Call b2ContactListener when this particle is about to interact with
/// a rigid body or stops interacting with a rigid body.
/// This results in an expensive operation compared to using
/// b2_fixtureContactFilterParticle to detect collisions between
/// particles.
b2ParticleFlag[b2ParticleFlag["b2_fixtureContactListenerParticle"] = 16384] = "b2_fixtureContactListenerParticle";
/// Call b2ContactListener when this particle is about to interact with
/// another particle or stops interacting with another particle.
/// This results in an expensive operation compared to using
/// b2_particleContactFilterParticle to detect collisions between
/// particles.
b2ParticleFlag[b2ParticleFlag["b2_particleContactListenerParticle"] = 32768] = "b2_particleContactListenerParticle";
/// Call b2ContactFilter when this particle interacts with rigid bodies.
b2ParticleFlag[b2ParticleFlag["b2_fixtureContactFilterParticle"] = 65536] = "b2_fixtureContactFilterParticle";
/// Call b2ContactFilter when this particle interacts with other
/// particles.
b2ParticleFlag[b2ParticleFlag["b2_particleContactFilterParticle"] = 131072] = "b2_particleContactFilterParticle";
})(exports.b2ParticleFlag || (exports.b2ParticleFlag = {}));
var b2ParticleDef = /** @class */ (function () {
function b2ParticleDef() {
this.flags = 0;
this.position = new b2Vec2();
this.velocity = new b2Vec2();
this.color = new b2Color(0, 0, 0, 0);
this.lifetime = 0.0;
this.userData = null;
this.group = null;
}
return b2ParticleDef;
}());
function b2CalculateParticleIterations(gravity, radius, timeStep) {
// In some situations you may want more particle iterations than this,
// but to avoid excessive cycle cost, don't recommend more than this.
var B2_MAX_RECOMMENDED_PARTICLE_ITERATIONS = 8;
var B2_RADIUS_THRESHOLD = 0.01;
var iterations = Math.ceil(Math.sqrt(gravity / (B2_RADIUS_THRESHOLD * radius)) * timeStep);
return b2Clamp(iterations, 1, B2_MAX_RECOMMENDED_PARTICLE_ITERATIONS);
}
var b2ParticleHandle = /** @class */ (function () {
function b2ParticleHandle() {
this.m_index = b2_invalidParticleIndex;
}
b2ParticleHandle.prototype.GetIndex = function () { return this.m_index; };
b2ParticleHandle.prototype.SetIndex = function (index) { this.m_index = index; };
return b2ParticleHandle;
}());
// #endif
/*
* Copyright (c) 2013 Google, Inc.
*
* 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 (b2ParticleGroupFlag) {
/// Prevents overlapping or leaking.
b2ParticleGroupFlag[b2ParticleGroupFlag["b2_solidParticleGroup"] = 1] = "b2_solidParticleGroup";
/// Keeps its shape.
b2ParticleGroupFlag[b2ParticleGroupFlag["b2_rigidParticleGroup"] = 2] = "b2_rigidParticleGroup";
/// Won't be destroyed if it gets empty.
b2ParticleGroupFlag[b2ParticleGroupFlag["b2_particleGroupCanBeEmpty"] = 4] = "b2_particleGroupCanBeEmpty";
/// Will be destroyed on next simulation step.
b2ParticleGroupFlag[b2ParticleGroupFlag["b2_particleGroupWillBeDestroyed"] = 8] = "b2_particleGroupWillBeDestroyed";
/// Updates depth data on next simulation step.
b2ParticleGroupFlag[b2ParticleGroupFlag["b2_particleGroupNeedsUpdateDepth"] = 16] = "b2_particleGroupNeedsUpdateDepth";
b2ParticleGroupFlag[b2ParticleGroupFlag["b2_particleGroupInternalMask"] = 24] = "b2_particleGroupInternalMask";
})(exports.b2ParticleGroupFlag || (exports.b2ParticleGroupFlag = {}));
var b2ParticleGroupDef = /** @class */ (function () {
function b2ParticleGroupDef() {
this.flags = 0;
this.groupFlags = 0;
this.position = new b2Vec2();
this.angle = 0.0;
this.linearVelocity = new b2Vec2();
this.angularVelocity = 0.0;
this.color = new b2Color();
this.strength = 1.0;
this.shapeCount = 0;
this.stride = 0;
this.particleCount = 0;
this.lifetime = 0;
this.userData = null;
this.group = null;
}
return b2ParticleGroupDef;
}());
var b2ParticleGroup = /** @class */ (function () {
function b2ParticleGroup(system) {
this.m_firstIndex = 0;
this.m_lastIndex = 0;
this.m_groupFlags = 0;
this.m_strength = 1.0;
this.m_prev = null;
this.m_next = null;
this.m_timestamp = -1;
this.m_mass = 0.0;
this.m_inertia = 0.0;
this.m_center = new b2Vec2();
this.m_linearVelocity = new b2Vec2();
this.m_angularVelocity = 0.0;
this.m_transform = new b2Transform();
///m_transform.SetIdentity();
this.m_userData = null;
this.m_system = system;
}
b2ParticleGroup.prototype.GetNext = function () {
return this.m_next;
};
b2ParticleGroup.prototype.GetParticleSystem = function () {
return this.m_system;
};
b2ParticleGroup.prototype.GetParticleCount = function () {
return this.m_lastIndex - this.m_firstIndex;
};
b2ParticleGroup.prototype.GetBufferIndex = function () {
return this.m_firstIndex;
};
b2ParticleGroup.prototype.ContainsParticle = function (index) {
return this.m_firstIndex <= index && index < this.m_lastIndex;
};
b2ParticleGroup.prototype.GetAllParticleFlags = function () {
if (!this.m_system.m_flagsBuffer.data) {
throw new Error();
}
var flags = 0;
for (var i = this.m_firstIndex; i < this.m_lastIndex; i++) {
flags |= this.m_system.m_flagsBuffer.data[i];
}
return flags;
};
b2ParticleGroup.prototype.GetGroupFlags = function () {
return this.m_groupFlags;
};
b2ParticleGroup.prototype.SetGroupFlags = function (flags) {
// DEBUG: b2Assert((flags & b2ParticleGroupFlag.b2_particleGroupInternalMask) === 0);
flags |= this.m_groupFlags & exports.b2ParticleGroupFlag.b2_particleGroupInternalMask;
this.m_system.SetGroupFlags(this, flags);
};
b2ParticleGroup.prototype.GetMass = function () {
this.UpdateStatistics();
return this.m_mass;
};
b2ParticleGroup.prototype.GetInertia = function () {
this.UpdateStatistics();
return this.m_inertia;
};
b2ParticleGroup.prototype.GetCenter = function () {
this.UpdateStatistics();
return this.m_center;
};
b2ParticleGroup.prototype.GetLinearVelocity = function () {
this.UpdateStatistics();
return this.m_linearVelocity;
};
b2ParticleGroup.prototype.GetAngularVelocity = function () {
this.UpdateStatistics();
return this.m_angularVelocity;
};
b2ParticleGroup.prototype.GetTransform = function () {
return this.m_transform;
};
b2ParticleGroup.prototype.GetPosition = function () {
return this.m_transform.p;
};
b2ParticleGroup.prototype.GetAngle = function () {
return this.m_transform.q.GetAngle();
};
b2ParticleGroup.prototype.GetLinearVelocityFromWorldPoint = function (worldPoint, out) {
var s_t0 = b2ParticleGroup.GetLinearVelocityFromWorldPoint_s_t0;
this.UpdateStatistics();
/// return m_linearVelocity + b2Cross(m_angularVelocity, worldPoint - m_center);
return b2Vec2.AddVCrossSV(this.m_linearVelocity, this.m_angularVelocity, b2Vec2.SubVV(worldPoint, this.m_center, s_t0), out);
};
b2ParticleGroup.prototype.GetUserData = function () {
return this.m_userData;
};
b2ParticleGroup.prototype.SetUserData = function (data) {
this.m_userData = data;
};
b2ParticleGroup.prototype.ApplyForce = function (force) {
this.m_system.ApplyForce(this.m_firstIndex, this.m_lastIndex, force);
};
b2ParticleGroup.prototype.ApplyLinearImpulse = function (impulse) {
this.m_system.ApplyLinearImpulse(this.m_firstIndex, this.m_lastIndex, impulse);
};
b2ParticleGroup.prototype.DestroyParticles = function (callDestructionListener) {
if (this.m_system.m_world.IsLocked()) {
throw new Error();
}
for (var i = this.m_firstIndex; i < this.m_lastIndex; i++) {
this.m_system.DestroyParticle(i, callDestructionListener);
}
};
b2ParticleGroup.prototype.UpdateStatistics = function () {
if (!this.m_system.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_system.m_velocityBuffer.data) {
throw new Error();
}
var p = new b2Vec2();
var v = new b2Vec2();
if (this.m_timestamp !== this.m_system.m_timestamp) {
var m = this.m_system.GetParticleMass();
/// this.m_mass = 0;
this.m_mass = m * (this.m_lastIndex - this.m_firstIndex);
this.m_center.SetZero();
this.m_linearVelocity.SetZero();
for (var i = this.m_firstIndex; i < this.m_lastIndex; i++) {
/// this.m_mass += m;
/// this.m_center += m * this.m_system.m_positionBuffer.data[i];
this.m_center.SelfMulAdd(m, this.m_system.m_positionBuffer.data[i]);
/// this.m_linearVelocity += m * this.m_system.m_velocityBuffer.data[i];
this.m_linearVelocity.SelfMulAdd(m, this.m_system.m_velocityBuffer.data[i]);
}
if (this.m_mass > 0) {
var inv_mass = 1 / this.m_mass;
///this.m_center *= 1 / this.m_mass;
this.m_center.SelfMul(inv_mass);
///this.m_linearVelocity *= 1 / this.m_mass;
this.m_linearVelocity.SelfMul(inv_mass);
}
this.m_inertia = 0;
this.m_angularVelocity = 0;
for (var i = this.m_firstIndex; i < this.m_lastIndex; i++) {
///b2Vec2 p = this.m_system.m_positionBuffer.data[i] - this.m_center;
b2Vec2.SubVV(this.m_system.m_positionBuffer.data[i], this.m_center, p);
///b2Vec2 v = this.m_system.m_velocityBuffer.data[i] - this.m_linearVelocity;
b2Vec2.SubVV(this.m_system.m_velocityBuffer.data[i], this.m_linearVelocity, v);
this.m_inertia += m * b2Vec2.DotVV(p, p);
this.m_angularVelocity += m * b2Vec2.CrossVV(p, v);
}
if (this.m_inertia > 0) {
this.m_angularVelocity *= 1 / this.m_inertia;
}
this.m_timestamp = this.m_system.m_timestamp;
}
};
b2ParticleGroup.GetLinearVelocityFromWorldPoint_s_t0 = new b2Vec2();
return b2ParticleGroup;
}());
// #endif
/*
* Copyright (c) 2013 Google, Inc.
*
* 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.
*/
var b2StackQueue = /** @class */ (function () {
function b2StackQueue(capacity) {
this.m_front = 0;
this.m_back = 0;
this.m_capacity = 0;
this.m_buffer = b2MakeArray(capacity, function (index) { return null; });
this.m_capacity = capacity;
}
b2StackQueue.prototype.Push = function (item) {
if (this.m_back >= this.m_capacity) {
for (var i = this.m_front; i < this.m_back; i++) {
this.m_buffer[i - this.m_front] = this.m_buffer[i];
}
this.m_back -= this.m_front;
this.m_front = 0;
if (this.m_back >= this.m_capacity) {
if (this.m_capacity > 0) {
this.m_buffer.concat(b2MakeArray(this.m_capacity, function (index) { return null; }));
this.m_capacity *= 2;
}
else {
this.m_buffer.concat(b2MakeArray(1, function (index) { return null; }));
this.m_capacity = 1;
}
///m_buffer = (T*) m_allocator->Reallocate(m_buffer, sizeof(T) * m_capacity);
}
}
this.m_buffer[this.m_back] = item;
this.m_back++;
};
b2StackQueue.prototype.Pop = function () {
// DEBUG: b2Assert(this.m_front < this.m_back);
this.m_buffer[this.m_front] = null;
this.m_front++;
};
b2StackQueue.prototype.Empty = function () {
// DEBUG: b2Assert(this.m_front <= this.m_back);
return this.m_front === this.m_back;
};
b2StackQueue.prototype.Front = function () {
var item = this.m_buffer[this.m_front];
if (!item) {
throw new Error();
}
return item;
};
return b2StackQueue;
}());
// #endif
/*
* Copyright (c) 2013 Google, Inc.
*
* 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.
*/
/**
* A field representing the nearest generator from each point.
*/
var b2VoronoiDiagram = /** @class */ (function () {
function b2VoronoiDiagram(generatorCapacity) {
this.m_generatorCapacity = 0;
this.m_generatorCount = 0;
this.m_countX = 0;
this.m_countY = 0;
this.m_diagram = [];
this.m_generatorBuffer = b2MakeArray(generatorCapacity, function (index) { return new b2VoronoiDiagram.Generator(); });
this.m_generatorCapacity = generatorCapacity;
}
/**
* Add a generator.
*
* @param center the position of the generator.
* @param tag a tag used to identify the generator in callback functions.
* @param necessary whether to callback for nodes associated with the generator.
*/
b2VoronoiDiagram.prototype.AddGenerator = function (center, tag, necessary) {
// DEBUG: b2Assert(this.m_generatorCount < this.m_generatorCapacity);
var g = this.m_generatorBuffer[this.m_generatorCount++];
g.center.Copy(center);
g.tag = tag;
g.necessary = necessary;
};
/**
* Generate the Voronoi diagram. It is rasterized with a given
* interval in the same range as the necessary generators exist.
*
* @param radius the interval of the diagram.
* @param margin margin for which the range of the diagram is extended.
*/
b2VoronoiDiagram.prototype.Generate = function (radius, margin) {
var inverseRadius = 1 / radius;
var lower = new b2Vec2(+b2_maxFloat, +b2_maxFloat);
var upper = new b2Vec2(-b2_maxFloat, -b2_maxFloat);
var necessary_count = 0;
for (var k = 0; k < this.m_generatorCount; k++) {
var g = this.m_generatorBuffer[k];
if (g.necessary) {
b2Vec2.MinV(lower, g.center, lower);
b2Vec2.MaxV(upper, g.center, upper);
++necessary_count;
}
}
if (necessary_count === 0) {
///debugger;
this.m_countX = 0;
this.m_countY = 0;
return;
}
lower.x -= margin;
lower.y -= margin;
upper.x += margin;
upper.y += margin;
this.m_countX = 1 + Math.floor(inverseRadius * (upper.x - lower.x));
this.m_countY = 1 + Math.floor(inverseRadius * (upper.y - lower.y));
/// m_diagram = (Generator**) m_allocator->Allocate(sizeof(Generator*) * m_countX * m_countY);
/// for (int32 i = 0; i < m_countX * m_countY; i++)
/// {
/// m_diagram[i] = NULL;
/// }
this.m_diagram = []; // b2MakeArray(this.m_countX * this.m_countY, (index) => null);
// (4 * m_countX * m_countY) is the queue capacity that is experimentally
// known to be necessary and sufficient for general particle distributions.
var queue = new b2StackQueue(4 * this.m_countX * this.m_countY);
for (var k = 0; k < this.m_generatorCount; k++) {
var g = this.m_generatorBuffer[k];
/// g.center = inverseRadius * (g.center - lower);
g.center.SelfSub(lower).SelfMul(inverseRadius);
var x = Math.floor(g.center.x);
var y = Math.floor(g.center.y);
if (x >= 0 && y >= 0 && x < this.m_countX && y < this.m_countY) {
queue.Push(new b2VoronoiDiagram.Task(x, y, x + y * this.m_countX, g));
}
}
while (!queue.Empty()) {
var task = queue.Front();
var x = task.m_x;
var y = task.m_y;
var i = task.m_i;
var g = task.m_generator;
queue.Pop();
if (!this.m_diagram[i]) {
this.m_diagram[i] = g;
if (x > 0) {
queue.Push(new b2VoronoiDiagram.Task(x - 1, y, i - 1, g));
}
if (y > 0) {
queue.Push(new b2VoronoiDiagram.Task(x, y - 1, i - this.m_countX, g));
}
if (x < this.m_countX - 1) {
queue.Push(new b2VoronoiDiagram.Task(x + 1, y, i + 1, g));
}
if (y < this.m_countY - 1) {
queue.Push(new b2VoronoiDiagram.Task(x, y + 1, i + this.m_countX, g));
}
}
}
for (var y = 0; y < this.m_countY; y++) {
for (var x = 0; x < this.m_countX - 1; x++) {
var i = x + y * this.m_countX;
var a = this.m_diagram[i];
var b = this.m_diagram[i + 1];
if (a !== b) {
queue.Push(new b2VoronoiDiagram.Task(x, y, i, b));
queue.Push(new b2VoronoiDiagram.Task(x + 1, y, i + 1, a));
}
}
}
for (var y = 0; y < this.m_countY - 1; y++) {
for (var x = 0; x < this.m_countX; x++) {
var i = x + y * this.m_countX;
var a = this.m_diagram[i];
var b = this.m_diagram[i + this.m_countX];
if (a !== b) {
queue.Push(new b2VoronoiDiagram.Task(x, y, i, b));
queue.Push(new b2VoronoiDiagram.Task(x, y + 1, i + this.m_countX, a));
}
}
}
while (!queue.Empty()) {
var task = queue.Front();
var x = task.m_x;
var y = task.m_y;
var i = task.m_i;
var k = task.m_generator;
queue.Pop();
var a = this.m_diagram[i];
var b = k;
if (a !== b) {
var ax = a.center.x - x;
var ay = a.center.y - y;
var bx = b.center.x - x;
var by = b.center.y - y;
var a2 = ax * ax + ay * ay;
var b2 = bx * bx + by * by;
if (a2 > b2) {
this.m_diagram[i] = b;
if (x > 0) {
queue.Push(new b2VoronoiDiagram.Task(x - 1, y, i - 1, b));
}
if (y > 0) {
queue.Push(new b2VoronoiDiagram.Task(x, y - 1, i - this.m_countX, b));
}
if (x < this.m_countX - 1) {
queue.Push(new b2VoronoiDiagram.Task(x + 1, y, i + 1, b));
}
if (y < this.m_countY - 1) {
queue.Push(new b2VoronoiDiagram.Task(x, y + 1, i + this.m_countX, b));
}
}
}
}
};
/**
* Enumerate all nodes that contain at least one necessary
* generator.
*/
b2VoronoiDiagram.prototype.GetNodes = function (callback) {
for (var y = 0; y < this.m_countY - 1; y++) {
for (var x = 0; x < this.m_countX - 1; x++) {
var i = x + y * this.m_countX;
var a = this.m_diagram[i];
var b = this.m_diagram[i + 1];
var c = this.m_diagram[i + this.m_countX];
var d = this.m_diagram[i + 1 + this.m_countX];
if (b !== c) {
if (a !== b && a !== c &&
(a.necessary || b.necessary || c.necessary)) {
callback(a.tag, b.tag, c.tag);
}
if (d !== b && d !== c &&
(a.necessary || b.necessary || c.necessary)) {
callback(b.tag, d.tag, c.tag);
}
}
}
}
};
return b2VoronoiDiagram;
}());
(function (b2VoronoiDiagram) {
var Generator = /** @class */ (function () {
function Generator() {
this.center = new b2Vec2();
this.tag = 0;
this.necessary = false;
}
return Generator;
}());
b2VoronoiDiagram.Generator = Generator;
var Task = /** @class */ (function () {
function Task(x, y, i, g) {
this.m_x = x;
this.m_y = y;
this.m_i = i;
this.m_generator = g;
}
return Task;
}());
b2VoronoiDiagram.Task = Task;
})(b2VoronoiDiagram || (b2VoronoiDiagram = {})); // namespace b2VoronoiDiagram
// #endif
/*
* Copyright (c) 2013 Google, Inc.
*
* 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 std_iter_swap(array, a, b) {
var tmp = array[a];
array[a] = array[b];
array[b] = tmp;
}
function default_compare(a, b) { return a < b; }
function std_sort(array, first, len, cmp) {
if (first === void 0) { first = 0; }
if (len === void 0) { len = array.length - first; }
if (cmp === void 0) { cmp = default_compare; }
var left = first;
var stack = [];
var pos = 0;
for (;;) { /* outer loop */
for (; left + 1 < len; len++) { /* sort left to len-1 */
var pivot = array[left + Math.floor(Math.random() * (len - left))]; /* pick random pivot */
stack[pos++] = len; /* sort right part later */
for (var right = left - 1;;) { /* inner loop: partitioning */
while (cmp(array[++right], pivot)) { } /* look for greater element */
while (cmp(pivot, array[--len])) { } /* look for smaller element */
if (right >= len) {
break;
} /* partition point found? */
std_iter_swap(array, right, len); /* the only swap */
} /* partitioned, continue left part */
}
if (pos === 0) {
break;
} /* stack empty? */
left = len; /* left to right is sorted */
len = stack[--pos]; /* get next range to sort */
}
return array;
}
function std_stable_sort(array, first, len, cmp) {
if (first === void 0) { first = 0; }
if (len === void 0) { len = array.length - first; }
if (cmp === void 0) { cmp = default_compare; }
return std_sort(array, first, len, cmp);
}
function std_remove_if(array, predicate, length) {
if (length === void 0) { length = array.length; }
var l = 0;
for (var c = 0; c < length; ++c) {
// if we can be collapsed, keep l where it is.
if (predicate(array[c])) {
continue;
}
// this node can't be collapsed; push it back as far as we can.
if (c === l) {
++l;
continue; // quick exit if we're already in the right spot
}
// array[l++] = array[c];
std_iter_swap(array, l++, c);
}
return l;
}
function std_lower_bound(array, first, last, val, cmp) {
if (cmp === void 0) { cmp = default_compare; }
var count = last - first;
while (count > 0) {
var step = Math.floor(count / 2);
var it = first + step;
if (cmp(array[it], val)) {
first = ++it;
count -= step + 1;
}
else {
count = step;
}
}
return first;
}
function std_upper_bound(array, first, last, val, cmp) {
if (cmp === void 0) { cmp = default_compare; }
var count = last - first;
while (count > 0) {
var step = Math.floor(count / 2);
var it = first + step;
if (!cmp(val, array[it])) {
first = ++it;
count -= step + 1;
}
else {
count = step;
}
}
return first;
}
function std_rotate(array, first, n_first, last) {
var next = n_first;
while (first !== next) {
std_iter_swap(array, first++, next++);
if (next === last) {
next = n_first;
}
else if (first === n_first) {
n_first = next;
}
}
}
function std_unique(array, first, last, cmp) {
if (first === last) {
return last;
}
var result = first;
while (++first !== last) {
if (!cmp(array[result], array[first])) {
///array[++result] = array[first];
std_iter_swap(array, ++result, first);
}
}
return ++result;
}
var b2GrowableBuffer = /** @class */ (function () {
function b2GrowableBuffer(allocator) {
this.data = [];
this.count = 0;
this.capacity = 0;
this.allocator = allocator;
}
b2GrowableBuffer.prototype.Append = function () {
if (this.count >= this.capacity) {
this.Grow();
}
return this.count++;
};
b2GrowableBuffer.prototype.Reserve = function (newCapacity) {
if (this.capacity >= newCapacity) {
return;
}
// DEBUG: b2Assert(this.capacity === this.data.length);
for (var i = this.capacity; i < newCapacity; ++i) {
this.data[i] = this.allocator();
}
this.capacity = newCapacity;
};
b2GrowableBuffer.prototype.Grow = function () {
// Double the capacity.
var newCapacity = this.capacity ? 2 * this.capacity : b2_minParticleSystemBufferCapacity;
// DEBUG: b2Assert(newCapacity > this.capacity);
this.Reserve(newCapacity);
};
b2GrowableBuffer.prototype.Free = function () {
if (this.data.length === 0) {
return;
}
this.data = [];
this.capacity = 0;
this.count = 0;
};
b2GrowableBuffer.prototype.Shorten = function (newEnd) {
// DEBUG: b2Assert(false);
};
b2GrowableBuffer.prototype.Data = function () {
return this.data;
};
b2GrowableBuffer.prototype.GetCount = function () {
return this.count;
};
b2GrowableBuffer.prototype.SetCount = function (newCount) {
// DEBUG: b2Assert(0 <= newCount && newCount <= this.capacity);
this.count = newCount;
};
b2GrowableBuffer.prototype.GetCapacity = function () {
return this.capacity;
};
b2GrowableBuffer.prototype.RemoveIf = function (pred) {
// DEBUG: let count = 0;
// DEBUG: for (let i = 0; i < this.count; ++i) {
// DEBUG: if (!pred(this.data[i])) {
// DEBUG: count++;
// DEBUG: }
// DEBUG: }
this.count = std_remove_if(this.data, pred, this.count);
// DEBUG: b2Assert(count === this.count);
};
b2GrowableBuffer.prototype.Unique = function (pred) {
this.count = std_unique(this.data, 0, this.count, pred);
};
return b2GrowableBuffer;
}());
var b2FixtureParticleQueryCallback = /** @class */ (function (_super) {
__extends(b2FixtureParticleQueryCallback, _super);
function b2FixtureParticleQueryCallback(system) {
var _this = _super.call(this) || this;
_this.m_system = system;
return _this;
}
b2FixtureParticleQueryCallback.prototype.ShouldQueryParticleSystem = function (system) {
// Skip reporting particles.
return false;
};
b2FixtureParticleQueryCallback.prototype.ReportFixture = function (fixture) {
if (fixture.IsSensor()) {
return true;
}
var shape = fixture.GetShape();
var childCount = shape.GetChildCount();
for (var childIndex = 0; childIndex < childCount; childIndex++) {
var aabb = fixture.GetAABB(childIndex);
var enumerator = this.m_system.GetInsideBoundsEnumerator(aabb);
var index = void 0;
while ((index = enumerator.GetNext()) >= 0) {
this.ReportFixtureAndParticle(fixture, childIndex, index);
}
}
return true;
};
b2FixtureParticleQueryCallback.prototype.ReportParticle = function (system, index) {
return false;
};
b2FixtureParticleQueryCallback.prototype.ReportFixtureAndParticle = function (fixture, childIndex, index) {
// DEBUG: b2Assert(false); // pure virtual
};
return b2FixtureParticleQueryCallback;
}(b2QueryCallback));
var b2ParticleContact = /** @class */ (function () {
function b2ParticleContact() {
this.indexA = 0;
this.indexB = 0;
this.weight = 0;
this.normal = new b2Vec2();
this.flags = 0;
}
b2ParticleContact.prototype.SetIndices = function (a, b) {
// DEBUG: b2Assert(a <= b2_maxParticleIndex && b <= b2_maxParticleIndex);
this.indexA = a;
this.indexB = b;
};
b2ParticleContact.prototype.SetWeight = function (w) {
this.weight = w;
};
b2ParticleContact.prototype.SetNormal = function (n) {
this.normal.Copy(n);
};
b2ParticleContact.prototype.SetFlags = function (f) {
this.flags = f;
};
b2ParticleContact.prototype.GetIndexA = function () {
return this.indexA;
};
b2ParticleContact.prototype.GetIndexB = function () {
return this.indexB;
};
b2ParticleContact.prototype.GetWeight = function () {
return this.weight;
};
b2ParticleContact.prototype.GetNormal = function () {
return this.normal;
};
b2ParticleContact.prototype.GetFlags = function () {
return this.flags;
};
b2ParticleContact.prototype.IsEqual = function (rhs) {
return this.indexA === rhs.indexA && this.indexB === rhs.indexB && this.flags === rhs.flags && this.weight === rhs.weight && this.normal.x === rhs.normal.x && this.normal.y === rhs.normal.y;
};
b2ParticleContact.prototype.IsNotEqual = function (rhs) {
return !this.IsEqual(rhs);
};
b2ParticleContact.prototype.ApproximatelyEqual = function (rhs) {
var MAX_WEIGHT_DIFF = 0.01; // Weight 0 ~ 1, so about 1%
var MAX_NORMAL_DIFF_SQ = 0.01 * 0.01; // Normal length = 1, so 1%
return this.indexA === rhs.indexA && this.indexB === rhs.indexB && this.flags === rhs.flags && b2Abs(this.weight - rhs.weight) < MAX_WEIGHT_DIFF && b2Vec2.DistanceSquaredVV(this.normal, rhs.normal) < MAX_NORMAL_DIFF_SQ;
};
return b2ParticleContact;
}());
var b2ParticleBodyContact = /** @class */ (function () {
function b2ParticleBodyContact() {
this.index = 0; // Index of the particle making contact.
this.weight = 0.0; // Weight of the contact. A value between 0.0f and 1.0f.
this.normal = new b2Vec2(); // The normalized direction from the particle to the body.
this.mass = 0.0; // The effective mass used in calculating force.
}
return b2ParticleBodyContact;
}());
var b2ParticlePair = /** @class */ (function () {
function b2ParticlePair() {
this.indexA = 0; // Indices of the respective particles making pair.
this.indexB = 0;
this.flags = 0; // The logical sum of the particle flags. See the b2ParticleFlag enum.
this.strength = 0.0; // The strength of cohesion among the particles.
this.distance = 0.0; // The initial distance of the particles.
}
return b2ParticlePair;
}());
var b2ParticleTriad = /** @class */ (function () {
function b2ParticleTriad() {
this.indexA = 0; // Indices of the respective particles making triad.
this.indexB = 0;
this.indexC = 0;
this.flags = 0; // The logical sum of the particle flags. See the b2ParticleFlag enum.
this.strength = 0.0; // The strength of cohesion among the particles.
this.pa = new b2Vec2(0.0, 0.0); // Values used for calculation.
this.pb = new b2Vec2(0.0, 0.0);
this.pc = new b2Vec2(0.0, 0.0);
this.ka = 0.0;
this.kb = 0.0;
this.kc = 0.0;
this.s = 0.0;
}
return b2ParticleTriad;
}());
var b2ParticleSystemDef = /** @class */ (function () {
function b2ParticleSystemDef() {
// Initialize physical coefficients to the maximum values that
// maintain numerical stability.
/**
* Enable strict Particle/Body contact check.
* See SetStrictContactCheck for details.
*/
this.strictContactCheck = false;
/**
* Set the particle density.
* See SetDensity for details.
*/
this.density = 1.0;
/**
* Change the particle gravity scale. Adjusts the effect of the
* global gravity vector on particles. Default value is 1.0f.
*/
this.gravityScale = 1.0;
/**
* Particles behave as circles with this radius. In Box2D units.
*/
this.radius = 1.0;
/**
* Set the maximum number of particles.
* By default, there is no maximum. The particle buffers can
* continue to grow while b2World's block allocator still has
* memory.
* See SetMaxParticleCount for details.
*/
this.maxCount = 0;
/**
* Increases pressure in response to compression
* Smaller values allow more compression
*/
this.pressureStrength = 0.005;
/**
* Reduces velocity along the collision normal
* Smaller value reduces less
*/
this.dampingStrength = 1.0;
/**
* Restores shape of elastic particle groups
* Larger values increase elastic particle velocity
*/
this.elasticStrength = 0.25;
/**
* Restores length of spring particle groups
* Larger values increase spring particle velocity
*/
this.springStrength = 0.25;
/**
* Reduces relative velocity of viscous particles
* Larger values slow down viscous particles more
*/
this.viscousStrength = 0.25;
/**
* Produces pressure on tensile particles
* 0~0.2. Larger values increase the amount of surface tension.
*/
this.surfaceTensionPressureStrength = 0.2;
/**
* Smoothes outline of tensile particles
* 0~0.2. Larger values result in rounder, smoother,
* water-drop-like clusters of particles.
*/
this.surfaceTensionNormalStrength = 0.2;
/**
* Produces additional pressure on repulsive particles
* Larger values repulse more
* Negative values mean attraction. The range where particles
* behave stably is about -0.2 to 2.0.
*/
this.repulsiveStrength = 1.0;
/**
* Produces repulsion between powder particles
* Larger values repulse more
*/
this.powderStrength = 0.5;
/**
* Pushes particles out of solid particle group
* Larger values repulse more
*/
this.ejectionStrength = 0.5;
/**
* Produces static pressure
* Larger values increase the pressure on neighboring partilces
* For a description of static pressure, see
* http://en.wikipedia.org/wiki/Static_pressure#Static_pressure_in_fluid_dynamics
*/
this.staticPressureStrength = 0.2;
/**
* Reduces instability in static pressure calculation
* Larger values make stabilize static pressure with fewer
* iterations
*/
this.staticPressureRelaxation = 0.2;
/**
* Computes static pressure more precisely
* See SetStaticPressureIterations for details
*/
this.staticPressureIterations = 8;
/**
* Determines how fast colors are mixed
* 1.0f ==> mixed immediately
* 0.5f ==> mixed half way each simulation step (see
* b2World::Step())
*/
this.colorMixingStrength = 0.5;
/**
* Whether to destroy particles by age when no more particles
* can be created. See #b2ParticleSystem::SetDestructionByAge()
* for more information.
*/
this.destroyByAge = true;
/**
* Granularity of particle lifetimes in seconds. By default
* this is set to (1.0f / 60.0f) seconds. b2ParticleSystem uses
* a 32-bit signed value to track particle lifetimes so the
* maximum lifetime of a particle is (2^32 - 1) / (1.0f /
* lifetimeGranularity) seconds. With the value set to 1/60 the
* maximum lifetime or age of a particle is 2.27 years.
*/
this.lifetimeGranularity = 1.0 / 60.0;
}
b2ParticleSystemDef.prototype.Copy = function (def) {
this.strictContactCheck = def.strictContactCheck;
this.density = def.density;
this.gravityScale = def.gravityScale;
this.radius = def.radius;
this.maxCount = def.maxCount;
this.pressureStrength = def.pressureStrength;
this.dampingStrength = def.dampingStrength;
this.elasticStrength = def.elasticStrength;
this.springStrength = def.springStrength;
this.viscousStrength = def.viscousStrength;
this.surfaceTensionPressureStrength = def.surfaceTensionPressureStrength;
this.surfaceTensionNormalStrength = def.surfaceTensionNormalStrength;
this.repulsiveStrength = def.repulsiveStrength;
this.powderStrength = def.powderStrength;
this.ejectionStrength = def.ejectionStrength;
this.staticPressureStrength = def.staticPressureStrength;
this.staticPressureRelaxation = def.staticPressureRelaxation;
this.staticPressureIterations = def.staticPressureIterations;
this.colorMixingStrength = def.colorMixingStrength;
this.destroyByAge = def.destroyByAge;
this.lifetimeGranularity = def.lifetimeGranularity;
return this;
};
b2ParticleSystemDef.prototype.Clone = function () {
return new b2ParticleSystemDef().Copy(this);
};
return b2ParticleSystemDef;
}());
exports.b2ParticleSystem = /** @class */ (function () {
function b2ParticleSystem(def, world) {
this.m_paused = false;
this.m_timestamp = 0;
this.m_allParticleFlags = 0;
this.m_needsUpdateAllParticleFlags = false;
this.m_allGroupFlags = 0;
this.m_needsUpdateAllGroupFlags = false;
this.m_hasForce = false;
this.m_iterationIndex = 0;
this.m_inverseDensity = 0.0;
this.m_particleDiameter = 0.0;
this.m_inverseDiameter = 0.0;
this.m_squaredDiameter = 0.0;
this.m_count = 0;
this.m_internalAllocatedCapacity = 0;
/**
* Allocator for b2ParticleHandle instances.
*/
///m_handleAllocator: any = null;
/**
* Maps particle indicies to handles.
*/
this.m_handleIndexBuffer = new b2ParticleSystem.UserOverridableBuffer();
this.m_flagsBuffer = new b2ParticleSystem.UserOverridableBuffer();
this.m_positionBuffer = new b2ParticleSystem.UserOverridableBuffer();
this.m_velocityBuffer = new b2ParticleSystem.UserOverridableBuffer();
this.m_forceBuffer = [];
/**
* this.m_weightBuffer is populated in ComputeWeight and used in
* ComputeDepth(), SolveStaticPressure() and SolvePressure().
*/
this.m_weightBuffer = [];
/**
* When any particles have the flag b2_staticPressureParticle,
* this.m_staticPressureBuffer is first allocated and used in
* SolveStaticPressure() and SolvePressure(). It will be
* reallocated on subsequent CreateParticle() calls.
*/
this.m_staticPressureBuffer = [];
/**
* this.m_accumulationBuffer is used in many functions as a temporary
* buffer for scalar values.
*/
this.m_accumulationBuffer = [];
/**
* When any particles have the flag b2_tensileParticle,
* this.m_accumulation2Buffer is first allocated and used in
* SolveTensile() as a temporary buffer for vector values. It
* will be reallocated on subsequent CreateParticle() calls.
*/
this.m_accumulation2Buffer = [];
/**
* When any particle groups have the flag b2_solidParticleGroup,
* this.m_depthBuffer is first allocated and populated in
* ComputeDepth() and used in SolveSolid(). It will be
* reallocated on subsequent CreateParticle() calls.
*/
this.m_depthBuffer = [];
this.m_colorBuffer = new b2ParticleSystem.UserOverridableBuffer();
this.m_groupBuffer = [];
this.m_userDataBuffer = new b2ParticleSystem.UserOverridableBuffer();
/**
* Stuck particle detection parameters and record keeping
*/
this.m_stuckThreshold = 0;
this.m_lastBodyContactStepBuffer = new b2ParticleSystem.UserOverridableBuffer();
this.m_bodyContactCountBuffer = new b2ParticleSystem.UserOverridableBuffer();
this.m_consecutiveContactStepsBuffer = new b2ParticleSystem.UserOverridableBuffer();
this.m_stuckParticleBuffer = new b2GrowableBuffer(function () { return 0; });
this.m_proxyBuffer = new b2GrowableBuffer(function () { return new b2ParticleSystem.Proxy(); });
this.m_contactBuffer = new b2GrowableBuffer(function () { return new b2ParticleContact(); });
this.m_bodyContactBuffer = new b2GrowableBuffer(function () { return new b2ParticleBodyContact(); });
this.m_pairBuffer = new b2GrowableBuffer(function () { return new b2ParticlePair(); });
this.m_triadBuffer = new b2GrowableBuffer(function () { return new b2ParticleTriad(); });
/**
* Time each particle should be destroyed relative to the last
* time this.m_timeElapsed was initialized. Each unit of time
* corresponds to b2ParticleSystemDef::lifetimeGranularity
* seconds.
*/
this.m_expirationTimeBuffer = new b2ParticleSystem.UserOverridableBuffer();
/**
* List of particle indices sorted by expiration time.
*/
this.m_indexByExpirationTimeBuffer = new b2ParticleSystem.UserOverridableBuffer();
/**
* Time elapsed in 32:32 fixed point. Each non-fractional unit
* of time corresponds to
* b2ParticleSystemDef::lifetimeGranularity seconds.
*/
this.m_timeElapsed = 0;
/**
* Whether the expiration time buffer has been modified and
* needs to be resorted.
*/
this.m_expirationTimeBufferRequiresSorting = false;
this.m_groupCount = 0;
this.m_groupList = null;
this.m_def = new b2ParticleSystemDef();
this.m_prev = null;
this.m_next = null;
this.SetStrictContactCheck(def.strictContactCheck);
this.SetDensity(def.density);
this.SetGravityScale(def.gravityScale);
this.SetRadius(def.radius);
this.SetMaxParticleCount(def.maxCount);
// DEBUG: b2Assert(def.lifetimeGranularity > 0.0);
this.m_def = def.Clone();
this.m_world = world;
this.SetDestructionByAge(this.m_def.destroyByAge);
}
b2ParticleSystem.computeTag = function (x, y) {
///return ((uint32)(y + yOffset) << yShift) + (uint32)(xScale * x + xOffset);
return ((((y + b2ParticleSystem.yOffset) >>> 0) << b2ParticleSystem.yShift) + ((b2ParticleSystem.xScale * x + b2ParticleSystem.xOffset) >>> 0)) >>> 0;
};
b2ParticleSystem.computeRelativeTag = function (tag, x, y) {
///return tag + (y << yShift) + (x << xShift);
return (tag + (y << b2ParticleSystem.yShift) + (x << b2ParticleSystem.xShift)) >>> 0;
};
b2ParticleSystem.prototype.Drop = function () {
while (this.m_groupList) {
this.DestroyParticleGroup(this.m_groupList);
}
this.FreeUserOverridableBuffer(this.m_handleIndexBuffer);
this.FreeUserOverridableBuffer(this.m_flagsBuffer);
this.FreeUserOverridableBuffer(this.m_lastBodyContactStepBuffer);
this.FreeUserOverridableBuffer(this.m_bodyContactCountBuffer);
this.FreeUserOverridableBuffer(this.m_consecutiveContactStepsBuffer);
this.FreeUserOverridableBuffer(this.m_positionBuffer);
this.FreeUserOverridableBuffer(this.m_velocityBuffer);
this.FreeUserOverridableBuffer(this.m_colorBuffer);
this.FreeUserOverridableBuffer(this.m_userDataBuffer);
this.FreeUserOverridableBuffer(this.m_expirationTimeBuffer);
this.FreeUserOverridableBuffer(this.m_indexByExpirationTimeBuffer);
this.FreeBuffer(this.m_forceBuffer, this.m_internalAllocatedCapacity);
this.FreeBuffer(this.m_weightBuffer, this.m_internalAllocatedCapacity);
this.FreeBuffer(this.m_staticPressureBuffer, this.m_internalAllocatedCapacity);
this.FreeBuffer(this.m_accumulationBuffer, this.m_internalAllocatedCapacity);
this.FreeBuffer(this.m_accumulation2Buffer, this.m_internalAllocatedCapacity);
this.FreeBuffer(this.m_depthBuffer, this.m_internalAllocatedCapacity);
this.FreeBuffer(this.m_groupBuffer, this.m_internalAllocatedCapacity);
};
/**
* Create a particle whose properties have been defined.
*
* No reference to the definition is retained.
*
* A simulation step must occur before it's possible to interact
* with a newly created particle. For example,
* DestroyParticleInShape() will not destroy a particle until
* b2World::Step() has been called.
*
* warning: This function is locked during callbacks.
*/
b2ParticleSystem.prototype.CreateParticle = function (def) {
if (this.m_world.IsLocked()) {
throw new Error();
}
if (this.m_count >= this.m_internalAllocatedCapacity) {
// Double the particle capacity.
var capacity = this.m_count ? 2 * this.m_count : b2_minParticleSystemBufferCapacity;
this.ReallocateInternalAllocatedBuffers(capacity);
}
if (this.m_count >= this.m_internalAllocatedCapacity) {
// If the oldest particle should be destroyed...
if (this.m_def.destroyByAge) {
this.DestroyOldestParticle(0, false);
// Need to destroy this particle *now* so that it's possible to
// create a new particle.
this.SolveZombie();
}
else {
return b2_invalidParticleIndex;
}
}
var index = this.m_count++;
if (!this.m_flagsBuffer.data) {
throw new Error();
}
this.m_flagsBuffer.data[index] = 0;
if (this.m_lastBodyContactStepBuffer.data) {
this.m_lastBodyContactStepBuffer.data[index] = 0;
}
if (this.m_bodyContactCountBuffer.data) {
this.m_bodyContactCountBuffer.data[index] = 0;
}
if (this.m_consecutiveContactStepsBuffer.data) {
this.m_consecutiveContactStepsBuffer.data[index] = 0;
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
this.m_positionBuffer.data[index] = (this.m_positionBuffer.data[index] || new b2Vec2()).Copy(b2Maybe(def.position, b2Vec2.ZERO));
this.m_velocityBuffer.data[index] = (this.m_velocityBuffer.data[index] || new b2Vec2()).Copy(b2Maybe(def.velocity, b2Vec2.ZERO));
this.m_weightBuffer[index] = 0;
this.m_forceBuffer[index] = (this.m_forceBuffer[index] || new b2Vec2()).SetZero();
if (this.m_staticPressureBuffer) {
this.m_staticPressureBuffer[index] = 0;
}
if (this.m_depthBuffer) {
this.m_depthBuffer[index] = 0;
}
var color = new b2Color().Copy(b2Maybe(def.color, b2Color.ZERO));
if (this.m_colorBuffer.data || !color.IsZero()) {
this.m_colorBuffer.data = this.RequestBuffer(this.m_colorBuffer.data);
this.m_colorBuffer.data[index] = (this.m_colorBuffer.data[index] || new b2Color()).Copy(color);
}
if (this.m_userDataBuffer.data || def.userData) {
this.m_userDataBuffer.data = this.RequestBuffer(this.m_userDataBuffer.data);
this.m_userDataBuffer.data[index] = def.userData;
}
if (this.m_handleIndexBuffer.data) {
this.m_handleIndexBuffer.data[index] = null;
}
///Proxy& proxy = m_proxyBuffer.Append();
var proxy = this.m_proxyBuffer.data[this.m_proxyBuffer.Append()];
// If particle lifetimes are enabled or the lifetime is set in the particle
// definition, initialize the lifetime.
var lifetime = b2Maybe(def.lifetime, 0.0);
var finiteLifetime = lifetime > 0.0;
if (this.m_expirationTimeBuffer.data || finiteLifetime) {
this.SetParticleLifetime(index, finiteLifetime ? lifetime :
this.ExpirationTimeToLifetime(-this.GetQuantizedTimeElapsed()));
// Add a reference to the newly added particle to the end of the
// queue.
if (!this.m_indexByExpirationTimeBuffer.data) {
throw new Error();
}
this.m_indexByExpirationTimeBuffer.data[index] = index;
}
proxy.index = index;
var group = b2Maybe(def.group, null);
this.m_groupBuffer[index] = group;
if (group) {
if (group.m_firstIndex < group.m_lastIndex) {
// Move particles in the group just before the new particle.
this.RotateBuffer(group.m_firstIndex, group.m_lastIndex, index);
// DEBUG: b2Assert(group.m_lastIndex === index);
// Update the index range of the group to contain the new particle.
group.m_lastIndex = index + 1;
}
else {
// If the group is empty, reset the index range to contain only the
// new particle.
group.m_firstIndex = index;
group.m_lastIndex = index + 1;
}
}
this.SetParticleFlags(index, b2Maybe(def.flags, 0));
return index;
};
/**
* Retrieve a handle to the particle at the specified index.
*
* Please see #b2ParticleHandle for why you might want a handle.
*/
b2ParticleSystem.prototype.GetParticleHandleFromIndex = function (index) {
// DEBUG: b2Assert(index >= 0 && index < this.GetParticleCount() && index !== b2_invalidParticleIndex);
this.m_handleIndexBuffer.data = this.RequestBuffer(this.m_handleIndexBuffer.data);
var handle = this.m_handleIndexBuffer.data[index];
if (handle) {
return handle;
}
// Create a handle.
///handle = m_handleAllocator.Allocate();
handle = new b2ParticleHandle();
// DEBUG: b2Assert(handle !== null);
handle.SetIndex(index);
this.m_handleIndexBuffer.data[index] = handle;
return handle;
};
/**
* Destroy a particle.
*
* The particle is removed after the next simulation step (see
* b2World::Step()).
*
* @param index Index of the particle to destroy.
* @param callDestructionListener Whether to call the
* destruction listener just before the particle is
* destroyed.
*/
b2ParticleSystem.prototype.DestroyParticle = function (index, callDestructionListener) {
if (callDestructionListener === void 0) { callDestructionListener = false; }
if (!this.m_flagsBuffer.data) {
throw new Error();
}
var flags = exports.b2ParticleFlag.b2_zombieParticle;
if (callDestructionListener) {
flags |= exports.b2ParticleFlag.b2_destructionListenerParticle;
}
this.SetParticleFlags(index, this.m_flagsBuffer.data[index] | flags);
};
/**
* Destroy the Nth oldest particle in the system.
*
* The particle is removed after the next b2World::Step().
*
* @param index Index of the Nth oldest particle to
* destroy, 0 will destroy the oldest particle in the
* system, 1 will destroy the next oldest particle etc.
* @param callDestructionListener Whether to call the
* destruction listener just before the particle is
* destroyed.
*/
b2ParticleSystem.prototype.DestroyOldestParticle = function (index, callDestructionListener) {
if (callDestructionListener === void 0) { callDestructionListener = false; }
var particleCount = this.GetParticleCount();
// DEBUG: b2Assert(index >= 0 && index < particleCount);
// Make sure particle lifetime tracking is enabled.
// DEBUG: b2Assert(this.m_indexByExpirationTimeBuffer.data !== null);
if (!this.m_indexByExpirationTimeBuffer.data) {
throw new Error();
}
if (!this.m_expirationTimeBuffer.data) {
throw new Error();
}
// Destroy the oldest particle (preferring to destroy finite
// lifetime particles first) to free a slot in the buffer.
var oldestFiniteLifetimeParticle = this.m_indexByExpirationTimeBuffer.data[particleCount - (index + 1)];
var oldestInfiniteLifetimeParticle = this.m_indexByExpirationTimeBuffer.data[index];
this.DestroyParticle(this.m_expirationTimeBuffer.data[oldestFiniteLifetimeParticle] > 0.0 ?
oldestFiniteLifetimeParticle : oldestInfiniteLifetimeParticle, callDestructionListener);
};
/**
* Destroy particles inside a shape.
*
* warning: This function is locked during callbacks.
*
* In addition, this function immediately destroys particles in
* the shape in constrast to DestroyParticle() which defers the
* destruction until the next simulation step.
*
* @return Number of particles destroyed.
* @param shape Shape which encloses particles
* that should be destroyed.
* @param xf Transform applied to the shape.
* @param callDestructionListener Whether to call the
* world b2DestructionListener for each particle
* destroyed.
*/
b2ParticleSystem.prototype.DestroyParticlesInShape = function (shape, xf, callDestructionListener) {
if (callDestructionListener === void 0) { callDestructionListener = false; }
var s_aabb = b2ParticleSystem.DestroyParticlesInShape_s_aabb;
if (this.m_world.IsLocked()) {
throw new Error();
}
var callback = new b2ParticleSystem.DestroyParticlesInShapeCallback(this, shape, xf, callDestructionListener);
var aabb = s_aabb;
shape.ComputeAABB(aabb, xf, 0);
this.m_world.QueryAABB(callback, aabb);
return callback.Destroyed();
};
/**
* Create a particle group whose properties have been defined.
*
* No reference to the definition is retained.
*
* warning: This function is locked during callbacks.
*/
b2ParticleSystem.prototype.CreateParticleGroup = function (groupDef) {
var s_transform = b2ParticleSystem.CreateParticleGroup_s_transform;
if (this.m_world.IsLocked()) {
throw new Error();
}
var transform = s_transform;
transform.SetPositionAngle(b2Maybe(groupDef.position, b2Vec2.ZERO), b2Maybe(groupDef.angle, 0));
var firstIndex = this.m_count;
if (groupDef.shape) {
this.CreateParticlesWithShapeForGroup(groupDef.shape, groupDef, transform);
}
if (groupDef.shapes) {
this.CreateParticlesWithShapesForGroup(groupDef.shapes, b2Maybe(groupDef.shapeCount, groupDef.shapes.length), groupDef, transform);
}
if (groupDef.positionData) {
var count = b2Maybe(groupDef.particleCount, groupDef.positionData.length);
for (var i = 0; i < count; i++) {
var p = groupDef.positionData[i];
this.CreateParticleForGroup(groupDef, transform, p);
}
}
var lastIndex = this.m_count;
var group = new b2ParticleGroup(this);
group.m_firstIndex = firstIndex;
group.m_lastIndex = lastIndex;
group.m_strength = b2Maybe(groupDef.strength, 1);
group.m_userData = groupDef.userData;
group.m_transform.Copy(transform);
group.m_prev = null;
group.m_next = this.m_groupList;
if (this.m_groupList) {
this.m_groupList.m_prev = group;
}
this.m_groupList = group;
++this.m_groupCount;
for (var i = firstIndex; i < lastIndex; i++) {
this.m_groupBuffer[i] = group;
}
this.SetGroupFlags(group, b2Maybe(groupDef.groupFlags, 0));
// Create pairs and triads between particles in the group.
var filter = new b2ParticleSystem.ConnectionFilter();
this.UpdateContacts(true);
this.UpdatePairsAndTriads(firstIndex, lastIndex, filter);
if (groupDef.group) {
this.JoinParticleGroups(groupDef.group, group);
group = groupDef.group;
}
return group;
};
/**
* Join two particle groups.
*
* warning: This function is locked during callbacks.
*
* @param groupA the first group. Expands to encompass the second group.
* @param groupB the second group. It is destroyed.
*/
b2ParticleSystem.prototype.JoinParticleGroups = function (groupA, groupB) {
if (this.m_world.IsLocked()) {
throw new Error();
}
// DEBUG: b2Assert(groupA !== groupB);
this.RotateBuffer(groupB.m_firstIndex, groupB.m_lastIndex, this.m_count);
// DEBUG: b2Assert(groupB.m_lastIndex === this.m_count);
this.RotateBuffer(groupA.m_firstIndex, groupA.m_lastIndex, groupB.m_firstIndex);
// DEBUG: b2Assert(groupA.m_lastIndex === groupB.m_firstIndex);
// Create pairs and triads connecting groupA and groupB.
var filter = new b2ParticleSystem.JoinParticleGroupsFilter(groupB.m_firstIndex);
this.UpdateContacts(true);
this.UpdatePairsAndTriads(groupA.m_firstIndex, groupB.m_lastIndex, filter);
for (var i = groupB.m_firstIndex; i < groupB.m_lastIndex; i++) {
this.m_groupBuffer[i] = groupA;
}
var groupFlags = groupA.m_groupFlags | groupB.m_groupFlags;
this.SetGroupFlags(groupA, groupFlags);
groupA.m_lastIndex = groupB.m_lastIndex;
groupB.m_firstIndex = groupB.m_lastIndex;
this.DestroyParticleGroup(groupB);
};
/**
* Split particle group into multiple disconnected groups.
*
* warning: This function is locked during callbacks.
*
* @param group the group to be split.
*/
b2ParticleSystem.prototype.SplitParticleGroup = function (group) {
this.UpdateContacts(true);
var particleCount = group.GetParticleCount();
// We create several linked lists. Each list represents a set of connected particles.
///ParticleListNode* nodeBuffer = (ParticleListNode*) m_world.m_stackAllocator.Allocate(sizeof(ParticleListNode) * particleCount);
var nodeBuffer = b2MakeArray(particleCount, function (index) { return new b2ParticleSystem.ParticleListNode(); });
b2ParticleSystem.InitializeParticleLists(group, nodeBuffer);
this.MergeParticleListsInContact(group, nodeBuffer);
var survivingList = b2ParticleSystem.FindLongestParticleList(group, nodeBuffer);
this.MergeZombieParticleListNodes(group, nodeBuffer, survivingList);
this.CreateParticleGroupsFromParticleList(group, nodeBuffer, survivingList);
this.UpdatePairsAndTriadsWithParticleList(group, nodeBuffer);
///this.m_world.m_stackAllocator.Free(nodeBuffer);
};
/**
* Get the world particle group list. With the returned group,
* use b2ParticleGroup::GetNext to get the next group in the
* world list.
*
* A null group indicates the end of the list.
*
* @return the head of the world particle group list.
*/
b2ParticleSystem.prototype.GetParticleGroupList = function () {
return this.m_groupList;
};
/**
* Get the number of particle groups.
*/
b2ParticleSystem.prototype.GetParticleGroupCount = function () {
return this.m_groupCount;
};
/**
* Get the number of particles.
*/
b2ParticleSystem.prototype.GetParticleCount = function () {
return this.m_count;
};
/**
* Get the maximum number of particles.
*/
b2ParticleSystem.prototype.GetMaxParticleCount = function () {
return this.m_def.maxCount;
};
/**
* Set the maximum number of particles.
*
* A value of 0 means there is no maximum. The particle buffers
* can continue to grow while b2World's block allocator still
* has memory.
*
* Note: If you try to CreateParticle() with more than this
* count, b2_invalidParticleIndex is returned unless
* SetDestructionByAge() is used to enable the destruction of
* the oldest particles in the system.
*/
b2ParticleSystem.prototype.SetMaxParticleCount = function (count) {
// DEBUG: b2Assert(this.m_count <= count);
this.m_def.maxCount = count;
};
/**
* Get all existing particle flags.
*/
b2ParticleSystem.prototype.GetAllParticleFlags = function () {
return this.m_allParticleFlags;
};
/**
* Get all existing particle group flags.
*/
b2ParticleSystem.prototype.GetAllGroupFlags = function () {
return this.m_allGroupFlags;
};
/**
* Pause or unpause the particle system. When paused,
* b2World::Step() skips over this particle system. All
* b2ParticleSystem function calls still work.
*
* @param paused paused is true to pause, false to un-pause.
*/
b2ParticleSystem.prototype.SetPaused = function (paused) {
this.m_paused = paused;
};
/**
* Initially, true, then, the last value passed into
* SetPaused().
*
* @return true if the particle system is being updated in b2World::Step().
*/
b2ParticleSystem.prototype.GetPaused = function () {
return this.m_paused;
};
/**
* Change the particle density.
*
* Particle density affects the mass of the particles, which in
* turn affects how the particles interact with b2Bodies. Note
* that the density does not affect how the particles interact
* with each other.
*/
b2ParticleSystem.prototype.SetDensity = function (density) {
this.m_def.density = density;
this.m_inverseDensity = 1 / this.m_def.density;
};
/**
* Get the particle density.
*/
b2ParticleSystem.prototype.GetDensity = function () {
return this.m_def.density;
};
/**
* Change the particle gravity scale. Adjusts the effect of the
* global gravity vector on particles.
*/
b2ParticleSystem.prototype.SetGravityScale = function (gravityScale) {
this.m_def.gravityScale = gravityScale;
};
/**
* Get the particle gravity scale.
*/
b2ParticleSystem.prototype.GetGravityScale = function () {
return this.m_def.gravityScale;
};
/**
* Damping is used to reduce the velocity of particles. The
* damping parameter can be larger than 1.0f but the damping
* effect becomes sensitive to the time step when the damping
* parameter is large.
*/
b2ParticleSystem.prototype.SetDamping = function (damping) {
this.m_def.dampingStrength = damping;
};
/**
* Get damping for particles
*/
b2ParticleSystem.prototype.GetDamping = function () {
return this.m_def.dampingStrength;
};
/**
* Change the number of iterations when calculating the static
* pressure of particles. By default, 8 iterations. You can
* reduce the number of iterations down to 1 in some situations,
* but this may cause instabilities when many particles come
* together. If you see particles popping away from each other
* like popcorn, you may have to increase the number of
* iterations.
*
* For a description of static pressure, see
* http://en.wikipedia.org/wiki/Static_pressure#Static_pressure_in_fluid_dynamics
*/
b2ParticleSystem.prototype.SetStaticPressureIterations = function (iterations) {
this.m_def.staticPressureIterations = iterations;
};
/**
* Get the number of iterations for static pressure of
* particles.
*/
b2ParticleSystem.prototype.GetStaticPressureIterations = function () {
return this.m_def.staticPressureIterations;
};
/**
* Change the particle radius.
*
* You should set this only once, on world start.
* If you change the radius during execution, existing particles
* may explode, shrink, or behave unexpectedly.
*/
b2ParticleSystem.prototype.SetRadius = function (radius) {
this.m_particleDiameter = 2 * radius;
this.m_squaredDiameter = this.m_particleDiameter * this.m_particleDiameter;
this.m_inverseDiameter = 1 / this.m_particleDiameter;
};
/**
* Get the particle radius.
*/
b2ParticleSystem.prototype.GetRadius = function () {
return this.m_particleDiameter / 2;
};
/**
* Get the position of each particle
*
* Array is length GetParticleCount()
*
* @return the pointer to the head of the particle positions array.
*/
b2ParticleSystem.prototype.GetPositionBuffer = function () {
if (!this.m_positionBuffer.data) {
throw new Error();
}
return this.m_positionBuffer.data;
};
/**
* Get the velocity of each particle
*
* Array is length GetParticleCount()
*
* @return the pointer to the head of the particle velocities array.
*/
b2ParticleSystem.prototype.GetVelocityBuffer = function () {
if (!this.m_velocityBuffer.data) {
throw new Error();
}
return this.m_velocityBuffer.data;
};
/**
* Get the color of each particle
*
* Array is length GetParticleCount()
*
* @return the pointer to the head of the particle colors array.
*/
b2ParticleSystem.prototype.GetColorBuffer = function () {
this.m_colorBuffer.data = this.RequestBuffer(this.m_colorBuffer.data);
return this.m_colorBuffer.data;
};
/**
* Get the particle-group of each particle.
*
* Array is length GetParticleCount()
*
* @return the pointer to the head of the particle group array.
*/
b2ParticleSystem.prototype.GetGroupBuffer = function () {
return this.m_groupBuffer;
};
/**
* Get the weight of each particle
*
* Array is length GetParticleCount()
*
* @return the pointer to the head of the particle positions array.
*/
b2ParticleSystem.prototype.GetWeightBuffer = function () {
return this.m_weightBuffer;
};
/**
* Get the user-specified data of each particle.
*
* Array is length GetParticleCount()
*
* @return the pointer to the head of the particle user-data array.
*/
b2ParticleSystem.prototype.GetUserDataBuffer = function () {
this.m_userDataBuffer.data = this.RequestBuffer(this.m_userDataBuffer.data);
return this.m_userDataBuffer.data;
};
/**
* Get the flags for each particle. See the b2ParticleFlag enum.
*
* Array is length GetParticleCount()
*
* @return the pointer to the head of the particle-flags array.
*/
b2ParticleSystem.prototype.GetFlagsBuffer = function () {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
return this.m_flagsBuffer.data;
};
/**
* Set flags for a particle. See the b2ParticleFlag enum.
*/
b2ParticleSystem.prototype.SetParticleFlags = function (index, newFlags) {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
var oldFlags = this.m_flagsBuffer.data[index];
if (oldFlags & ~newFlags) {
// If any flags might be removed
this.m_needsUpdateAllParticleFlags = true;
}
if (~this.m_allParticleFlags & newFlags) {
// If any flags were added
if (newFlags & exports.b2ParticleFlag.b2_tensileParticle) {
this.m_accumulation2Buffer = this.RequestBuffer(this.m_accumulation2Buffer);
}
if (newFlags & exports.b2ParticleFlag.b2_colorMixingParticle) {
this.m_colorBuffer.data = this.RequestBuffer(this.m_colorBuffer.data);
}
this.m_allParticleFlags |= newFlags;
}
this.m_flagsBuffer.data[index] = newFlags;
};
/**
* Get flags for a particle. See the b2ParticleFlag enum.
*/
b2ParticleSystem.prototype.GetParticleFlags = function (index) {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
return this.m_flagsBuffer.data[index];
};
/**
* Set an external buffer for particle data.
*
* Normally, the b2World's block allocator is used for particle
* data. However, sometimes you may have an OpenGL or Java
* buffer for particle data. To avoid data duplication, you may
* supply this external buffer.
*
* Note that, when b2World's block allocator is used, the
* particle data buffers can grow as required. However, when
* external buffers are used, the maximum number of particles is
* clamped to the size of the smallest external buffer.
*
* @param buffer a pointer to a block of memory.
* @param capacity the number of values in the block.
*/
b2ParticleSystem.prototype.SetFlagsBuffer = function (buffer, capacity) {
this.SetUserOverridableBuffer(this.m_flagsBuffer, buffer, capacity);
};
b2ParticleSystem.prototype.SetPositionBuffer = function (buffer, capacity) {
///if (buffer instanceof Float32Array) {
///let array = [];
///for (let i = 0; i < capacity; ++i) {
/// array[i] = new b2Vec2(buffer.subarray(i * 2, i * 2 + 2));
///}
///this.SetUserOverridableBuffer(this.m_positionBuffer, array, capacity);
///} else {
this.SetUserOverridableBuffer(this.m_positionBuffer, buffer, capacity);
///}
};
b2ParticleSystem.prototype.SetVelocityBuffer = function (buffer, capacity) {
///if (buffer instanceof Float32Array) {
///let array = [];
///for (let i = 0; i < capacity; ++i) {
/// array[i] = new b2Vec2(buffer.subarray(i * 2, i * 2 + 2));
///}
///this.SetUserOverridableBuffer(this.m_velocityBuffer, array, capacity);
///} else {
this.SetUserOverridableBuffer(this.m_velocityBuffer, buffer, capacity);
///}
};
b2ParticleSystem.prototype.SetColorBuffer = function (buffer, capacity) {
///if (buffer instanceof Uint8Array) {
///let array: b2Color[] = [];
///for (let i = 0; i < capacity; ++i) {
/// array[i] = new b2Color(buffer.subarray(i * 4, i * 4 + 4));
///}
///this.SetUserOverridableBuffer(this.m_colorBuffer, array, capacity);
///} else {
this.SetUserOverridableBuffer(this.m_colorBuffer, buffer, capacity);
///}
};
b2ParticleSystem.prototype.SetUserDataBuffer = function (buffer, capacity) {
this.SetUserOverridableBuffer(this.m_userDataBuffer, buffer, capacity);
};
/**
* Get contacts between particles
* Contact data can be used for many reasons, for example to
* trigger rendering or audio effects.
*/
b2ParticleSystem.prototype.GetContacts = function () {
return this.m_contactBuffer.data;
};
b2ParticleSystem.prototype.GetContactCount = function () {
return this.m_contactBuffer.count;
};
/**
* Get contacts between particles and bodies
*
* Contact data can be used for many reasons, for example to
* trigger rendering or audio effects.
*/
b2ParticleSystem.prototype.GetBodyContacts = function () {
return this.m_bodyContactBuffer.data;
};
b2ParticleSystem.prototype.GetBodyContactCount = function () {
return this.m_bodyContactBuffer.count;
};
/**
* Get array of particle pairs. The particles in a pair:
* (1) are contacting,
* (2) are in the same particle group,
* (3) are part of a rigid particle group, or are spring, elastic,
* or wall particles.
* (4) have at least one particle that is a spring or barrier
* particle (i.e. one of the types in k_pairFlags),
* (5) have at least one particle that returns true for
* ConnectionFilter::IsNecessary,
* (6) are not zombie particles.
*
* Essentially, this is an array of spring or barrier particles
* that are interacting. The array is sorted by b2ParticlePair's
* indexA, and then indexB. There are no duplicate entries.
*/
b2ParticleSystem.prototype.GetPairs = function () {
return this.m_pairBuffer.data;
};
b2ParticleSystem.prototype.GetPairCount = function () {
return this.m_pairBuffer.count;
};
/**
* Get array of particle triads. The particles in a triad:
* (1) are in the same particle group,
* (2) are in a Voronoi triangle together,
* (3) are within b2_maxTriadDistance particle diameters of each
* other,
* (4) return true for ConnectionFilter::ShouldCreateTriad
* (5) have at least one particle of type elastic (i.e. one of the
* types in k_triadFlags),
* (6) are part of a rigid particle group, or are spring, elastic,
* or wall particles.
* (7) are not zombie particles.
*
* Essentially, this is an array of elastic particles that are
* interacting. The array is sorted by b2ParticleTriad's indexA,
* then indexB, then indexC. There are no duplicate entries.
*/
b2ParticleSystem.prototype.GetTriads = function () {
return this.m_triadBuffer.data;
};
b2ParticleSystem.prototype.GetTriadCount = function () {
return this.m_triadBuffer.count;
};
/**
* Set an optional threshold for the maximum number of
* consecutive particle iterations that a particle may contact
* multiple bodies before it is considered a candidate for being
* "stuck". Setting to zero or less disables.
*/
b2ParticleSystem.prototype.SetStuckThreshold = function (steps) {
this.m_stuckThreshold = steps;
if (steps > 0) {
this.m_lastBodyContactStepBuffer.data = this.RequestBuffer(this.m_lastBodyContactStepBuffer.data);
this.m_bodyContactCountBuffer.data = this.RequestBuffer(this.m_bodyContactCountBuffer.data);
this.m_consecutiveContactStepsBuffer.data = this.RequestBuffer(this.m_consecutiveContactStepsBuffer.data);
}
};
/**
* Get potentially stuck particles from the last step; the user
* must decide if they are stuck or not, and if so, delete or
* move them
*/
b2ParticleSystem.prototype.GetStuckCandidates = function () {
///return m_stuckParticleBuffer.Data();
return this.m_stuckParticleBuffer.Data();
};
/**
* Get the number of stuck particle candidates from the last
* step.
*/
b2ParticleSystem.prototype.GetStuckCandidateCount = function () {
///return m_stuckParticleBuffer.GetCount();
return this.m_stuckParticleBuffer.GetCount();
};
/**
* Compute the kinetic energy that can be lost by damping force
*/
b2ParticleSystem.prototype.ComputeCollisionEnergy = function () {
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var s_v = b2ParticleSystem.ComputeCollisionEnergy_s_v;
var vel_data = this.m_velocityBuffer.data;
var sum_v2 = 0;
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
var a = contact.indexA;
var b = contact.indexB;
var n = contact.normal;
///b2Vec2 v = m_velocityBuffer.data[b] - m_velocityBuffer.data[a];
var v = b2Vec2.SubVV(vel_data[b], vel_data[a], s_v);
var vn = b2Vec2.DotVV(v, n);
if (vn < 0) {
sum_v2 += vn * vn;
}
}
return 0.5 * this.GetParticleMass() * sum_v2;
};
/**
* Set strict Particle/Body contact check.
*
* This is an option that will help ensure correct behavior if
* there are corners in the world model where Particle/Body
* contact is ambiguous. This option scales at n*log(n) of the
* number of Particle/Body contacts, so it is best to only
* enable if it is necessary for your geometry. Enable if you
* see strange particle behavior around b2Body intersections.
*/
b2ParticleSystem.prototype.SetStrictContactCheck = function (enabled) {
this.m_def.strictContactCheck = enabled;
};
/**
* Get the status of the strict contact check.
*/
b2ParticleSystem.prototype.GetStrictContactCheck = function () {
return this.m_def.strictContactCheck;
};
/**
* Set the lifetime (in seconds) of a particle relative to the
* current time. A lifetime of less than or equal to 0.0f
* results in the particle living forever until it's manually
* destroyed by the application.
*/
b2ParticleSystem.prototype.SetParticleLifetime = function (index, lifetime) {
// DEBUG: b2Assert(this.ValidateParticleIndex(index));
var initializeExpirationTimes = this.m_indexByExpirationTimeBuffer.data === null;
this.m_expirationTimeBuffer.data = this.RequestBuffer(this.m_expirationTimeBuffer.data);
this.m_indexByExpirationTimeBuffer.data = this.RequestBuffer(this.m_indexByExpirationTimeBuffer.data);
// Initialize the inverse mapping buffer.
if (initializeExpirationTimes) {
var particleCount = this.GetParticleCount();
for (var i = 0; i < particleCount; ++i) {
this.m_indexByExpirationTimeBuffer.data[i] = i;
}
}
///const int32 quantizedLifetime = (int32)(lifetime / m_def.lifetimeGranularity);
var quantizedLifetime = lifetime / this.m_def.lifetimeGranularity;
// Use a negative lifetime so that it's possible to track which
// of the infinite lifetime particles are older.
var newExpirationTime = quantizedLifetime > 0.0 ? this.GetQuantizedTimeElapsed() + quantizedLifetime : quantizedLifetime;
if (newExpirationTime !== this.m_expirationTimeBuffer.data[index]) {
this.m_expirationTimeBuffer.data[index] = newExpirationTime;
this.m_expirationTimeBufferRequiresSorting = true;
}
};
/**
* Get the lifetime (in seconds) of a particle relative to the
* current time. A value > 0.0f is returned if the particle is
* scheduled to be destroyed in the future, values <= 0.0f
* indicate the particle has an infinite lifetime.
*/
b2ParticleSystem.prototype.GetParticleLifetime = function (index) {
// DEBUG: b2Assert(this.ValidateParticleIndex(index));
return this.ExpirationTimeToLifetime(this.GetExpirationTimeBuffer()[index]);
};
/**
* Enable / disable destruction of particles in CreateParticle()
* when no more particles can be created due to a prior call to
* SetMaxParticleCount(). When this is enabled, the oldest
* particle is destroyed in CreateParticle() favoring the
* destruction of particles with a finite lifetime over
* particles with infinite lifetimes. This feature is enabled by
* default when particle lifetimes are tracked. Explicitly
* enabling this feature using this function enables particle
* lifetime tracking.
*/
b2ParticleSystem.prototype.SetDestructionByAge = function (enable) {
if (enable) {
this.GetExpirationTimeBuffer();
}
this.m_def.destroyByAge = enable;
};
/**
* Get whether the oldest particle will be destroyed in
* CreateParticle() when the maximum number of particles are
* present in the system.
*/
b2ParticleSystem.prototype.GetDestructionByAge = function () {
return this.m_def.destroyByAge;
};
/**
* Get the array of particle expiration times indexed by
* particle index.
*
* GetParticleCount() items are in the returned array.
*/
b2ParticleSystem.prototype.GetExpirationTimeBuffer = function () {
this.m_expirationTimeBuffer.data = this.RequestBuffer(this.m_expirationTimeBuffer.data);
return this.m_expirationTimeBuffer.data;
};
/**
* Convert a expiration time value in returned by
* GetExpirationTimeBuffer() to a time in seconds relative to
* the current simulation time.
*/
b2ParticleSystem.prototype.ExpirationTimeToLifetime = function (expirationTime) {
return (expirationTime > 0 ?
expirationTime - this.GetQuantizedTimeElapsed() :
expirationTime) * this.m_def.lifetimeGranularity;
};
/**
* Get the array of particle indices ordered by reverse
* lifetime. The oldest particle indexes are at the end of the
* array with the newest at the start. Particles with infinite
* lifetimes (i.e expiration times less than or equal to 0) are
* placed at the start of the array.
* ExpirationTimeToLifetime(GetExpirationTimeBuffer()[index]) is
* equivalent to GetParticleLifetime(index).
*
* GetParticleCount() items are in the returned array.
*/
b2ParticleSystem.prototype.GetIndexByExpirationTimeBuffer = function () {
// If particles are present, initialize / reinitialize the lifetime buffer.
if (this.GetParticleCount()) {
this.SetParticleLifetime(0, this.GetParticleLifetime(0));
}
else {
this.m_indexByExpirationTimeBuffer.data = this.RequestBuffer(this.m_indexByExpirationTimeBuffer.data);
}
if (!this.m_indexByExpirationTimeBuffer.data) {
throw new Error();
}
return this.m_indexByExpirationTimeBuffer.data;
};
/**
* Apply an impulse to one particle. This immediately modifies
* the velocity. Similar to b2Body::ApplyLinearImpulse.
*
* @param index the particle that will be modified.
* @param impulse impulse the world impulse vector, usually in N-seconds or kg-m/s.
*/
b2ParticleSystem.prototype.ParticleApplyLinearImpulse = function (index, impulse) {
this.ApplyLinearImpulse(index, index + 1, impulse);
};
/**
* Apply an impulse to all particles between 'firstIndex' and
* 'lastIndex'. This immediately modifies the velocity. Note
* that the impulse is applied to the total mass of all
* particles. So, calling ParticleApplyLinearImpulse(0, impulse)
* and ParticleApplyLinearImpulse(1, impulse) will impart twice
* as much velocity as calling just ApplyLinearImpulse(0, 1,
* impulse).
*
* @param firstIndex the first particle to be modified.
* @param lastIndex the last particle to be modified.
* @param impulse the world impulse vector, usually in N-seconds or kg-m/s.
*/
b2ParticleSystem.prototype.ApplyLinearImpulse = function (firstIndex, lastIndex, impulse) {
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var vel_data = this.m_velocityBuffer.data;
var numParticles = (lastIndex - firstIndex);
var totalMass = numParticles * this.GetParticleMass();
///const b2Vec2 velocityDelta = impulse / totalMass;
var velocityDelta = new b2Vec2().Copy(impulse).SelfMul(1 / totalMass);
for (var i = firstIndex; i < lastIndex; i++) {
///m_velocityBuffer.data[i] += velocityDelta;
vel_data[i].SelfAdd(velocityDelta);
}
};
b2ParticleSystem.IsSignificantForce = function (force) {
return force.x !== 0 || force.y !== 0;
};
/**
* Apply a force to the center of a particle.
*
* @param index the particle that will be modified.
* @param force the world force vector, usually in Newtons (N).
*/
b2ParticleSystem.prototype.ParticleApplyForce = function (index, force) {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (b2ParticleSystem.IsSignificantForce(force) &&
this.ForceCanBeApplied(this.m_flagsBuffer.data[index])) {
this.PrepareForceBuffer();
///m_forceBuffer[index] += force;
this.m_forceBuffer[index].SelfAdd(force);
}
};
/**
* Distribute a force across several particles. The particles
* must not be wall particles. Note that the force is
* distributed across all the particles, so calling this
* function for indices 0..N is not the same as calling
* ParticleApplyForce(i, force) for i in 0..N.
*
* @param firstIndex the first particle to be modified.
* @param lastIndex the last particle to be modified.
* @param force the world force vector, usually in Newtons (N).
*/
b2ParticleSystem.prototype.ApplyForce = function (firstIndex, lastIndex, force) {
// Ensure we're not trying to apply force to particles that can't move,
// such as wall particles.
// DEBUG: if (!this.m_flagsBuffer.data) { throw new Error(); }
// DEBUG: let flags = 0;
// DEBUG: for (let i = firstIndex; i < lastIndex; i++) {
// DEBUG: flags |= this.m_flagsBuffer.data[i];
// DEBUG: }
// DEBUG: b2Assert(this.ForceCanBeApplied(flags));
// Early out if force does nothing (optimization).
///const b2Vec2 distributedForce = force / (float32)(lastIndex - firstIndex);
var distributedForce = new b2Vec2().Copy(force).SelfMul(1 / (lastIndex - firstIndex));
if (b2ParticleSystem.IsSignificantForce(distributedForce)) {
this.PrepareForceBuffer();
// Distribute the force over all the particles.
for (var i = firstIndex; i < lastIndex; i++) {
///m_forceBuffer[i] += distributedForce;
this.m_forceBuffer[i].SelfAdd(distributedForce);
}
}
};
/**
* Get the next particle-system in the world's particle-system
* list.
*/
b2ParticleSystem.prototype.GetNext = function () {
return this.m_next;
};
/**
* Query the particle system for all particles that potentially
* overlap the provided AABB.
* b2QueryCallback::ShouldQueryParticleSystem is ignored.
*
* @param callback a user implemented callback class.
* @param aabb the query box.
*/
b2ParticleSystem.prototype.QueryAABB = function (callback, aabb) {
if (this.m_proxyBuffer.count === 0) {
return;
}
var beginProxy = 0;
var endProxy = this.m_proxyBuffer.count;
var firstProxy = std_lower_bound(this.m_proxyBuffer.data, beginProxy, endProxy, b2ParticleSystem.computeTag(this.m_inverseDiameter * aabb.lowerBound.x, this.m_inverseDiameter * aabb.lowerBound.y), b2ParticleSystem.Proxy.CompareProxyTag);
var lastProxy = std_upper_bound(this.m_proxyBuffer.data, firstProxy, endProxy, b2ParticleSystem.computeTag(this.m_inverseDiameter * aabb.upperBound.x, this.m_inverseDiameter * aabb.upperBound.y), b2ParticleSystem.Proxy.CompareTagProxy);
if (!this.m_positionBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
for (var k = firstProxy; k < lastProxy; ++k) {
var proxy = this.m_proxyBuffer.data[k];
var i = proxy.index;
var p = pos_data[i];
if (aabb.lowerBound.x < p.x && p.x < aabb.upperBound.x &&
aabb.lowerBound.y < p.y && p.y < aabb.upperBound.y) {
if (!callback.ReportParticle(this, i)) {
break;
}
}
}
};
/**
* Query the particle system for all particles that potentially
* overlap the provided shape's AABB. Calls QueryAABB
* internally. b2QueryCallback::ShouldQueryParticleSystem is
* ignored.
*
* @param callback a user implemented callback class.
* @param shape the query shape
* @param xf the transform of the AABB
* @param childIndex
*/
b2ParticleSystem.prototype.QueryShapeAABB = function (callback, shape, xf, childIndex) {
if (childIndex === void 0) { childIndex = 0; }
var s_aabb = b2ParticleSystem.QueryShapeAABB_s_aabb;
var aabb = s_aabb;
shape.ComputeAABB(aabb, xf, childIndex);
this.QueryAABB(callback, aabb);
};
b2ParticleSystem.prototype.QueryPointAABB = function (callback, point, slop) {
if (slop === void 0) { slop = b2_linearSlop; }
var s_aabb = b2ParticleSystem.QueryPointAABB_s_aabb;
var aabb = s_aabb;
aabb.lowerBound.Set(point.x - slop, point.y - slop);
aabb.upperBound.Set(point.x + slop, point.y + slop);
this.QueryAABB(callback, aabb);
};
/**
* Ray-cast the particle system for all particles in the path of
* the ray. Your callback controls whether you get the closest
* point, any point, or n-points. The ray-cast ignores particles
* that contain the starting point.
* b2RayCastCallback::ShouldQueryParticleSystem is ignored.
*
* @param callback a user implemented callback class.
* @param point1 the ray starting point
* @param point2 the ray ending point
*/
b2ParticleSystem.prototype.RayCast = function (callback, point1, point2) {
var s_aabb = b2ParticleSystem.RayCast_s_aabb;
var s_p = b2ParticleSystem.RayCast_s_p;
var s_v = b2ParticleSystem.RayCast_s_v;
var s_n = b2ParticleSystem.RayCast_s_n;
var s_point = b2ParticleSystem.RayCast_s_point;
if (this.m_proxyBuffer.count === 0) {
return;
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var aabb = s_aabb;
b2Vec2.MinV(point1, point2, aabb.lowerBound);
b2Vec2.MaxV(point1, point2, aabb.upperBound);
var fraction = 1;
// solving the following equation:
// ((1-t)*point1+t*point2-position)^2=diameter^2
// where t is a potential fraction
///b2Vec2 v = point2 - point1;
var v = b2Vec2.SubVV(point2, point1, s_v);
var v2 = b2Vec2.DotVV(v, v);
var enumerator = this.GetInsideBoundsEnumerator(aabb);
var i;
while ((i = enumerator.GetNext()) >= 0) {
///b2Vec2 p = point1 - m_positionBuffer.data[i];
var p = b2Vec2.SubVV(point1, pos_data[i], s_p);
var pv = b2Vec2.DotVV(p, v);
var p2 = b2Vec2.DotVV(p, p);
var determinant = pv * pv - v2 * (p2 - this.m_squaredDiameter);
if (determinant >= 0) {
var sqrtDeterminant = b2Sqrt(determinant);
// find a solution between 0 and fraction
var t = (-pv - sqrtDeterminant) / v2;
if (t > fraction) {
continue;
}
if (t < 0) {
t = (-pv + sqrtDeterminant) / v2;
if (t < 0 || t > fraction) {
continue;
}
}
///b2Vec2 n = p + t * v;
var n = b2Vec2.AddVMulSV(p, t, v, s_n);
n.Normalize();
///float32 f = callback.ReportParticle(this, i, point1 + t * v, n, t);
var f = callback.ReportParticle(this, i, b2Vec2.AddVMulSV(point1, t, v, s_point), n, t);
fraction = b2Min(fraction, f);
if (fraction <= 0) {
break;
}
}
}
};
/**
* Compute the axis-aligned bounding box for all particles
* contained within this particle system.
* @param aabb Returns the axis-aligned bounding box of the system.
*/
b2ParticleSystem.prototype.ComputeAABB = function (aabb) {
var particleCount = this.GetParticleCount();
// DEBUG: b2Assert(aabb !== null);
aabb.lowerBound.x = +b2_maxFloat;
aabb.lowerBound.y = +b2_maxFloat;
aabb.upperBound.x = -b2_maxFloat;
aabb.upperBound.y = -b2_maxFloat;
if (!this.m_positionBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
for (var i = 0; i < particleCount; i++) {
var p = pos_data[i];
b2Vec2.MinV(aabb.lowerBound, p, aabb.lowerBound);
b2Vec2.MaxV(aabb.upperBound, p, aabb.upperBound);
}
aabb.lowerBound.x -= this.m_particleDiameter;
aabb.lowerBound.y -= this.m_particleDiameter;
aabb.upperBound.x += this.m_particleDiameter;
aabb.upperBound.y += this.m_particleDiameter;
};
b2ParticleSystem.prototype.FreeBuffer = function (b, capacity) {
if (b === null) {
return;
}
b.length = 0;
};
b2ParticleSystem.prototype.FreeUserOverridableBuffer = function (b) {
if (b.userSuppliedCapacity === 0) {
this.FreeBuffer(b.data, this.m_internalAllocatedCapacity);
}
};
/**
* Reallocate a buffer
*/
b2ParticleSystem.prototype.ReallocateBuffer3 = function (oldBuffer, oldCapacity, newCapacity) {
// b2Assert(newCapacity > oldCapacity);
if (newCapacity <= oldCapacity) {
throw new Error();
}
var newBuffer = (oldBuffer) ? oldBuffer.slice() : [];
newBuffer.length = newCapacity;
return newBuffer;
};
/**
* Reallocate a buffer
*/
b2ParticleSystem.prototype.ReallocateBuffer5 = function (buffer, userSuppliedCapacity, oldCapacity, newCapacity, deferred) {
// b2Assert(newCapacity > oldCapacity);
if (newCapacity <= oldCapacity) {
throw new Error();
}
// A 'deferred' buffer is reallocated only if it is not NULL.
// If 'userSuppliedCapacity' is not zero, buffer is user supplied and must
// be kept.
// b2Assert(!userSuppliedCapacity || newCapacity <= userSuppliedCapacity);
if (!(!userSuppliedCapacity || newCapacity <= userSuppliedCapacity)) {
throw new Error();
}
if ((!deferred || buffer) && !userSuppliedCapacity) {
buffer = this.ReallocateBuffer3(buffer, oldCapacity, newCapacity);
}
return buffer; // TODO: fix this
};
/**
* Reallocate a buffer
*/
b2ParticleSystem.prototype.ReallocateBuffer4 = function (buffer, oldCapacity, newCapacity, deferred) {
// DEBUG: b2Assert(newCapacity > oldCapacity);
return this.ReallocateBuffer5(buffer.data, buffer.userSuppliedCapacity, oldCapacity, newCapacity, deferred);
};
b2ParticleSystem.prototype.RequestBuffer = function (buffer) {
if (!buffer) {
if (this.m_internalAllocatedCapacity === 0) {
this.ReallocateInternalAllocatedBuffers(b2_minParticleSystemBufferCapacity);
}
buffer = [];
buffer.length = this.m_internalAllocatedCapacity;
}
return buffer;
};
/**
* Reallocate the handle / index map and schedule the allocation
* of a new pool for handle allocation.
*/
b2ParticleSystem.prototype.ReallocateHandleBuffers = function (newCapacity) {
// DEBUG: b2Assert(newCapacity > this.m_internalAllocatedCapacity);
// Reallocate a new handle / index map buffer, copying old handle pointers
// is fine since they're kept around.
this.m_handleIndexBuffer.data = this.ReallocateBuffer4(this.m_handleIndexBuffer, this.m_internalAllocatedCapacity, newCapacity, true);
// Set the size of the next handle allocation.
///this.m_handleAllocator.SetItemsPerSlab(newCapacity - this.m_internalAllocatedCapacity);
};
b2ParticleSystem.prototype.ReallocateInternalAllocatedBuffers = function (capacity) {
function LimitCapacity(capacity, maxCount) {
return maxCount && capacity > maxCount ? maxCount : capacity;
}
// Don't increase capacity beyond the smallest user-supplied buffer size.
capacity = LimitCapacity(capacity, this.m_def.maxCount);
capacity = LimitCapacity(capacity, this.m_flagsBuffer.userSuppliedCapacity);
capacity = LimitCapacity(capacity, this.m_positionBuffer.userSuppliedCapacity);
capacity = LimitCapacity(capacity, this.m_velocityBuffer.userSuppliedCapacity);
capacity = LimitCapacity(capacity, this.m_colorBuffer.userSuppliedCapacity);
capacity = LimitCapacity(capacity, this.m_userDataBuffer.userSuppliedCapacity);
if (this.m_internalAllocatedCapacity < capacity) {
this.ReallocateHandleBuffers(capacity);
this.m_flagsBuffer.data = this.ReallocateBuffer4(this.m_flagsBuffer, this.m_internalAllocatedCapacity, capacity, false);
// Conditionally defer these as they are optional if the feature is
// not enabled.
var stuck = this.m_stuckThreshold > 0;
this.m_lastBodyContactStepBuffer.data = this.ReallocateBuffer4(this.m_lastBodyContactStepBuffer, this.m_internalAllocatedCapacity, capacity, stuck);
this.m_bodyContactCountBuffer.data = this.ReallocateBuffer4(this.m_bodyContactCountBuffer, this.m_internalAllocatedCapacity, capacity, stuck);
this.m_consecutiveContactStepsBuffer.data = this.ReallocateBuffer4(this.m_consecutiveContactStepsBuffer, this.m_internalAllocatedCapacity, capacity, stuck);
this.m_positionBuffer.data = this.ReallocateBuffer4(this.m_positionBuffer, this.m_internalAllocatedCapacity, capacity, false);
this.m_velocityBuffer.data = this.ReallocateBuffer4(this.m_velocityBuffer, this.m_internalAllocatedCapacity, capacity, false);
this.m_forceBuffer = this.ReallocateBuffer5(this.m_forceBuffer, 0, this.m_internalAllocatedCapacity, capacity, false);
this.m_weightBuffer = this.ReallocateBuffer5(this.m_weightBuffer, 0, this.m_internalAllocatedCapacity, capacity, false);
this.m_staticPressureBuffer = this.ReallocateBuffer5(this.m_staticPressureBuffer, 0, this.m_internalAllocatedCapacity, capacity, true);
this.m_accumulationBuffer = this.ReallocateBuffer5(this.m_accumulationBuffer, 0, this.m_internalAllocatedCapacity, capacity, false);
this.m_accumulation2Buffer = this.ReallocateBuffer5(this.m_accumulation2Buffer, 0, this.m_internalAllocatedCapacity, capacity, true);
this.m_depthBuffer = this.ReallocateBuffer5(this.m_depthBuffer, 0, this.m_internalAllocatedCapacity, capacity, true);
this.m_colorBuffer.data = this.ReallocateBuffer4(this.m_colorBuffer, this.m_internalAllocatedCapacity, capacity, true);
this.m_groupBuffer = this.ReallocateBuffer5(this.m_groupBuffer, 0, this.m_internalAllocatedCapacity, capacity, false);
this.m_userDataBuffer.data = this.ReallocateBuffer4(this.m_userDataBuffer, this.m_internalAllocatedCapacity, capacity, true);
this.m_expirationTimeBuffer.data = this.ReallocateBuffer4(this.m_expirationTimeBuffer, this.m_internalAllocatedCapacity, capacity, true);
this.m_indexByExpirationTimeBuffer.data = this.ReallocateBuffer4(this.m_indexByExpirationTimeBuffer, this.m_internalAllocatedCapacity, capacity, false);
this.m_internalAllocatedCapacity = capacity;
}
};
b2ParticleSystem.prototype.CreateParticleForGroup = function (groupDef, xf, p) {
var particleDef = new b2ParticleDef();
particleDef.flags = b2Maybe(groupDef.flags, 0);
///particleDef.position = b2Mul(xf, p);
b2Transform.MulXV(xf, p, particleDef.position);
///particleDef.velocity =
/// groupDef.linearVelocity +
/// b2Cross(groupDef.angularVelocity,
/// particleDef.position - groupDef.position);
b2Vec2.AddVV(b2Maybe(groupDef.linearVelocity, b2Vec2.ZERO), b2Vec2.CrossSV(b2Maybe(groupDef.angularVelocity, 0), b2Vec2.SubVV(particleDef.position, b2Maybe(groupDef.position, b2Vec2.ZERO), b2Vec2.s_t0), b2Vec2.s_t0), particleDef.velocity);
particleDef.color.Copy(b2Maybe(groupDef.color, b2Color.ZERO));
particleDef.lifetime = b2Maybe(groupDef.lifetime, 0);
particleDef.userData = groupDef.userData;
this.CreateParticle(particleDef);
};
b2ParticleSystem.prototype.CreateParticlesStrokeShapeForGroup = function (shape, groupDef, xf) {
var s_edge = b2ParticleSystem.CreateParticlesStrokeShapeForGroup_s_edge;
var s_d = b2ParticleSystem.CreateParticlesStrokeShapeForGroup_s_d;
var s_p = b2ParticleSystem.CreateParticlesStrokeShapeForGroup_s_p;
var stride = b2Maybe(groupDef.stride, 0);
if (stride === 0) {
stride = this.GetParticleStride();
}
var positionOnEdge = 0;
var childCount = shape.GetChildCount();
for (var childIndex = 0; childIndex < childCount; childIndex++) {
var edge = null;
if (shape.GetType() === exports.b2ShapeType.e_edgeShape) {
edge = shape;
}
else {
// DEBUG: b2Assert(shape.GetType() === b2ShapeType.e_chainShape);
edge = s_edge;
shape.GetChildEdge(edge, childIndex);
}
var d = b2Vec2.SubVV(edge.m_vertex2, edge.m_vertex1, s_d);
var edgeLength = d.Length();
while (positionOnEdge < edgeLength) {
///b2Vec2 p = edge.m_vertex1 + positionOnEdge / edgeLength * d;
var p = b2Vec2.AddVMulSV(edge.m_vertex1, positionOnEdge / edgeLength, d, s_p);
this.CreateParticleForGroup(groupDef, xf, p);
positionOnEdge += stride;
}
positionOnEdge -= edgeLength;
}
};
b2ParticleSystem.prototype.CreateParticlesFillShapeForGroup = function (shape, groupDef, xf) {
var s_aabb = b2ParticleSystem.CreateParticlesFillShapeForGroup_s_aabb;
var s_p = b2ParticleSystem.CreateParticlesFillShapeForGroup_s_p;
var stride = b2Maybe(groupDef.stride, 0);
if (stride === 0) {
stride = this.GetParticleStride();
}
///b2Transform identity;
/// identity.SetIdentity();
var identity = b2Transform.IDENTITY;
var aabb = s_aabb;
// DEBUG: b2Assert(shape.GetChildCount() === 1);
shape.ComputeAABB(aabb, identity, 0);
for (var y = Math.floor(aabb.lowerBound.y / stride) * stride; y < aabb.upperBound.y; y += stride) {
for (var x = Math.floor(aabb.lowerBound.x / stride) * stride; x < aabb.upperBound.x; x += stride) {
var p = s_p.Set(x, y);
if (shape.TestPoint(identity, p)) {
this.CreateParticleForGroup(groupDef, xf, p);
}
}
}
};
b2ParticleSystem.prototype.CreateParticlesWithShapeForGroup = function (shape, groupDef, xf) {
switch (shape.GetType()) {
case exports.b2ShapeType.e_edgeShape:
case exports.b2ShapeType.e_chainShape:
this.CreateParticlesStrokeShapeForGroup(shape, groupDef, xf);
break;
case exports.b2ShapeType.e_polygonShape:
case exports.b2ShapeType.e_circleShape:
this.CreateParticlesFillShapeForGroup(shape, groupDef, xf);
break;
default:
// DEBUG: b2Assert(false);
break;
}
};
b2ParticleSystem.prototype.CreateParticlesWithShapesForGroup = function (shapes, shapeCount, groupDef, xf) {
var compositeShape = new b2ParticleSystem.CompositeShape(shapes, shapeCount);
this.CreateParticlesFillShapeForGroup(compositeShape, groupDef, xf);
};
b2ParticleSystem.prototype.CloneParticle = function (oldIndex, group) {
var def = new b2ParticleDef();
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
def.flags = this.m_flagsBuffer.data[oldIndex];
def.position.Copy(this.m_positionBuffer.data[oldIndex]);
def.velocity.Copy(this.m_velocityBuffer.data[oldIndex]);
if (this.m_colorBuffer.data) {
def.color.Copy(this.m_colorBuffer.data[oldIndex]);
}
if (this.m_userDataBuffer.data) {
def.userData = this.m_userDataBuffer.data[oldIndex];
}
def.group = group;
var newIndex = this.CreateParticle(def);
if (this.m_handleIndexBuffer.data) {
var handle = this.m_handleIndexBuffer.data[oldIndex];
if (handle) {
handle.SetIndex(newIndex);
}
this.m_handleIndexBuffer.data[newIndex] = handle;
this.m_handleIndexBuffer.data[oldIndex] = null;
}
if (this.m_lastBodyContactStepBuffer.data) {
this.m_lastBodyContactStepBuffer.data[newIndex] =
this.m_lastBodyContactStepBuffer.data[oldIndex];
}
if (this.m_bodyContactCountBuffer.data) {
this.m_bodyContactCountBuffer.data[newIndex] =
this.m_bodyContactCountBuffer.data[oldIndex];
}
if (this.m_consecutiveContactStepsBuffer.data) {
this.m_consecutiveContactStepsBuffer.data[newIndex] =
this.m_consecutiveContactStepsBuffer.data[oldIndex];
}
if (this.m_hasForce) {
this.m_forceBuffer[newIndex].Copy(this.m_forceBuffer[oldIndex]);
}
if (this.m_staticPressureBuffer) {
this.m_staticPressureBuffer[newIndex] = this.m_staticPressureBuffer[oldIndex];
}
if (this.m_depthBuffer) {
this.m_depthBuffer[newIndex] = this.m_depthBuffer[oldIndex];
}
if (this.m_expirationTimeBuffer.data) {
this.m_expirationTimeBuffer.data[newIndex] =
this.m_expirationTimeBuffer.data[oldIndex];
}
return newIndex;
};
b2ParticleSystem.prototype.DestroyParticlesInGroup = function (group, callDestructionListener) {
if (callDestructionListener === void 0) { callDestructionListener = false; }
for (var i = group.m_firstIndex; i < group.m_lastIndex; i++) {
this.DestroyParticle(i, callDestructionListener);
}
};
b2ParticleSystem.prototype.DestroyParticleGroup = function (group) {
// DEBUG: b2Assert(this.m_groupCount > 0);
// DEBUG: b2Assert(group !== null);
if (this.m_world.m_destructionListener) {
this.m_world.m_destructionListener.SayGoodbyeParticleGroup(group);
}
this.SetGroupFlags(group, 0);
for (var i = group.m_firstIndex; i < group.m_lastIndex; i++) {
this.m_groupBuffer[i] = null;
}
if (group.m_prev) {
group.m_prev.m_next = group.m_next;
}
if (group.m_next) {
group.m_next.m_prev = group.m_prev;
}
if (group === this.m_groupList) {
this.m_groupList = group.m_next;
}
--this.m_groupCount;
};
b2ParticleSystem.ParticleCanBeConnected = function (flags, group) {
return ((flags & (exports.b2ParticleFlag.b2_wallParticle | exports.b2ParticleFlag.b2_springParticle | exports.b2ParticleFlag.b2_elasticParticle)) !== 0) ||
((group !== null) && ((group.GetGroupFlags() & exports.b2ParticleGroupFlag.b2_rigidParticleGroup) !== 0));
};
b2ParticleSystem.prototype.UpdatePairsAndTriads = function (firstIndex, lastIndex, filter) {
var s_dab = b2ParticleSystem.UpdatePairsAndTriads_s_dab;
var s_dbc = b2ParticleSystem.UpdatePairsAndTriads_s_dbc;
var s_dca = b2ParticleSystem.UpdatePairsAndTriads_s_dca;
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
// Create pairs or triads.
// All particles in each pair/triad should satisfy the following:
// * firstIndex <= index < lastIndex
// * don't have b2_zombieParticle
// * ParticleCanBeConnected returns true
// * ShouldCreatePair/ShouldCreateTriad returns true
// Any particles in each pair/triad should satisfy the following:
// * filter.IsNeeded returns true
// * have one of k_pairFlags/k_triadsFlags
// DEBUG: b2Assert(firstIndex <= lastIndex);
var particleFlags = 0;
for (var i = firstIndex; i < lastIndex; i++) {
particleFlags |= this.m_flagsBuffer.data[i];
}
if (particleFlags & b2ParticleSystem.k_pairFlags) {
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
var a = contact.indexA;
var b = contact.indexB;
var af = this.m_flagsBuffer.data[a];
var bf = this.m_flagsBuffer.data[b];
var groupA = this.m_groupBuffer[a];
var groupB = this.m_groupBuffer[b];
if (a >= firstIndex && a < lastIndex &&
b >= firstIndex && b < lastIndex &&
!((af | bf) & exports.b2ParticleFlag.b2_zombieParticle) &&
((af | bf) & b2ParticleSystem.k_pairFlags) &&
(filter.IsNecessary(a) || filter.IsNecessary(b)) &&
b2ParticleSystem.ParticleCanBeConnected(af, groupA) &&
b2ParticleSystem.ParticleCanBeConnected(bf, groupB) &&
filter.ShouldCreatePair(a, b)) {
///b2ParticlePair& pair = m_pairBuffer.Append();
var pair = this.m_pairBuffer.data[this.m_pairBuffer.Append()];
pair.indexA = a;
pair.indexB = b;
pair.flags = contact.flags;
pair.strength = b2Min(groupA ? groupA.m_strength : 1, groupB ? groupB.m_strength : 1);
///pair.distance = b2Distance(pos_data[a], pos_data[b]); // TODO: this was wrong!
pair.distance = b2Vec2.DistanceVV(pos_data[a], pos_data[b]);
}
///std::stable_sort(m_pairBuffer.Begin(), m_pairBuffer.End(), ComparePairIndices);
std_stable_sort(this.m_pairBuffer.data, 0, this.m_pairBuffer.count, b2ParticleSystem.ComparePairIndices);
///m_pairBuffer.Unique(MatchPairIndices);
this.m_pairBuffer.Unique(b2ParticleSystem.MatchPairIndices);
}
}
if (particleFlags & b2ParticleSystem.k_triadFlags) {
var diagram = new b2VoronoiDiagram(lastIndex - firstIndex);
///let necessary_count = 0;
for (var i = firstIndex; i < lastIndex; i++) {
var flags = this.m_flagsBuffer.data[i];
var group = this.m_groupBuffer[i];
if (!(flags & exports.b2ParticleFlag.b2_zombieParticle) &&
b2ParticleSystem.ParticleCanBeConnected(flags, group)) {
///if (filter.IsNecessary(i)) {
///++necessary_count;
///}
diagram.AddGenerator(pos_data[i], i, filter.IsNecessary(i));
}
}
///if (necessary_count === 0) {
/////debugger;
///for (let i = firstIndex; i < lastIndex; i++) {
/// filter.IsNecessary(i);
///}
///}
var stride = this.GetParticleStride();
diagram.Generate(stride / 2, stride * 2);
var system_1 = this;
var callback = /*UpdateTriadsCallback*/ function (a, b, c) {
if (!system_1.m_flagsBuffer.data) {
throw new Error();
}
var af = system_1.m_flagsBuffer.data[a];
var bf = system_1.m_flagsBuffer.data[b];
var cf = system_1.m_flagsBuffer.data[c];
if (((af | bf | cf) & b2ParticleSystem.k_triadFlags) &&
filter.ShouldCreateTriad(a, b, c)) {
var pa = pos_data[a];
var pb = pos_data[b];
var pc = pos_data[c];
var dab = b2Vec2.SubVV(pa, pb, s_dab);
var dbc = b2Vec2.SubVV(pb, pc, s_dbc);
var dca = b2Vec2.SubVV(pc, pa, s_dca);
var maxDistanceSquared = b2_maxTriadDistanceSquared * system_1.m_squaredDiameter;
if (b2Vec2.DotVV(dab, dab) > maxDistanceSquared ||
b2Vec2.DotVV(dbc, dbc) > maxDistanceSquared ||
b2Vec2.DotVV(dca, dca) > maxDistanceSquared) {
return;
}
var groupA = system_1.m_groupBuffer[a];
var groupB = system_1.m_groupBuffer[b];
var groupC = system_1.m_groupBuffer[c];
///b2ParticleTriad& triad = m_system.m_triadBuffer.Append();
var triad = system_1.m_triadBuffer.data[system_1.m_triadBuffer.Append()];
triad.indexA = a;
triad.indexB = b;
triad.indexC = c;
triad.flags = af | bf | cf;
triad.strength = b2Min(b2Min(groupA ? groupA.m_strength : 1, groupB ? groupB.m_strength : 1), groupC ? groupC.m_strength : 1);
///let midPoint = b2Vec2.MulSV(1.0 / 3.0, b2Vec2.AddVV(pa, b2Vec2.AddVV(pb, pc, new b2Vec2()), new b2Vec2()), new b2Vec2());
var midPoint_x = (pa.x + pb.x + pc.x) / 3.0;
var midPoint_y = (pa.y + pb.y + pc.y) / 3.0;
///triad.pa = b2Vec2.SubVV(pa, midPoint, new b2Vec2());
triad.pa.x = pa.x - midPoint_x;
triad.pa.y = pa.y - midPoint_y;
///triad.pb = b2Vec2.SubVV(pb, midPoint, new b2Vec2());
triad.pb.x = pb.x - midPoint_x;
triad.pb.y = pb.y - midPoint_y;
///triad.pc = b2Vec2.SubVV(pc, midPoint, new b2Vec2());
triad.pc.x = pc.x - midPoint_x;
triad.pc.y = pc.y - midPoint_y;
triad.ka = -b2Vec2.DotVV(dca, dab);
triad.kb = -b2Vec2.DotVV(dab, dbc);
triad.kc = -b2Vec2.DotVV(dbc, dca);
triad.s = b2Vec2.CrossVV(pa, pb) + b2Vec2.CrossVV(pb, pc) + b2Vec2.CrossVV(pc, pa);
}
};
diagram.GetNodes(callback);
///std::stable_sort(m_triadBuffer.Begin(), m_triadBuffer.End(), CompareTriadIndices);
std_stable_sort(this.m_triadBuffer.data, 0, this.m_triadBuffer.count, b2ParticleSystem.CompareTriadIndices);
///m_triadBuffer.Unique(MatchTriadIndices);
this.m_triadBuffer.Unique(b2ParticleSystem.MatchTriadIndices);
}
};
b2ParticleSystem.prototype.UpdatePairsAndTriadsWithReactiveParticles = function () {
var filter = new b2ParticleSystem.ReactiveFilter(this.m_flagsBuffer);
this.UpdatePairsAndTriads(0, this.m_count, filter);
if (!this.m_flagsBuffer.data) {
throw new Error();
}
for (var i = 0; i < this.m_count; i++) {
this.m_flagsBuffer.data[i] &= ~exports.b2ParticleFlag.b2_reactiveParticle;
}
this.m_allParticleFlags &= ~exports.b2ParticleFlag.b2_reactiveParticle;
};
b2ParticleSystem.ComparePairIndices = function (a, b) {
var diffA = a.indexA - b.indexA;
if (diffA !== 0) {
return diffA < 0;
}
return a.indexB < b.indexB;
};
b2ParticleSystem.MatchPairIndices = function (a, b) {
return a.indexA === b.indexA && a.indexB === b.indexB;
};
b2ParticleSystem.CompareTriadIndices = function (a, b) {
var diffA = a.indexA - b.indexA;
if (diffA !== 0) {
return diffA < 0;
}
var diffB = a.indexB - b.indexB;
if (diffB !== 0) {
return diffB < 0;
}
return a.indexC < b.indexC;
};
b2ParticleSystem.MatchTriadIndices = function (a, b) {
return a.indexA === b.indexA && a.indexB === b.indexB && a.indexC === b.indexC;
};
b2ParticleSystem.InitializeParticleLists = function (group, nodeBuffer) {
var bufferIndex = group.GetBufferIndex();
var particleCount = group.GetParticleCount();
for (var i = 0; i < particleCount; i++) {
var node = nodeBuffer[i];
node.list = node;
node.next = null;
node.count = 1;
node.index = i + bufferIndex;
}
};
b2ParticleSystem.prototype.MergeParticleListsInContact = function (group, nodeBuffer) {
var bufferIndex = group.GetBufferIndex();
for (var k = 0; k < this.m_contactBuffer.count; k++) {
/*const b2ParticleContact&*/
var contact = this.m_contactBuffer.data[k];
var a = contact.indexA;
var b = contact.indexB;
if (!group.ContainsParticle(a) || !group.ContainsParticle(b)) {
continue;
}
var listA = nodeBuffer[a - bufferIndex].list;
var listB = nodeBuffer[b - bufferIndex].list;
if (listA === listB) {
continue;
}
// To minimize the cost of insertion, make sure listA is longer than
// listB.
if (listA.count < listB.count) {
var _tmp = listA;
listA = listB;
listB = _tmp; ///b2Swap(listA, listB);
}
// DEBUG: b2Assert(listA.count >= listB.count);
b2ParticleSystem.MergeParticleLists(listA, listB);
}
};
b2ParticleSystem.MergeParticleLists = function (listA, listB) {
// Insert listB between index 0 and 1 of listA
// Example:
// listA => a1 => a2 => a3 => null
// listB => b1 => b2 => null
// to
// listA => listB => b1 => b2 => a1 => a2 => a3 => null
// DEBUG: b2Assert(listA !== listB);
for (var b = listB;;) {
b.list = listA;
var nextB = b.next;
if (nextB) {
b = nextB;
}
else {
b.next = listA.next;
break;
}
}
listA.next = listB;
listA.count += listB.count;
listB.count = 0;
};
b2ParticleSystem.FindLongestParticleList = function (group, nodeBuffer) {
var particleCount = group.GetParticleCount();
var result = nodeBuffer[0];
for (var i = 0; i < particleCount; i++) {
var node = nodeBuffer[i];
if (result.count < node.count) {
result = node;
}
}
return result;
};
b2ParticleSystem.prototype.MergeZombieParticleListNodes = function (group, nodeBuffer, survivingList) {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
var particleCount = group.GetParticleCount();
for (var i = 0; i < particleCount; i++) {
var node = nodeBuffer[i];
if (node !== survivingList &&
(this.m_flagsBuffer.data[node.index] & exports.b2ParticleFlag.b2_zombieParticle)) {
b2ParticleSystem.MergeParticleListAndNode(survivingList, node);
}
}
};
b2ParticleSystem.MergeParticleListAndNode = function (list, node) {
// Insert node between index 0 and 1 of list
// Example:
// list => a1 => a2 => a3 => null
// node => null
// to
// list => node => a1 => a2 => a3 => null
// DEBUG: b2Assert(node !== list);
// DEBUG: b2Assert(node.list === node);
// DEBUG: b2Assert(node.count === 1);
node.list = list;
node.next = list.next;
list.next = node;
list.count++;
node.count = 0;
};
b2ParticleSystem.prototype.CreateParticleGroupsFromParticleList = function (group, nodeBuffer, survivingList) {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
var particleCount = group.GetParticleCount();
var def = new b2ParticleGroupDef();
def.groupFlags = group.GetGroupFlags();
def.userData = group.GetUserData();
for (var i = 0; i < particleCount; i++) {
var list = nodeBuffer[i];
if (!list.count || list === survivingList) {
continue;
}
// DEBUG: b2Assert(list.list === list);
var newGroup = this.CreateParticleGroup(def);
for (var node = list; node; node = node.next) {
var oldIndex = node.index;
// DEBUG: const flags = this.m_flagsBuffer.data[oldIndex];
// DEBUG: b2Assert(!(flags & b2ParticleFlag.b2_zombieParticle));
var newIndex = this.CloneParticle(oldIndex, newGroup);
this.m_flagsBuffer.data[oldIndex] |= exports.b2ParticleFlag.b2_zombieParticle;
node.index = newIndex;
}
}
};
b2ParticleSystem.prototype.UpdatePairsAndTriadsWithParticleList = function (group, nodeBuffer) {
var bufferIndex = group.GetBufferIndex();
// Update indices in pairs and triads. If an index belongs to the group,
// replace it with the corresponding value in nodeBuffer.
// Note that nodeBuffer is allocated only for the group and the index should
// be shifted by bufferIndex.
for (var k = 0; k < this.m_pairBuffer.count; k++) {
var pair = this.m_pairBuffer.data[k];
var a = pair.indexA;
var b = pair.indexB;
if (group.ContainsParticle(a)) {
pair.indexA = nodeBuffer[a - bufferIndex].index;
}
if (group.ContainsParticle(b)) {
pair.indexB = nodeBuffer[b - bufferIndex].index;
}
}
for (var k = 0; k < this.m_triadBuffer.count; k++) {
var triad = this.m_triadBuffer.data[k];
var a = triad.indexA;
var b = triad.indexB;
var c = triad.indexC;
if (group.ContainsParticle(a)) {
triad.indexA = nodeBuffer[a - bufferIndex].index;
}
if (group.ContainsParticle(b)) {
triad.indexB = nodeBuffer[b - bufferIndex].index;
}
if (group.ContainsParticle(c)) {
triad.indexC = nodeBuffer[c - bufferIndex].index;
}
}
};
b2ParticleSystem.prototype.ComputeDepth = function () {
///b2ParticleContact* contactGroups = (b2ParticleContact*) this.m_world.m_stackAllocator.Allocate(sizeof(b2ParticleContact) * this.m_contactBuffer.GetCount());
var contactGroups = []; // TODO: static
var contactGroupsCount = 0;
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
var a = contact.indexA;
var b = contact.indexB;
var groupA = this.m_groupBuffer[a];
var groupB = this.m_groupBuffer[b];
if (groupA && groupA === groupB &&
(groupA.m_groupFlags & exports.b2ParticleGroupFlag.b2_particleGroupNeedsUpdateDepth)) {
contactGroups[contactGroupsCount++] = contact;
}
}
///b2ParticleGroup** groupsToUpdate = (b2ParticleGroup**) this.m_world.m_stackAllocator.Allocate(sizeof(b2ParticleGroup*) * this.m_groupCount);
var groupsToUpdate = []; // TODO: static
var groupsToUpdateCount = 0;
for (var group = this.m_groupList; group; group = group.GetNext()) {
if (group.m_groupFlags & exports.b2ParticleGroupFlag.b2_particleGroupNeedsUpdateDepth) {
groupsToUpdate[groupsToUpdateCount++] = group;
this.SetGroupFlags(group, group.m_groupFlags &
~exports.b2ParticleGroupFlag.b2_particleGroupNeedsUpdateDepth);
for (var i = group.m_firstIndex; i < group.m_lastIndex; i++) {
this.m_accumulationBuffer[i] = 0;
}
}
}
// Compute sum of weight of contacts except between different groups.
for (var k = 0; k < contactGroupsCount; k++) {
var contact = contactGroups[k];
var a = contact.indexA;
var b = contact.indexB;
var w = contact.weight;
this.m_accumulationBuffer[a] += w;
this.m_accumulationBuffer[b] += w;
}
// DEBUG: b2Assert(this.m_depthBuffer !== null);
for (var i = 0; i < groupsToUpdateCount; i++) {
var group = groupsToUpdate[i];
for (var i_1 = group.m_firstIndex; i_1 < group.m_lastIndex; i_1++) {
var w = this.m_accumulationBuffer[i_1];
this.m_depthBuffer[i_1] = w < 0.8 ? 0 : b2_maxFloat;
}
}
// The number of iterations is equal to particle number from the deepest
// particle to the nearest surface particle, and in general it is smaller
// than sqrt of total particle number.
///int32 iterationCount = (int32)b2Sqrt((float)m_count);
var iterationCount = b2Sqrt(this.m_count) >> 0;
for (var t = 0; t < iterationCount; t++) {
var updated = false;
for (var k = 0; k < contactGroupsCount; k++) {
var contact = contactGroups[k];
var a = contact.indexA;
var b = contact.indexB;
var r = 1 - contact.weight;
///float32& ap0 = m_depthBuffer[a];
var ap0 = this.m_depthBuffer[a];
///float32& bp0 = m_depthBuffer[b];
var bp0 = this.m_depthBuffer[b];
var ap1 = bp0 + r;
var bp1 = ap0 + r;
if (ap0 > ap1) {
///ap0 = ap1;
this.m_depthBuffer[a] = ap1;
updated = true;
}
if (bp0 > bp1) {
///bp0 = bp1;
this.m_depthBuffer[b] = bp1;
updated = true;
}
}
if (!updated) {
break;
}
}
for (var i = 0; i < groupsToUpdateCount; i++) {
var group = groupsToUpdate[i];
for (var i_2 = group.m_firstIndex; i_2 < group.m_lastIndex; i_2++) {
if (this.m_depthBuffer[i_2] < b2_maxFloat) {
this.m_depthBuffer[i_2] *= this.m_particleDiameter;
}
else {
this.m_depthBuffer[i_2] = 0;
}
}
}
///this.m_world.m_stackAllocator.Free(groupsToUpdate);
///this.m_world.m_stackAllocator.Free(contactGroups);
};
b2ParticleSystem.prototype.GetInsideBoundsEnumerator = function (aabb) {
var lowerTag = b2ParticleSystem.computeTag(this.m_inverseDiameter * aabb.lowerBound.x - 1, this.m_inverseDiameter * aabb.lowerBound.y - 1);
var upperTag = b2ParticleSystem.computeTag(this.m_inverseDiameter * aabb.upperBound.x + 1, this.m_inverseDiameter * aabb.upperBound.y + 1);
///const Proxy* beginProxy = m_proxyBuffer.Begin();
var beginProxy = 0;
///const Proxy* endProxy = m_proxyBuffer.End();
var endProxy = this.m_proxyBuffer.count;
///const Proxy* firstProxy = std::lower_bound(beginProxy, endProxy, lowerTag);
var firstProxy = std_lower_bound(this.m_proxyBuffer.data, beginProxy, endProxy, lowerTag, b2ParticleSystem.Proxy.CompareProxyTag);
///const Proxy* lastProxy = std::upper_bound(firstProxy, endProxy, upperTag);
var lastProxy = std_upper_bound(this.m_proxyBuffer.data, beginProxy, endProxy, upperTag, b2ParticleSystem.Proxy.CompareTagProxy);
// DEBUG: b2Assert(beginProxy <= firstProxy);
// DEBUG: b2Assert(firstProxy <= lastProxy);
// DEBUG: b2Assert(lastProxy <= endProxy);
return new b2ParticleSystem.InsideBoundsEnumerator(this, lowerTag, upperTag, firstProxy, lastProxy);
};
b2ParticleSystem.prototype.UpdateAllParticleFlags = function () {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
this.m_allParticleFlags = 0;
for (var i = 0; i < this.m_count; i++) {
this.m_allParticleFlags |= this.m_flagsBuffer.data[i];
}
this.m_needsUpdateAllParticleFlags = false;
};
b2ParticleSystem.prototype.UpdateAllGroupFlags = function () {
this.m_allGroupFlags = 0;
for (var group = this.m_groupList; group; group = group.GetNext()) {
this.m_allGroupFlags |= group.m_groupFlags;
}
this.m_needsUpdateAllGroupFlags = false;
};
b2ParticleSystem.prototype.AddContact = function (a, b, contacts) {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
var s_d = b2ParticleSystem.AddContact_s_d;
var pos_data = this.m_positionBuffer.data;
// DEBUG: b2Assert(contacts === this.m_contactBuffer);
///b2Vec2 d = m_positionBuffer.data[b] - m_positionBuffer.data[a];
var d = b2Vec2.SubVV(pos_data[b], pos_data[a], s_d);
var distBtParticlesSq = b2Vec2.DotVV(d, d);
if (distBtParticlesSq < this.m_squaredDiameter) {
var invD = b2InvSqrt(distBtParticlesSq);
if (!isFinite(invD)) {
invD = 1.98177537e+019;
}
///b2ParticleContact& contact = contacts.Append();
var contact = this.m_contactBuffer.data[this.m_contactBuffer.Append()];
contact.indexA = a;
contact.indexB = b;
contact.flags = this.m_flagsBuffer.data[a] | this.m_flagsBuffer.data[b];
contact.weight = 1 - distBtParticlesSq * invD * this.m_inverseDiameter;
///contact.SetNormal(invD * d);
b2Vec2.MulSV(invD, d, contact.normal);
}
};
b2ParticleSystem.prototype.FindContacts_Reference = function (contacts) {
// DEBUG: b2Assert(contacts === this.m_contactBuffer);
var beginProxy = 0;
var endProxy = this.m_proxyBuffer.count;
this.m_contactBuffer.count = 0;
for (var a = beginProxy, c = beginProxy; a < endProxy; a++) {
var rightTag = b2ParticleSystem.computeRelativeTag(this.m_proxyBuffer.data[a].tag, 1, 0);
for (var b = a + 1; b < endProxy; b++) {
if (rightTag < this.m_proxyBuffer.data[b].tag) {
break;
}
this.AddContact(this.m_proxyBuffer.data[a].index, this.m_proxyBuffer.data[b].index, this.m_contactBuffer);
}
var bottomLeftTag = b2ParticleSystem.computeRelativeTag(this.m_proxyBuffer.data[a].tag, -1, 1);
for (; c < endProxy; c++) {
if (bottomLeftTag <= this.m_proxyBuffer.data[c].tag) {
break;
}
}
var bottomRightTag = b2ParticleSystem.computeRelativeTag(this.m_proxyBuffer.data[a].tag, 1, 1);
for (var b = c; b < endProxy; b++) {
if (bottomRightTag < this.m_proxyBuffer.data[b].tag) {
break;
}
this.AddContact(this.m_proxyBuffer.data[a].index, this.m_proxyBuffer.data[b].index, this.m_contactBuffer);
}
}
};
///void ReorderForFindContact(FindContactInput* reordered, int alignedCount) const;
///void GatherChecksOneParticle(const uint32 bound, const int startIndex, const int particleIndex, int* nextUncheckedIndex, b2GrowableBuffer<FindContactCheck>& checks) const;
///void GatherChecks(b2GrowableBuffer<FindContactCheck>& checks) const;
///void FindContacts_Simd(b2GrowableBuffer<b2ParticleContact>& contacts) const;
b2ParticleSystem.prototype.FindContacts = function (contacts) {
this.FindContacts_Reference(contacts);
};
///static void UpdateProxyTags(const uint32* const tags, b2GrowableBuffer<Proxy>& proxies);
///static bool ProxyBufferHasIndex(int32 index, const Proxy* const a, int count);
///static int NumProxiesWithSameTag(const Proxy* const a, const Proxy* const b, int count);
///static bool AreProxyBuffersTheSame(const b2GrowableBuffer<Proxy>& a, const b2GrowableBuffer<Proxy>& b);
b2ParticleSystem.prototype.UpdateProxies_Reference = function (proxies) {
// DEBUG: b2Assert(proxies === this.m_proxyBuffer);
if (!this.m_positionBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var inv_diam = this.m_inverseDiameter;
for (var k = 0; k < this.m_proxyBuffer.count; ++k) {
var proxy = this.m_proxyBuffer.data[k];
var i = proxy.index;
var p = pos_data[i];
proxy.tag = b2ParticleSystem.computeTag(inv_diam * p.x, inv_diam * p.y);
}
};
///void UpdateProxies_Simd(b2GrowableBuffer<Proxy>& proxies) const;
b2ParticleSystem.prototype.UpdateProxies = function (proxies) {
this.UpdateProxies_Reference(proxies);
};
b2ParticleSystem.prototype.SortProxies = function (proxies) {
// DEBUG: b2Assert(proxies === this.m_proxyBuffer);
///std::sort(proxies.Begin(), proxies.End());
std_sort(this.m_proxyBuffer.data, 0, this.m_proxyBuffer.count, b2ParticleSystem.Proxy.CompareProxyProxy);
};
b2ParticleSystem.prototype.FilterContacts = function (contacts) {
// Optionally filter the contact.
var contactFilter = this.GetParticleContactFilter();
if (contactFilter === null) {
return;
}
/// contacts.RemoveIf(b2ParticleContactRemovePredicate(this, contactFilter));
// DEBUG: b2Assert(contacts === this.m_contactBuffer);
var system = this;
var predicate = function (contact) {
return ((contact.flags & exports.b2ParticleFlag.b2_particleContactFilterParticle) !== 0) && !contactFilter.ShouldCollideParticleParticle(system, contact.indexA, contact.indexB);
};
this.m_contactBuffer.RemoveIf(predicate);
};
b2ParticleSystem.prototype.NotifyContactListenerPreContact = function (particlePairs) {
var contactListener = this.GetParticleContactListener();
if (contactListener === null) {
return;
}
///particlePairs.Initialize(m_contactBuffer.Begin(), m_contactBuffer.GetCount(), GetFlagsBuffer());
particlePairs.Initialize(this.m_contactBuffer, this.m_flagsBuffer);
throw new Error(); // TODO: notify
};
b2ParticleSystem.prototype.NotifyContactListenerPostContact = function (particlePairs) {
var contactListener = this.GetParticleContactListener();
if (contactListener === null) {
return;
}
// Loop through all new contacts, reporting any new ones, and
// "invalidating" the ones that still exist.
///const b2ParticleContact* const endContact = m_contactBuffer.End();
///for (b2ParticleContact* contact = m_contactBuffer.Begin(); contact < endContact; ++contact)
for (var k = 0; k < this.m_contactBuffer.count; ++k) {
var contact = this.m_contactBuffer.data[k];
{
// Just started touching, inform the listener.
contactListener.BeginContactParticleParticle(this, contact);
}
}
// Report particles that are no longer touching.
// That is, any pairs that were not invalidated above.
///const int32 pairCount = particlePairs.GetCount();
///const ParticlePair* const pairs = particlePairs.GetBuffer();
///const int8* const valid = particlePairs.GetValidBuffer();
///for (int32 i = 0; i < pairCount; ++i)
///{
/// if (valid[i])
/// {
/// contactListener.EndContactParticleParticle(this, pairs[i].first, pairs[i].second);
/// }
///}
throw new Error(); // TODO: notify
};
b2ParticleSystem.b2ParticleContactIsZombie = function (contact) {
return (contact.flags & exports.b2ParticleFlag.b2_zombieParticle) === exports.b2ParticleFlag.b2_zombieParticle;
};
b2ParticleSystem.prototype.UpdateContacts = function (exceptZombie) {
this.UpdateProxies(this.m_proxyBuffer);
this.SortProxies(this.m_proxyBuffer);
///b2ParticlePairSet particlePairs(&this.m_world.m_stackAllocator);
var particlePairs = new b2ParticleSystem.b2ParticlePairSet(); // TODO: static
this.NotifyContactListenerPreContact(particlePairs);
this.FindContacts(this.m_contactBuffer);
this.FilterContacts(this.m_contactBuffer);
this.NotifyContactListenerPostContact(particlePairs);
if (exceptZombie) {
this.m_contactBuffer.RemoveIf(b2ParticleSystem.b2ParticleContactIsZombie);
}
};
b2ParticleSystem.prototype.NotifyBodyContactListenerPreContact = function (fixtureSet) {
var contactListener = this.GetFixtureContactListener();
if (contactListener === null) {
return;
}
///fixtureSet.Initialize(m_bodyContactBuffer.Begin(), m_bodyContactBuffer.GetCount(), GetFlagsBuffer());
fixtureSet.Initialize(this.m_bodyContactBuffer, this.m_flagsBuffer);
throw new Error(); // TODO: notify
};
b2ParticleSystem.prototype.NotifyBodyContactListenerPostContact = function (fixtureSet) {
var contactListener = this.GetFixtureContactListener();
if (contactListener === null) {
return;
}
// Loop through all new contacts, reporting any new ones, and
// "invalidating" the ones that still exist.
///for (b2ParticleBodyContact* contact = m_bodyContactBuffer.Begin(); contact !== m_bodyContactBuffer.End(); ++contact)
for (var k = 0; k < this.m_bodyContactBuffer.count; k++) {
var contact = this.m_bodyContactBuffer.data[k];
{
// Just started touching, report it!
contactListener.BeginContactFixtureParticle(this, contact);
}
}
// If the contact listener is enabled, report all fixtures that are no
// longer in contact with particles.
///const FixtureParticle* const fixtureParticles = fixtureSet.GetBuffer();
///const int8* const fixtureParticlesValid = fixtureSet.GetValidBuffer();
///const int32 fixtureParticleCount = fixtureSet.GetCount();
///for (int32 i = 0; i < fixtureParticleCount; ++i)
///{
/// if (fixtureParticlesValid[i])
/// {
/// const FixtureParticle* const fixtureParticle = &fixtureParticles[i];
/// contactListener.EndContactFixtureParticle(fixtureParticle.first, this, fixtureParticle.second);
/// }
///}
throw new Error(); // TODO: notify
};
b2ParticleSystem.prototype.UpdateBodyContacts = function () {
var s_aabb = b2ParticleSystem.UpdateBodyContacts_s_aabb;
// If the particle contact listener is enabled, generate a set of
// fixture / particle contacts.
///FixtureParticleSet fixtureSet(&m_world.m_stackAllocator);
var fixtureSet = new b2ParticleSystem.FixtureParticleSet(); // TODO: static
this.NotifyBodyContactListenerPreContact(fixtureSet);
if (this.m_stuckThreshold > 0) {
if (!this.m_bodyContactCountBuffer.data) {
throw new Error();
}
if (!this.m_lastBodyContactStepBuffer.data) {
throw new Error();
}
if (!this.m_consecutiveContactStepsBuffer.data) {
throw new Error();
}
var particleCount = this.GetParticleCount();
for (var i = 0; i < particleCount; i++) {
// Detect stuck particles, see comment in
// b2ParticleSystem::DetectStuckParticle()
this.m_bodyContactCountBuffer.data[i] = 0;
if (this.m_timestamp > (this.m_lastBodyContactStepBuffer.data[i] + 1)) {
this.m_consecutiveContactStepsBuffer.data[i] = 0;
}
}
}
this.m_bodyContactBuffer.SetCount(0);
this.m_stuckParticleBuffer.SetCount(0);
var aabb = s_aabb;
this.ComputeAABB(aabb);
var callback = new b2ParticleSystem.UpdateBodyContactsCallback(this, this.GetFixtureContactFilter());
this.m_world.QueryAABB(callback, aabb);
if (this.m_def.strictContactCheck) {
this.RemoveSpuriousBodyContacts();
}
this.NotifyBodyContactListenerPostContact(fixtureSet);
};
b2ParticleSystem.prototype.Solve = function (step) {
var s_subStep = b2ParticleSystem.Solve_s_subStep;
if (this.m_count === 0) {
return;
}
// If particle lifetimes are enabled, destroy particles that are too old.
if (this.m_expirationTimeBuffer.data) {
this.SolveLifetimes(step);
}
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_zombieParticle) {
this.SolveZombie();
}
if (this.m_needsUpdateAllParticleFlags) {
this.UpdateAllParticleFlags();
}
if (this.m_needsUpdateAllGroupFlags) {
this.UpdateAllGroupFlags();
}
if (this.m_paused) {
return;
}
for (this.m_iterationIndex = 0; this.m_iterationIndex < step.particleIterations; this.m_iterationIndex++) {
++this.m_timestamp;
var subStep = s_subStep.Copy(step);
subStep.dt /= step.particleIterations;
subStep.inv_dt *= step.particleIterations;
this.UpdateContacts(false);
this.UpdateBodyContacts();
this.ComputeWeight();
if (this.m_allGroupFlags & exports.b2ParticleGroupFlag.b2_particleGroupNeedsUpdateDepth) {
this.ComputeDepth();
}
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_reactiveParticle) {
this.UpdatePairsAndTriadsWithReactiveParticles();
}
if (this.m_hasForce) {
this.SolveForce(subStep);
}
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_viscousParticle) {
this.SolveViscous();
}
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_repulsiveParticle) {
this.SolveRepulsive(subStep);
}
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_powderParticle) {
this.SolvePowder(subStep);
}
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_tensileParticle) {
this.SolveTensile(subStep);
}
if (this.m_allGroupFlags & exports.b2ParticleGroupFlag.b2_solidParticleGroup) {
this.SolveSolid(subStep);
}
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_colorMixingParticle) {
this.SolveColorMixing();
}
this.SolveGravity(subStep);
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_staticPressureParticle) {
this.SolveStaticPressure(subStep);
}
this.SolvePressure(subStep);
this.SolveDamping(subStep);
if (this.m_allParticleFlags & b2ParticleSystem.k_extraDampingFlags) {
this.SolveExtraDamping();
}
// SolveElastic and SolveSpring refer the current velocities for
// numerical stability, they should be called as late as possible.
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_elasticParticle) {
this.SolveElastic(subStep);
}
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_springParticle) {
this.SolveSpring(subStep);
}
this.LimitVelocity(subStep);
if (this.m_allGroupFlags & exports.b2ParticleGroupFlag.b2_rigidParticleGroup) {
this.SolveRigidDamping();
}
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_barrierParticle) {
this.SolveBarrier(subStep);
}
// SolveCollision, SolveRigid and SolveWall should be called after
// other force functions because they may require particles to have
// specific velocities.
this.SolveCollision(subStep);
if (this.m_allGroupFlags & exports.b2ParticleGroupFlag.b2_rigidParticleGroup) {
this.SolveRigid(subStep);
}
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_wallParticle) {
this.SolveWall();
}
// The particle positions can be updated only at the end of substep.
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
for (var i = 0; i < this.m_count; i++) {
///m_positionBuffer.data[i] += subStep.dt * m_velocityBuffer.data[i];
this.m_positionBuffer.data[i].SelfMulAdd(subStep.dt, this.m_velocityBuffer.data[i]);
}
}
};
b2ParticleSystem.prototype.SolveCollision = function (step) {
var s_aabb = b2ParticleSystem.SolveCollision_s_aabb;
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var vel_data = this.m_velocityBuffer.data;
// This function detects particles which are crossing boundary of bodies
// and modifies velocities of them so that they will move just in front of
// boundary. This function function also applies the reaction force to
// bodies as precisely as the numerical stability is kept.
var aabb = s_aabb;
aabb.lowerBound.x = +b2_maxFloat;
aabb.lowerBound.y = +b2_maxFloat;
aabb.upperBound.x = -b2_maxFloat;
aabb.upperBound.y = -b2_maxFloat;
for (var i = 0; i < this.m_count; i++) {
var v = vel_data[i];
var p1 = pos_data[i];
///let p2 = p1 + step.dt * v;
var p2_x = p1.x + step.dt * v.x;
var p2_y = p1.y + step.dt * v.y;
///aabb.lowerBound = b2Min(aabb.lowerBound, b2Min(p1, p2));
aabb.lowerBound.x = b2Min(aabb.lowerBound.x, b2Min(p1.x, p2_x));
aabb.lowerBound.y = b2Min(aabb.lowerBound.y, b2Min(p1.y, p2_y));
///aabb.upperBound = b2Max(aabb.upperBound, b2Max(p1, p2));
aabb.upperBound.x = b2Max(aabb.upperBound.x, b2Max(p1.x, p2_x));
aabb.upperBound.y = b2Max(aabb.upperBound.y, b2Max(p1.y, p2_y));
}
var callback = new b2ParticleSystem.SolveCollisionCallback(this, step);
this.m_world.QueryAABB(callback, aabb);
};
b2ParticleSystem.prototype.LimitVelocity = function (step) {
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var vel_data = this.m_velocityBuffer.data;
var criticalVelocitySquared = this.GetCriticalVelocitySquared(step);
for (var i = 0; i < this.m_count; i++) {
var v = vel_data[i];
var v2 = b2Vec2.DotVV(v, v);
if (v2 > criticalVelocitySquared) {
///v *= b2Sqrt(criticalVelocitySquared / v2);
v.SelfMul(b2Sqrt(criticalVelocitySquared / v2));
}
}
};
b2ParticleSystem.prototype.SolveGravity = function (step) {
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var s_gravity = b2ParticleSystem.SolveGravity_s_gravity;
var vel_data = this.m_velocityBuffer.data;
///b2Vec2 gravity = step.dt * m_def.gravityScale * m_world.GetGravity();
var gravity = b2Vec2.MulSV(step.dt * this.m_def.gravityScale, this.m_world.GetGravity(), s_gravity);
for (var i = 0; i < this.m_count; i++) {
vel_data[i].SelfAdd(gravity);
}
};
b2ParticleSystem.prototype.SolveBarrier = function (step) {
var s_aabb = b2ParticleSystem.SolveBarrier_s_aabb;
var s_va = b2ParticleSystem.SolveBarrier_s_va;
var s_vb = b2ParticleSystem.SolveBarrier_s_vb;
var s_pba = b2ParticleSystem.SolveBarrier_s_pba;
var s_vba = b2ParticleSystem.SolveBarrier_s_vba;
var s_vc = b2ParticleSystem.SolveBarrier_s_vc;
var s_pca = b2ParticleSystem.SolveBarrier_s_pca;
var s_vca = b2ParticleSystem.SolveBarrier_s_vca;
var s_qba = b2ParticleSystem.SolveBarrier_s_qba;
var s_qca = b2ParticleSystem.SolveBarrier_s_qca;
var s_dv = b2ParticleSystem.SolveBarrier_s_dv;
var s_f = b2ParticleSystem.SolveBarrier_s_f;
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var vel_data = this.m_velocityBuffer.data;
// If a particle is passing between paired barrier particles,
// its velocity will be decelerated to avoid passing.
for (var i = 0; i < this.m_count; i++) {
var flags = this.m_flagsBuffer.data[i];
///if ((flags & b2ParticleSystem.k_barrierWallFlags) === b2ParticleSystem.k_barrierWallFlags)
if ((flags & b2ParticleSystem.k_barrierWallFlags) !== 0) {
vel_data[i].SetZero();
}
}
var tmax = b2_barrierCollisionTime * step.dt;
var mass = this.GetParticleMass();
for (var k = 0; k < this.m_pairBuffer.count; k++) {
var pair = this.m_pairBuffer.data[k];
if (pair.flags & exports.b2ParticleFlag.b2_barrierParticle) {
var a = pair.indexA;
var b = pair.indexB;
var pa = pos_data[a];
var pb = pos_data[b];
/// b2AABB aabb;
var aabb = s_aabb;
///aabb.lowerBound = b2Min(pa, pb);
b2Vec2.MinV(pa, pb, aabb.lowerBound);
///aabb.upperBound = b2Max(pa, pb);
b2Vec2.MaxV(pa, pb, aabb.upperBound);
var aGroup = this.m_groupBuffer[a];
var bGroup = this.m_groupBuffer[b];
///b2Vec2 va = GetLinearVelocity(aGroup, a, pa);
var va = this.GetLinearVelocity(aGroup, a, pa, s_va);
///b2Vec2 vb = GetLinearVelocity(bGroup, b, pb);
var vb = this.GetLinearVelocity(bGroup, b, pb, s_vb);
///b2Vec2 pba = pb - pa;
var pba = b2Vec2.SubVV(pb, pa, s_pba);
///b2Vec2 vba = vb - va;
var vba = b2Vec2.SubVV(vb, va, s_vba);
///InsideBoundsEnumerator enumerator = GetInsideBoundsEnumerator(aabb);
var enumerator = this.GetInsideBoundsEnumerator(aabb);
var c = void 0;
while ((c = enumerator.GetNext()) >= 0) {
var pc = pos_data[c];
var cGroup = this.m_groupBuffer[c];
if (aGroup !== cGroup && bGroup !== cGroup) {
///b2Vec2 vc = GetLinearVelocity(cGroup, c, pc);
var vc = this.GetLinearVelocity(cGroup, c, pc, s_vc);
// Solve the equation below:
// (1-s)*(pa+t*va)+s*(pb+t*vb) = pc+t*vc
// which expresses that the particle c will pass a line
// connecting the particles a and b at the time of t.
// if s is between 0 and 1, c will pass between a and b.
///b2Vec2 pca = pc - pa;
var pca = b2Vec2.SubVV(pc, pa, s_pca);
///b2Vec2 vca = vc - va;
var vca = b2Vec2.SubVV(vc, va, s_vca);
var e2 = b2Vec2.CrossVV(vba, vca);
var e1 = b2Vec2.CrossVV(pba, vca) - b2Vec2.CrossVV(pca, vba);
var e0 = b2Vec2.CrossVV(pba, pca);
var s = void 0, t = void 0;
///b2Vec2 qba, qca;
var qba = s_qba, qca = s_qca;
if (e2 === 0) {
if (e1 === 0) {
continue;
}
t = -e0 / e1;
if (!(t >= 0 && t < tmax)) {
continue;
}
///qba = pba + t * vba;
b2Vec2.AddVMulSV(pba, t, vba, qba);
///qca = pca + t * vca;
b2Vec2.AddVMulSV(pca, t, vca, qca);
s = b2Vec2.DotVV(qba, qca) / b2Vec2.DotVV(qba, qba);
if (!(s >= 0 && s <= 1)) {
continue;
}
}
else {
var det = e1 * e1 - 4 * e0 * e2;
if (det < 0) {
continue;
}
var sqrtDet = b2Sqrt(det);
var t1 = (-e1 - sqrtDet) / (2 * e2);
var t2 = (-e1 + sqrtDet) / (2 * e2);
///if (t1 > t2) b2Swap(t1, t2);
if (t1 > t2) {
var tmp = t1;
t1 = t2;
t2 = tmp;
}
t = t1;
///qba = pba + t * vba;
b2Vec2.AddVMulSV(pba, t, vba, qba);
///qca = pca + t * vca;
b2Vec2.AddVMulSV(pca, t, vca, qca);
///s = b2Dot(qba, qca) / b2Dot(qba, qba);
s = b2Vec2.DotVV(qba, qca) / b2Vec2.DotVV(qba, qba);
if (!(t >= 0 && t < tmax && s >= 0 && s <= 1)) {
t = t2;
if (!(t >= 0 && t < tmax)) {
continue;
}
///qba = pba + t * vba;
b2Vec2.AddVMulSV(pba, t, vba, qba);
///qca = pca + t * vca;
b2Vec2.AddVMulSV(pca, t, vca, qca);
///s = b2Dot(qba, qca) / b2Dot(qba, qba);
s = b2Vec2.DotVV(qba, qca) / b2Vec2.DotVV(qba, qba);
if (!(s >= 0 && s <= 1)) {
continue;
}
}
}
// Apply a force to particle c so that it will have the
// interpolated velocity at the collision point on line ab.
///b2Vec2 dv = va + s * vba - vc;
var dv = s_dv;
dv.x = va.x + s * vba.x - vc.x;
dv.y = va.y + s * vba.y - vc.y;
///b2Vec2 f = GetParticleMass() * dv;
var f = b2Vec2.MulSV(mass, dv, s_f);
if (cGroup && this.IsRigidGroup(cGroup)) {
// If c belongs to a rigid group, the force will be
// distributed in the group.
var mass_1 = cGroup.GetMass();
var inertia = cGroup.GetInertia();
if (mass_1 > 0) {
///cGroup.m_linearVelocity += 1 / mass * f;
cGroup.m_linearVelocity.SelfMulAdd(1 / mass_1, f);
}
if (inertia > 0) {
///cGroup.m_angularVelocity += b2Cross(pc - cGroup.GetCenter(), f) / inertia;
cGroup.m_angularVelocity += b2Vec2.CrossVV(b2Vec2.SubVV(pc, cGroup.GetCenter(), b2Vec2.s_t0), f) / inertia;
}
}
else {
///m_velocityBuffer.data[c] += dv;
vel_data[c].SelfAdd(dv);
}
// Apply a reversed force to particle c after particle
// movement so that momentum will be preserved.
///ParticleApplyForce(c, -step.inv_dt * f);
this.ParticleApplyForce(c, f.SelfMul(-step.inv_dt));
}
}
}
}
};
b2ParticleSystem.prototype.SolveStaticPressure = function (step) {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
this.m_staticPressureBuffer = this.RequestBuffer(this.m_staticPressureBuffer);
var criticalPressure = this.GetCriticalPressure(step);
var pressurePerWeight = this.m_def.staticPressureStrength * criticalPressure;
var maxPressure = b2_maxParticlePressure * criticalPressure;
var relaxation = this.m_def.staticPressureRelaxation;
/// Compute pressure satisfying the modified Poisson equation:
/// Sum_for_j((p_i - p_j) * w_ij) + relaxation * p_i =
/// pressurePerWeight * (w_i - b2_minParticleWeight)
/// by iterating the calculation:
/// p_i = (Sum_for_j(p_j * w_ij) + pressurePerWeight *
/// (w_i - b2_minParticleWeight)) / (w_i + relaxation)
/// where
/// p_i and p_j are static pressure of particle i and j
/// w_ij is contact weight between particle i and j
/// w_i is sum of contact weight of particle i
for (var t = 0; t < this.m_def.staticPressureIterations; t++) {
///memset(m_accumulationBuffer, 0, sizeof(*m_accumulationBuffer) * m_count);
for (var i = 0; i < this.m_count; i++) {
this.m_accumulationBuffer[i] = 0;
}
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
if (contact.flags & exports.b2ParticleFlag.b2_staticPressureParticle) {
var a = contact.indexA;
var b = contact.indexB;
var w = contact.weight;
this.m_accumulationBuffer[a] += w * this.m_staticPressureBuffer[b]; // a <- b
this.m_accumulationBuffer[b] += w * this.m_staticPressureBuffer[a]; // b <- a
}
}
for (var i = 0; i < this.m_count; i++) {
var w = this.m_weightBuffer[i];
if (this.m_flagsBuffer.data[i] & exports.b2ParticleFlag.b2_staticPressureParticle) {
var wh = this.m_accumulationBuffer[i];
var h = (wh + pressurePerWeight * (w - b2_minParticleWeight)) /
(w + relaxation);
this.m_staticPressureBuffer[i] = b2Clamp(h, 0.0, maxPressure);
}
else {
this.m_staticPressureBuffer[i] = 0;
}
}
}
};
b2ParticleSystem.prototype.ComputeWeight = function () {
// calculates the sum of contact-weights for each particle
// that means dimensionless density
///memset(m_weightBuffer, 0, sizeof(*m_weightBuffer) * m_count);
for (var k = 0; k < this.m_count; k++) {
this.m_weightBuffer[k] = 0;
}
for (var k = 0; k < this.m_bodyContactBuffer.count; k++) {
var contact = this.m_bodyContactBuffer.data[k];
var a = contact.index;
var w = contact.weight;
this.m_weightBuffer[a] += w;
}
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
var a = contact.indexA;
var b = contact.indexB;
var w = contact.weight;
this.m_weightBuffer[a] += w;
this.m_weightBuffer[b] += w;
}
};
b2ParticleSystem.prototype.SolvePressure = function (step) {
var s_f = b2ParticleSystem.SolvePressure_s_f;
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var vel_data = this.m_velocityBuffer.data;
// calculates pressure as a linear function of density
var criticalPressure = this.GetCriticalPressure(step);
var pressurePerWeight = this.m_def.pressureStrength * criticalPressure;
var maxPressure = b2_maxParticlePressure * criticalPressure;
for (var i = 0; i < this.m_count; i++) {
var w = this.m_weightBuffer[i];
var h = pressurePerWeight * b2Max(0.0, w - b2_minParticleWeight);
this.m_accumulationBuffer[i] = b2Min(h, maxPressure);
}
// ignores particles which have their own repulsive force
if (this.m_allParticleFlags & b2ParticleSystem.k_noPressureFlags) {
for (var i = 0; i < this.m_count; i++) {
if (this.m_flagsBuffer.data[i] & b2ParticleSystem.k_noPressureFlags) {
this.m_accumulationBuffer[i] = 0;
}
}
}
// static pressure
if (this.m_allParticleFlags & exports.b2ParticleFlag.b2_staticPressureParticle) {
// DEBUG: b2Assert(this.m_staticPressureBuffer !== null);
for (var i = 0; i < this.m_count; i++) {
if (this.m_flagsBuffer.data[i] & exports.b2ParticleFlag.b2_staticPressureParticle) {
this.m_accumulationBuffer[i] += this.m_staticPressureBuffer[i];
}
}
}
// applies pressure between each particles in contact
var velocityPerPressure = step.dt / (this.m_def.density * this.m_particleDiameter);
var inv_mass = this.GetParticleInvMass();
for (var k = 0; k < this.m_bodyContactBuffer.count; k++) {
var contact = this.m_bodyContactBuffer.data[k];
var a = contact.index;
var b = contact.body;
var w = contact.weight;
var m = contact.mass;
var n = contact.normal;
var p = pos_data[a];
var h = this.m_accumulationBuffer[a] + pressurePerWeight * w;
///b2Vec2 f = velocityPerPressure * w * m * h * n;
var f = b2Vec2.MulSV(velocityPerPressure * w * m * h, n, s_f);
///m_velocityBuffer.data[a] -= GetParticleInvMass() * f;
vel_data[a].SelfMulSub(inv_mass, f);
b.ApplyLinearImpulse(f, p, true);
}
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
var a = contact.indexA;
var b = contact.indexB;
var w = contact.weight;
var n = contact.normal;
var h = this.m_accumulationBuffer[a] + this.m_accumulationBuffer[b];
///b2Vec2 f = velocityPerPressure * w * h * n;
var f = b2Vec2.MulSV(velocityPerPressure * w * h, n, s_f);
///m_velocityBuffer.data[a] -= f;
vel_data[a].SelfSub(f);
///m_velocityBuffer.data[b] += f;
vel_data[b].SelfAdd(f);
}
};
b2ParticleSystem.prototype.SolveDamping = function (step) {
var s_v = b2ParticleSystem.SolveDamping_s_v;
var s_f = b2ParticleSystem.SolveDamping_s_f;
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var vel_data = this.m_velocityBuffer.data;
// reduces normal velocity of each contact
var linearDamping = this.m_def.dampingStrength;
var quadraticDamping = 1 / this.GetCriticalVelocity(step);
var inv_mass = this.GetParticleInvMass();
for (var k = 0; k < this.m_bodyContactBuffer.count; k++) {
var contact = this.m_bodyContactBuffer.data[k];
var a = contact.index;
var b = contact.body;
var w = contact.weight;
var m = contact.mass;
var n = contact.normal;
var p = pos_data[a];
///b2Vec2 v = b.GetLinearVelocityFromWorldPoint(p) - m_velocityBuffer.data[a];
var v = b2Vec2.SubVV(b.GetLinearVelocityFromWorldPoint(p, b2Vec2.s_t0), vel_data[a], s_v);
var vn = b2Vec2.DotVV(v, n);
if (vn < 0) {
var damping = b2Max(linearDamping * w, b2Min(-quadraticDamping * vn, 0.5));
///b2Vec2 f = damping * m * vn * n;
var f = b2Vec2.MulSV(damping * m * vn, n, s_f);
///m_velocityBuffer.data[a] += GetParticleInvMass() * f;
vel_data[a].SelfMulAdd(inv_mass, f);
///b.ApplyLinearImpulse(-f, p, true);
b.ApplyLinearImpulse(f.SelfNeg(), p, true);
}
}
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
var a = contact.indexA;
var b = contact.indexB;
var w = contact.weight;
var n = contact.normal;
///b2Vec2 v = m_velocityBuffer.data[b] - m_velocityBuffer.data[a];
var v = b2Vec2.SubVV(vel_data[b], vel_data[a], s_v);
var vn = b2Vec2.DotVV(v, n);
if (vn < 0) {
///float32 damping = b2Max(linearDamping * w, b2Min(- quadraticDamping * vn, 0.5f));
var damping = b2Max(linearDamping * w, b2Min(-quadraticDamping * vn, 0.5));
///b2Vec2 f = damping * vn * n;
var f = b2Vec2.MulSV(damping * vn, n, s_f);
///this.m_velocityBuffer.data[a] += f;
vel_data[a].SelfAdd(f);
///this.m_velocityBuffer.data[b] -= f;
vel_data[b].SelfSub(f);
}
}
};
b2ParticleSystem.prototype.SolveRigidDamping = function () {
var s_t0 = b2ParticleSystem.SolveRigidDamping_s_t0;
var s_t1 = b2ParticleSystem.SolveRigidDamping_s_t1;
var s_p = b2ParticleSystem.SolveRigidDamping_s_p;
var s_v = b2ParticleSystem.SolveRigidDamping_s_v;
var invMassA = [0.0], invInertiaA = [0.0], tangentDistanceA = [0.0]; // TODO: static
var invMassB = [0.0], invInertiaB = [0.0], tangentDistanceB = [0.0]; // TODO: static
// Apply impulse to rigid particle groups colliding with other objects
// to reduce relative velocity at the colliding point.
if (!this.m_positionBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var damping = this.m_def.dampingStrength;
for (var k = 0; k < this.m_bodyContactBuffer.count; k++) {
var contact = this.m_bodyContactBuffer.data[k];
var a = contact.index;
var aGroup = this.m_groupBuffer[a];
if (aGroup && this.IsRigidGroup(aGroup)) {
var b = contact.body;
var n = contact.normal;
var w = contact.weight;
var p = pos_data[a];
///b2Vec2 v = b.GetLinearVelocityFromWorldPoint(p) - aGroup.GetLinearVelocityFromWorldPoint(p);
var v = b2Vec2.SubVV(b.GetLinearVelocityFromWorldPoint(p, s_t0), aGroup.GetLinearVelocityFromWorldPoint(p, s_t1), s_v);
var vn = b2Vec2.DotVV(v, n);
if (vn < 0) {
// The group's average velocity at particle position 'p' is pushing
// the particle into the body.
///this.InitDampingParameterWithRigidGroupOrParticle(&invMassA, &invInertiaA, &tangentDistanceA, true, aGroup, a, p, n);
this.InitDampingParameterWithRigidGroupOrParticle(invMassA, invInertiaA, tangentDistanceA, true, aGroup, a, p, n);
// Calculate b.m_I from public functions of b2Body.
///this.InitDampingParameter(&invMassB, &invInertiaB, &tangentDistanceB, b.GetMass(), b.GetInertia() - b.GetMass() * b.GetLocalCenter().LengthSquared(), b.GetWorldCenter(), p, n);
this.InitDampingParameter(invMassB, invInertiaB, tangentDistanceB, b.GetMass(), b.GetInertia() - b.GetMass() * b.GetLocalCenter().LengthSquared(), b.GetWorldCenter(), p, n);
///float32 f = damping * b2Min(w, 1.0) * this.ComputeDampingImpulse(invMassA, invInertiaA, tangentDistanceA, invMassB, invInertiaB, tangentDistanceB, vn);
var f = damping * b2Min(w, 1.0) * this.ComputeDampingImpulse(invMassA[0], invInertiaA[0], tangentDistanceA[0], invMassB[0], invInertiaB[0], tangentDistanceB[0], vn);
///this.ApplyDamping(invMassA, invInertiaA, tangentDistanceA, true, aGroup, a, f, n);
this.ApplyDamping(invMassA[0], invInertiaA[0], tangentDistanceA[0], true, aGroup, a, f, n);
///b.ApplyLinearImpulse(-f * n, p, true);
b.ApplyLinearImpulse(b2Vec2.MulSV(-f, n, b2Vec2.s_t0), p, true);
}
}
}
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
var a = contact.indexA;
var b = contact.indexB;
var n = contact.normal;
var w = contact.weight;
var aGroup = this.m_groupBuffer[a];
var bGroup = this.m_groupBuffer[b];
var aRigid = this.IsRigidGroup(aGroup);
var bRigid = this.IsRigidGroup(bGroup);
if (aGroup !== bGroup && (aRigid || bRigid)) {
///b2Vec2 p = 0.5f * (this.m_positionBuffer.data[a] + this.m_positionBuffer.data[b]);
var p = b2Vec2.MidVV(pos_data[a], pos_data[b], s_p);
///b2Vec2 v = GetLinearVelocity(bGroup, b, p) - GetLinearVelocity(aGroup, a, p);
var v = b2Vec2.SubVV(this.GetLinearVelocity(bGroup, b, p, s_t0), this.GetLinearVelocity(aGroup, a, p, s_t1), s_v);
var vn = b2Vec2.DotVV(v, n);
if (vn < 0) {
///this.InitDampingParameterWithRigidGroupOrParticle(&invMassA, &invInertiaA, &tangentDistanceA, aRigid, aGroup, a, p, n);
this.InitDampingParameterWithRigidGroupOrParticle(invMassA, invInertiaA, tangentDistanceA, aRigid, aGroup, a, p, n);
///this.InitDampingParameterWithRigidGroupOrParticle(&invMassB, &invInertiaB, &tangentDistanceB, bRigid, bGroup, b, p, n);
this.InitDampingParameterWithRigidGroupOrParticle(invMassB, invInertiaB, tangentDistanceB, bRigid, bGroup, b, p, n);
///float32 f = damping * w * this.ComputeDampingImpulse(invMassA, invInertiaA, tangentDistanceA, invMassB, invInertiaB, tangentDistanceB, vn);
var f = damping * w * this.ComputeDampingImpulse(invMassA[0], invInertiaA[0], tangentDistanceA[0], invMassB[0], invInertiaB[0], tangentDistanceB[0], vn);
///this.ApplyDamping(invMassA, invInertiaA, tangentDistanceA, aRigid, aGroup, a, f, n);
this.ApplyDamping(invMassA[0], invInertiaA[0], tangentDistanceA[0], aRigid, aGroup, a, f, n);
///this.ApplyDamping(invMassB, invInertiaB, tangentDistanceB, bRigid, bGroup, b, -f, n);
this.ApplyDamping(invMassB[0], invInertiaB[0], tangentDistanceB[0], bRigid, bGroup, b, -f, n);
}
}
}
};
b2ParticleSystem.prototype.SolveExtraDamping = function () {
var s_v = b2ParticleSystem.SolveExtraDamping_s_v;
var s_f = b2ParticleSystem.SolveExtraDamping_s_f;
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var vel_data = this.m_velocityBuffer.data;
// Applies additional damping force between bodies and particles which can
// produce strong repulsive force. Applying damping force multiple times
// is effective in suppressing vibration.
var pos_data = this.m_positionBuffer.data;
var inv_mass = this.GetParticleInvMass();
for (var k = 0; k < this.m_bodyContactBuffer.count; k++) {
var contact = this.m_bodyContactBuffer.data[k];
var a = contact.index;
if (this.m_flagsBuffer.data[a] & b2ParticleSystem.k_extraDampingFlags) {
var b = contact.body;
var m = contact.mass;
var n = contact.normal;
var p = pos_data[a];
///b2Vec2 v = b.GetLinearVelocityFromWorldPoint(p) - m_velocityBuffer.data[a];
var v = b2Vec2.SubVV(b.GetLinearVelocityFromWorldPoint(p, b2Vec2.s_t0), vel_data[a], s_v);
///float32 vn = b2Dot(v, n);
var vn = b2Vec2.DotVV(v, n);
if (vn < 0) {
///b2Vec2 f = 0.5f * m * vn * n;
var f = b2Vec2.MulSV(0.5 * m * vn, n, s_f);
///m_velocityBuffer.data[a] += GetParticleInvMass() * f;
vel_data[a].SelfMulAdd(inv_mass, f);
///b.ApplyLinearImpulse(-f, p, true);
b.ApplyLinearImpulse(f.SelfNeg(), p, true);
}
}
}
};
b2ParticleSystem.prototype.SolveWall = function () {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var vel_data = this.m_velocityBuffer.data;
for (var i = 0; i < this.m_count; i++) {
if (this.m_flagsBuffer.data[i] & exports.b2ParticleFlag.b2_wallParticle) {
vel_data[i].SetZero();
}
}
};
b2ParticleSystem.prototype.SolveRigid = function (step) {
var s_position = b2ParticleSystem.SolveRigid_s_position;
var s_rotation = b2ParticleSystem.SolveRigid_s_rotation;
var s_transform = b2ParticleSystem.SolveRigid_s_transform;
var s_velocityTransform = b2ParticleSystem.SolveRigid_s_velocityTransform;
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var vel_data = this.m_velocityBuffer.data;
for (var group = this.m_groupList; group; group = group.GetNext()) {
if (group.m_groupFlags & exports.b2ParticleGroupFlag.b2_rigidParticleGroup) {
group.UpdateStatistics();
///b2Rot rotation(step.dt * group.m_angularVelocity);
var rotation = s_rotation;
rotation.SetAngle(step.dt * group.m_angularVelocity);
///b2Transform transform(group.m_center + step.dt * group.m_linearVelocity - b2Mul(rotation, group.m_center), rotation);
var position = b2Vec2.AddVV(group.m_center, b2Vec2.SubVV(b2Vec2.MulSV(step.dt, group.m_linearVelocity, b2Vec2.s_t0), b2Rot.MulRV(rotation, group.m_center, b2Vec2.s_t1), b2Vec2.s_t0), s_position);
var transform = s_transform;
transform.SetPositionRotation(position, rotation);
///group.m_transform = b2Mul(transform, group.m_transform);
b2Transform.MulXX(transform, group.m_transform, group.m_transform);
var velocityTransform = s_velocityTransform;
velocityTransform.p.x = step.inv_dt * transform.p.x;
velocityTransform.p.y = step.inv_dt * transform.p.y;
velocityTransform.q.s = step.inv_dt * transform.q.s;
velocityTransform.q.c = step.inv_dt * (transform.q.c - 1);
for (var i = group.m_firstIndex; i < group.m_lastIndex; i++) {
///m_velocityBuffer.data[i] = b2Mul(velocityTransform, m_positionBuffer.data[i]);
b2Transform.MulXV(velocityTransform, pos_data[i], vel_data[i]);
}
}
}
};
b2ParticleSystem.prototype.SolveElastic = function (step) {
var s_pa = b2ParticleSystem.SolveElastic_s_pa;
var s_pb = b2ParticleSystem.SolveElastic_s_pb;
var s_pc = b2ParticleSystem.SolveElastic_s_pc;
var s_r = b2ParticleSystem.SolveElastic_s_r;
var s_t0 = b2ParticleSystem.SolveElastic_s_t0;
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var vel_data = this.m_velocityBuffer.data;
var elasticStrength = step.inv_dt * this.m_def.elasticStrength;
for (var k = 0; k < this.m_triadBuffer.count; k++) {
var triad = this.m_triadBuffer.data[k];
if (triad.flags & exports.b2ParticleFlag.b2_elasticParticle) {
var a = triad.indexA;
var b = triad.indexB;
var c = triad.indexC;
var oa = triad.pa;
var ob = triad.pb;
var oc = triad.pc;
///b2Vec2 pa = m_positionBuffer.data[a];
var pa = s_pa.Copy(pos_data[a]);
///b2Vec2 pb = m_positionBuffer.data[b];
var pb = s_pb.Copy(pos_data[b]);
///b2Vec2 pc = m_positionBuffer.data[c];
var pc = s_pc.Copy(pos_data[c]);
var va = vel_data[a];
var vb = vel_data[b];
var vc = vel_data[c];
///pa += step.dt * va;
pa.SelfMulAdd(step.dt, va);
///pb += step.dt * vb;
pb.SelfMulAdd(step.dt, vb);
///pc += step.dt * vc;
pc.SelfMulAdd(step.dt, vc);
///b2Vec2 midPoint = (float32) 1 / 3 * (pa + pb + pc);
var midPoint_x = (pa.x + pb.x + pc.x) / 3.0;
var midPoint_y = (pa.y + pb.y + pc.y) / 3.0;
///pa -= midPoint;
pa.x -= midPoint_x;
pa.y -= midPoint_y;
///pb -= midPoint;
pb.x -= midPoint_x;
pb.y -= midPoint_y;
///pc -= midPoint;
pc.x -= midPoint_x;
pc.y -= midPoint_y;
///b2Rot r;
var r = s_r;
r.s = b2Vec2.CrossVV(oa, pa) + b2Vec2.CrossVV(ob, pb) + b2Vec2.CrossVV(oc, pc);
r.c = b2Vec2.DotVV(oa, pa) + b2Vec2.DotVV(ob, pb) + b2Vec2.DotVV(oc, pc);
var r2 = r.s * r.s + r.c * r.c;
var invR = b2InvSqrt(r2);
if (!isFinite(invR)) {
invR = 1.98177537e+019;
}
r.s *= invR;
r.c *= invR;
///r.angle = Math.atan2(r.s, r.c); // TODO: optimize
var strength = elasticStrength * triad.strength;
///va += strength * (b2Mul(r, oa) - pa);
b2Rot.MulRV(r, oa, s_t0);
b2Vec2.SubVV(s_t0, pa, s_t0);
b2Vec2.MulSV(strength, s_t0, s_t0);
va.SelfAdd(s_t0);
///vb += strength * (b2Mul(r, ob) - pb);
b2Rot.MulRV(r, ob, s_t0);
b2Vec2.SubVV(s_t0, pb, s_t0);
b2Vec2.MulSV(strength, s_t0, s_t0);
vb.SelfAdd(s_t0);
///vc += strength * (b2Mul(r, oc) - pc);
b2Rot.MulRV(r, oc, s_t0);
b2Vec2.SubVV(s_t0, pc, s_t0);
b2Vec2.MulSV(strength, s_t0, s_t0);
vc.SelfAdd(s_t0);
}
}
};
b2ParticleSystem.prototype.SolveSpring = function (step) {
var s_pa = b2ParticleSystem.SolveSpring_s_pa;
var s_pb = b2ParticleSystem.SolveSpring_s_pb;
var s_d = b2ParticleSystem.SolveSpring_s_d;
var s_f = b2ParticleSystem.SolveSpring_s_f;
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var vel_data = this.m_velocityBuffer.data;
var springStrength = step.inv_dt * this.m_def.springStrength;
for (var k = 0; k < this.m_pairBuffer.count; k++) {
var pair = this.m_pairBuffer.data[k];
if (pair.flags & exports.b2ParticleFlag.b2_springParticle) {
///int32 a = pair.indexA;
var a = pair.indexA;
///int32 b = pair.indexB;
var b = pair.indexB;
///b2Vec2 pa = m_positionBuffer.data[a];
var pa = s_pa.Copy(pos_data[a]);
///b2Vec2 pb = m_positionBuffer.data[b];
var pb = s_pb.Copy(pos_data[b]);
///b2Vec2& va = m_velocityBuffer.data[a];
var va = vel_data[a];
///b2Vec2& vb = m_velocityBuffer.data[b];
var vb = vel_data[b];
///pa += step.dt * va;
pa.SelfMulAdd(step.dt, va);
///pb += step.dt * vb;
pb.SelfMulAdd(step.dt, vb);
///b2Vec2 d = pb - pa;
var d = b2Vec2.SubVV(pb, pa, s_d);
///float32 r0 = pair.distance;
var r0 = pair.distance;
///float32 r1 = d.Length();
var r1 = d.Length();
///float32 strength = springStrength * pair.strength;
var strength = springStrength * pair.strength;
///b2Vec2 f = strength * (r0 - r1) / r1 * d;
var f = b2Vec2.MulSV(strength * (r0 - r1) / r1, d, s_f);
///va -= f;
va.SelfSub(f);
///vb += f;
vb.SelfAdd(f);
}
}
};
b2ParticleSystem.prototype.SolveTensile = function (step) {
var s_weightedNormal = b2ParticleSystem.SolveTensile_s_weightedNormal;
var s_s = b2ParticleSystem.SolveTensile_s_s;
var s_f = b2ParticleSystem.SolveTensile_s_f;
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var vel_data = this.m_velocityBuffer.data;
// DEBUG: b2Assert(this.m_accumulation2Buffer !== null);
for (var i = 0; i < this.m_count; i++) {
this.m_accumulation2Buffer[i] = new b2Vec2();
this.m_accumulation2Buffer[i].SetZero();
}
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
if (contact.flags & exports.b2ParticleFlag.b2_tensileParticle) {
var a = contact.indexA;
var b = contact.indexB;
var w = contact.weight;
var n = contact.normal;
///b2Vec2 weightedNormal = (1 - w) * w * n;
var weightedNormal = b2Vec2.MulSV((1 - w) * w, n, s_weightedNormal);
///m_accumulation2Buffer[a] -= weightedNormal;
this.m_accumulation2Buffer[a].SelfSub(weightedNormal);
///m_accumulation2Buffer[b] += weightedNormal;
this.m_accumulation2Buffer[b].SelfAdd(weightedNormal);
}
}
var criticalVelocity = this.GetCriticalVelocity(step);
var pressureStrength = this.m_def.surfaceTensionPressureStrength * criticalVelocity;
var normalStrength = this.m_def.surfaceTensionNormalStrength * criticalVelocity;
var maxVelocityVariation = b2_maxParticleForce * criticalVelocity;
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
if (contact.flags & exports.b2ParticleFlag.b2_tensileParticle) {
var a = contact.indexA;
var b = contact.indexB;
var w = contact.weight;
var n = contact.normal;
var h = this.m_weightBuffer[a] + this.m_weightBuffer[b];
///b2Vec2 s = m_accumulation2Buffer[b] - m_accumulation2Buffer[a];
var s = b2Vec2.SubVV(this.m_accumulation2Buffer[b], this.m_accumulation2Buffer[a], s_s);
var fn = b2Min(pressureStrength * (h - 2) + normalStrength * b2Vec2.DotVV(s, n), maxVelocityVariation) * w;
///b2Vec2 f = fn * n;
var f = b2Vec2.MulSV(fn, n, s_f);
///m_velocityBuffer.data[a] -= f;
vel_data[a].SelfSub(f);
///m_velocityBuffer.data[b] += f;
vel_data[b].SelfAdd(f);
}
}
};
b2ParticleSystem.prototype.SolveViscous = function () {
var s_v = b2ParticleSystem.SolveViscous_s_v;
var s_f = b2ParticleSystem.SolveViscous_s_f;
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var vel_data = this.m_velocityBuffer.data;
var viscousStrength = this.m_def.viscousStrength;
var inv_mass = this.GetParticleInvMass();
for (var k = 0; k < this.m_bodyContactBuffer.count; k++) {
var contact = this.m_bodyContactBuffer.data[k];
var a = contact.index;
if (this.m_flagsBuffer.data[a] & exports.b2ParticleFlag.b2_viscousParticle) {
var b = contact.body;
var w = contact.weight;
var m = contact.mass;
var p = pos_data[a];
///b2Vec2 v = b.GetLinearVelocityFromWorldPoint(p) - m_velocityBuffer.data[a];
var v = b2Vec2.SubVV(b.GetLinearVelocityFromWorldPoint(p, b2Vec2.s_t0), vel_data[a], s_v);
///b2Vec2 f = viscousStrength * m * w * v;
var f = b2Vec2.MulSV(viscousStrength * m * w, v, s_f);
///m_velocityBuffer.data[a] += GetParticleInvMass() * f;
vel_data[a].SelfMulAdd(inv_mass, f);
///b.ApplyLinearImpulse(-f, p, true);
b.ApplyLinearImpulse(f.SelfNeg(), p, true);
}
}
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
if (contact.flags & exports.b2ParticleFlag.b2_viscousParticle) {
var a = contact.indexA;
var b = contact.indexB;
var w = contact.weight;
///b2Vec2 v = m_velocityBuffer.data[b] - m_velocityBuffer.data[a];
var v = b2Vec2.SubVV(vel_data[b], vel_data[a], s_v);
///b2Vec2 f = viscousStrength * w * v;
var f = b2Vec2.MulSV(viscousStrength * w, v, s_f);
///m_velocityBuffer.data[a] += f;
vel_data[a].SelfAdd(f);
///m_velocityBuffer.data[b] -= f;
vel_data[b].SelfSub(f);
}
}
};
b2ParticleSystem.prototype.SolveRepulsive = function (step) {
var s_f = b2ParticleSystem.SolveRepulsive_s_f;
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var vel_data = this.m_velocityBuffer.data;
var repulsiveStrength = this.m_def.repulsiveStrength * this.GetCriticalVelocity(step);
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
if (contact.flags & exports.b2ParticleFlag.b2_repulsiveParticle) {
var a = contact.indexA;
var b = contact.indexB;
if (this.m_groupBuffer[a] !== this.m_groupBuffer[b]) {
var w = contact.weight;
var n = contact.normal;
///b2Vec2 f = repulsiveStrength * w * n;
var f = b2Vec2.MulSV(repulsiveStrength * w, n, s_f);
///m_velocityBuffer.data[a] -= f;
vel_data[a].SelfSub(f);
///m_velocityBuffer.data[b] += f;
vel_data[b].SelfAdd(f);
}
}
}
};
b2ParticleSystem.prototype.SolvePowder = function (step) {
var s_f = b2ParticleSystem.SolvePowder_s_f;
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var pos_data = this.m_positionBuffer.data;
var vel_data = this.m_velocityBuffer.data;
var powderStrength = this.m_def.powderStrength * this.GetCriticalVelocity(step);
var minWeight = 1.0 - b2_particleStride;
var inv_mass = this.GetParticleInvMass();
for (var k = 0; k < this.m_bodyContactBuffer.count; k++) {
var contact = this.m_bodyContactBuffer.data[k];
var a = contact.index;
if (this.m_flagsBuffer.data[a] & exports.b2ParticleFlag.b2_powderParticle) {
var w = contact.weight;
if (w > minWeight) {
var b = contact.body;
var m = contact.mass;
var p = pos_data[a];
var n = contact.normal;
var f = b2Vec2.MulSV(powderStrength * m * (w - minWeight), n, s_f);
vel_data[a].SelfMulSub(inv_mass, f);
b.ApplyLinearImpulse(f, p, true);
}
}
}
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
if (contact.flags & exports.b2ParticleFlag.b2_powderParticle) {
var w = contact.weight;
if (w > minWeight) {
var a = contact.indexA;
var b = contact.indexB;
var n = contact.normal;
var f = b2Vec2.MulSV(powderStrength * (w - minWeight), n, s_f);
vel_data[a].SelfSub(f);
vel_data[b].SelfAdd(f);
}
}
}
};
b2ParticleSystem.prototype.SolveSolid = function (step) {
var s_f = b2ParticleSystem.SolveSolid_s_f;
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var vel_data = this.m_velocityBuffer.data;
// applies extra repulsive force from solid particle groups
this.m_depthBuffer = this.RequestBuffer(this.m_depthBuffer);
var ejectionStrength = step.inv_dt * this.m_def.ejectionStrength;
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
var a = contact.indexA;
var b = contact.indexB;
if (this.m_groupBuffer[a] !== this.m_groupBuffer[b]) {
var w = contact.weight;
var n = contact.normal;
var h = this.m_depthBuffer[a] + this.m_depthBuffer[b];
var f = b2Vec2.MulSV(ejectionStrength * h * w, n, s_f);
vel_data[a].SelfSub(f);
vel_data[b].SelfAdd(f);
}
}
};
b2ParticleSystem.prototype.SolveForce = function (step) {
if (!this.m_velocityBuffer.data) {
throw new Error();
}
var vel_data = this.m_velocityBuffer.data;
var velocityPerForce = step.dt * this.GetParticleInvMass();
for (var i = 0; i < this.m_count; i++) {
///m_velocityBuffer.data[i] += velocityPerForce * m_forceBuffer[i];
vel_data[i].SelfMulAdd(velocityPerForce, this.m_forceBuffer[i]);
}
this.m_hasForce = false;
};
b2ParticleSystem.prototype.SolveColorMixing = function () {
// mixes color between contacting particles
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_colorBuffer.data) {
throw new Error();
}
var colorMixing = 0.5 * this.m_def.colorMixingStrength;
if (colorMixing) {
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
var a = contact.indexA;
var b = contact.indexB;
if (this.m_flagsBuffer.data[a] & this.m_flagsBuffer.data[b] &
exports.b2ParticleFlag.b2_colorMixingParticle) {
var colorA = this.m_colorBuffer.data[a];
var colorB = this.m_colorBuffer.data[b];
// Use the static method to ensure certain compilers inline
// this correctly.
b2Color.MixColors(colorA, colorB, colorMixing);
}
}
}
};
b2ParticleSystem.prototype.SolveZombie = function () {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
// removes particles with zombie flag
var newCount = 0;
///int32* newIndices = (int32*) this.m_world.m_stackAllocator.Allocate(sizeof(int32) * this.m_count);
var newIndices = []; // TODO: static
for (var i = 0; i < this.m_count; i++) {
newIndices[i] = b2_invalidParticleIndex;
}
// DEBUG: b2Assert(newIndices.length === this.m_count);
var allParticleFlags = 0;
for (var i = 0; i < this.m_count; i++) {
var flags = this.m_flagsBuffer.data[i];
if (flags & exports.b2ParticleFlag.b2_zombieParticle) {
var destructionListener = this.m_world.m_destructionListener;
if ((flags & exports.b2ParticleFlag.b2_destructionListenerParticle) && destructionListener) {
destructionListener.SayGoodbyeParticle(this, i);
}
// Destroy particle handle.
if (this.m_handleIndexBuffer.data) {
var handle = this.m_handleIndexBuffer.data[i];
if (handle) {
handle.SetIndex(b2_invalidParticleIndex);
this.m_handleIndexBuffer.data[i] = null;
///m_handleAllocator.Free(handle);
}
}
newIndices[i] = b2_invalidParticleIndex;
}
else {
newIndices[i] = newCount;
if (i !== newCount) {
// Update handle to reference new particle index.
if (this.m_handleIndexBuffer.data) {
var handle = this.m_handleIndexBuffer.data[i];
if (handle) {
handle.SetIndex(newCount);
}
this.m_handleIndexBuffer.data[newCount] = handle;
}
this.m_flagsBuffer.data[newCount] = this.m_flagsBuffer.data[i];
if (this.m_lastBodyContactStepBuffer.data) {
this.m_lastBodyContactStepBuffer.data[newCount] = this.m_lastBodyContactStepBuffer.data[i];
}
if (this.m_bodyContactCountBuffer.data) {
this.m_bodyContactCountBuffer.data[newCount] = this.m_bodyContactCountBuffer.data[i];
}
if (this.m_consecutiveContactStepsBuffer.data) {
this.m_consecutiveContactStepsBuffer.data[newCount] = this.m_consecutiveContactStepsBuffer.data[i];
}
this.m_positionBuffer.data[newCount].Copy(this.m_positionBuffer.data[i]);
this.m_velocityBuffer.data[newCount].Copy(this.m_velocityBuffer.data[i]);
this.m_groupBuffer[newCount] = this.m_groupBuffer[i];
if (this.m_hasForce) {
this.m_forceBuffer[newCount].Copy(this.m_forceBuffer[i]);
}
if (this.m_staticPressureBuffer) {
this.m_staticPressureBuffer[newCount] = this.m_staticPressureBuffer[i];
}
if (this.m_depthBuffer) {
this.m_depthBuffer[newCount] = this.m_depthBuffer[i];
}
if (this.m_colorBuffer.data) {
this.m_colorBuffer.data[newCount].Copy(this.m_colorBuffer.data[i]);
}
if (this.m_userDataBuffer.data) {
this.m_userDataBuffer.data[newCount] = this.m_userDataBuffer.data[i];
}
if (this.m_expirationTimeBuffer.data) {
this.m_expirationTimeBuffer.data[newCount] = this.m_expirationTimeBuffer.data[i];
}
}
newCount++;
allParticleFlags |= flags;
}
}
// predicate functions
var Test = {
///static bool IsProxyInvalid(const Proxy& proxy)
IsProxyInvalid: function (proxy) {
return proxy.index < 0;
},
///static bool IsContactInvalid(const b2ParticleContact& contact)
IsContactInvalid: function (contact) {
return contact.indexA < 0 || contact.indexB < 0;
},
///static bool IsBodyContactInvalid(const b2ParticleBodyContact& contact)
IsBodyContactInvalid: function (contact) {
return contact.index < 0;
},
///static bool IsPairInvalid(const b2ParticlePair& pair)
IsPairInvalid: function (pair) {
return pair.indexA < 0 || pair.indexB < 0;
},
///static bool IsTriadInvalid(const b2ParticleTriad& triad)
IsTriadInvalid: function (triad) {
return triad.indexA < 0 || triad.indexB < 0 || triad.indexC < 0;
},
};
// update proxies
for (var k = 0; k < this.m_proxyBuffer.count; k++) {
var proxy = this.m_proxyBuffer.data[k];
proxy.index = newIndices[proxy.index];
}
this.m_proxyBuffer.RemoveIf(Test.IsProxyInvalid);
// update contacts
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
contact.indexA = newIndices[contact.indexA];
contact.indexB = newIndices[contact.indexB];
}
this.m_contactBuffer.RemoveIf(Test.IsContactInvalid);
// update particle-body contacts
for (var k = 0; k < this.m_bodyContactBuffer.count; k++) {
var contact = this.m_bodyContactBuffer.data[k];
contact.index = newIndices[contact.index];
}
this.m_bodyContactBuffer.RemoveIf(Test.IsBodyContactInvalid);
// update pairs
for (var k = 0; k < this.m_pairBuffer.count; k++) {
var pair = this.m_pairBuffer.data[k];
pair.indexA = newIndices[pair.indexA];
pair.indexB = newIndices[pair.indexB];
}
this.m_pairBuffer.RemoveIf(Test.IsPairInvalid);
// update triads
for (var k = 0; k < this.m_triadBuffer.count; k++) {
var triad = this.m_triadBuffer.data[k];
triad.indexA = newIndices[triad.indexA];
triad.indexB = newIndices[triad.indexB];
triad.indexC = newIndices[triad.indexC];
}
this.m_triadBuffer.RemoveIf(Test.IsTriadInvalid);
// Update lifetime indices.
if (this.m_indexByExpirationTimeBuffer.data) {
var writeOffset = 0;
for (var readOffset = 0; readOffset < this.m_count; readOffset++) {
var newIndex = newIndices[this.m_indexByExpirationTimeBuffer.data[readOffset]];
if (newIndex !== b2_invalidParticleIndex) {
this.m_indexByExpirationTimeBuffer.data[writeOffset++] = newIndex;
}
}
}
// update groups
for (var group = this.m_groupList; group; group = group.GetNext()) {
var firstIndex = newCount;
var lastIndex = 0;
var modified = false;
for (var i = group.m_firstIndex; i < group.m_lastIndex; i++) {
var j = newIndices[i];
if (j >= 0) {
firstIndex = b2Min(firstIndex, j);
lastIndex = b2Max(lastIndex, j + 1);
}
else {
modified = true;
}
}
if (firstIndex < lastIndex) {
group.m_firstIndex = firstIndex;
group.m_lastIndex = lastIndex;
if (modified) {
if (group.m_groupFlags & exports.b2ParticleGroupFlag.b2_solidParticleGroup) {
this.SetGroupFlags(group, group.m_groupFlags | exports.b2ParticleGroupFlag.b2_particleGroupNeedsUpdateDepth);
}
}
}
else {
group.m_firstIndex = 0;
group.m_lastIndex = 0;
if (!(group.m_groupFlags & exports.b2ParticleGroupFlag.b2_particleGroupCanBeEmpty)) {
this.SetGroupFlags(group, group.m_groupFlags | exports.b2ParticleGroupFlag.b2_particleGroupWillBeDestroyed);
}
}
}
// update particle count
this.m_count = newCount;
///m_world.m_stackAllocator.Free(newIndices);
this.m_allParticleFlags = allParticleFlags;
this.m_needsUpdateAllParticleFlags = false;
// destroy bodies with no particles
for (var group = this.m_groupList; group;) {
var next = group.GetNext();
if (group.m_groupFlags & exports.b2ParticleGroupFlag.b2_particleGroupWillBeDestroyed) {
this.DestroyParticleGroup(group);
}
group = next;
}
};
/**
* Destroy all particles which have outlived their lifetimes set
* by SetParticleLifetime().
*/
b2ParticleSystem.prototype.SolveLifetimes = function (step) {
if (!this.m_expirationTimeBuffer.data) {
throw new Error();
}
if (!this.m_indexByExpirationTimeBuffer.data) {
throw new Error();
}
// Update the time elapsed.
this.m_timeElapsed = this.LifetimeToExpirationTime(step.dt);
// Get the floor (non-fractional component) of the elapsed time.
var quantizedTimeElapsed = this.GetQuantizedTimeElapsed();
var expirationTimes = this.m_expirationTimeBuffer.data;
var expirationTimeIndices = this.m_indexByExpirationTimeBuffer.data;
var particleCount = this.GetParticleCount();
// Sort the lifetime buffer if it's required.
if (this.m_expirationTimeBufferRequiresSorting) {
///const ExpirationTimeComparator expirationTimeComparator(expirationTimes);
///std::sort(expirationTimeIndices, expirationTimeIndices + particleCount, expirationTimeComparator);
/**
* Compare the lifetime of particleIndexA and particleIndexB
* returning true if the lifetime of A is greater than B for
* particles that will expire. If either particle's lifetime is
* infinite (<= 0.0f) this function return true if the lifetime
* of A is lesser than B. When used with std::sort() this
* results in an array of particle indicies sorted in reverse
* order by particle lifetime.
*
* For example, the set of lifetimes
* (1.0, 0.7, 0.3, 0.0, -1.0, 2.0)
* would be sorted as
* (0.0, 1.0, -2.0, 1.0, 0.7, 0.3)
*/
var ExpirationTimeComparator = function (particleIndexA, particleIndexB) {
var expirationTimeA = expirationTimes[particleIndexA];
var expirationTimeB = expirationTimes[particleIndexB];
var infiniteExpirationTimeA = expirationTimeA <= 0.0;
var infiniteExpirationTimeB = expirationTimeB <= 0.0;
return infiniteExpirationTimeA === infiniteExpirationTimeB ?
expirationTimeA > expirationTimeB : infiniteExpirationTimeA;
};
std_sort(expirationTimeIndices, 0, particleCount, ExpirationTimeComparator);
this.m_expirationTimeBufferRequiresSorting = false;
}
// Destroy particles which have expired.
for (var i = particleCount - 1; i >= 0; --i) {
var particleIndex = expirationTimeIndices[i];
var expirationTime = expirationTimes[particleIndex];
// If no particles need to be destroyed, skip this.
if (quantizedTimeElapsed < expirationTime || expirationTime <= 0) {
break;
}
// Destroy this particle.
this.DestroyParticle(particleIndex);
}
};
b2ParticleSystem.prototype.RotateBuffer = function (start, mid, end) {
// move the particles assigned to the given group toward the end of array
if (start === mid || mid === end) {
return;
}
// DEBUG: b2Assert(mid >= start && mid <= end);
function newIndices(i) {
if (i < start) {
return i;
}
else if (i < mid) {
return i + end - mid;
}
else if (i < end) {
return i + start - mid;
}
else {
return i;
}
}
if (!this.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_velocityBuffer.data) {
throw new Error();
}
///std::rotate(m_flagsBuffer.data + start, m_flagsBuffer.data + mid, m_flagsBuffer.data + end);
std_rotate(this.m_flagsBuffer.data, start, mid, end);
if (this.m_lastBodyContactStepBuffer.data) {
///std::rotate(m_lastBodyContactStepBuffer.data + start, m_lastBodyContactStepBuffer.data + mid, m_lastBodyContactStepBuffer.data + end);
std_rotate(this.m_lastBodyContactStepBuffer.data, start, mid, end);
}
if (this.m_bodyContactCountBuffer.data) {
///std::rotate(m_bodyContactCountBuffer.data + start, m_bodyContactCountBuffer.data + mid, m_bodyContactCountBuffer.data + end);
std_rotate(this.m_bodyContactCountBuffer.data, start, mid, end);
}
if (this.m_consecutiveContactStepsBuffer.data) {
///std::rotate(m_consecutiveContactStepsBuffer.data + start, m_consecutiveContactStepsBuffer.data + mid, m_consecutiveContactStepsBuffer.data + end);
std_rotate(this.m_consecutiveContactStepsBuffer.data, start, mid, end);
}
///std::rotate(m_positionBuffer.data + start, m_positionBuffer.data + mid, m_positionBuffer.data + end);
std_rotate(this.m_positionBuffer.data, start, mid, end);
///std::rotate(m_velocityBuffer.data + start, m_velocityBuffer.data + mid, m_velocityBuffer.data + end);
std_rotate(this.m_velocityBuffer.data, start, mid, end);
///std::rotate(m_groupBuffer + start, m_groupBuffer + mid, m_groupBuffer + end);
std_rotate(this.m_groupBuffer, start, mid, end);
if (this.m_hasForce) {
///std::rotate(m_forceBuffer + start, m_forceBuffer + mid, m_forceBuffer + end);
std_rotate(this.m_forceBuffer, start, mid, end);
}
if (this.m_staticPressureBuffer) {
///std::rotate(m_staticPressureBuffer + start, m_staticPressureBuffer + mid, m_staticPressureBuffer + end);
std_rotate(this.m_staticPressureBuffer, start, mid, end);
}
if (this.m_depthBuffer) {
///std::rotate(m_depthBuffer + start, m_depthBuffer + mid, m_depthBuffer + end);
std_rotate(this.m_depthBuffer, start, mid, end);
}
if (this.m_colorBuffer.data) {
///std::rotate(m_colorBuffer.data + start, m_colorBuffer.data + mid, m_colorBuffer.data + end);
std_rotate(this.m_colorBuffer.data, start, mid, end);
}
if (this.m_userDataBuffer.data) {
///std::rotate(m_userDataBuffer.data + start, m_userDataBuffer.data + mid, m_userDataBuffer.data + end);
std_rotate(this.m_userDataBuffer.data, start, mid, end);
}
// Update handle indices.
if (this.m_handleIndexBuffer.data) {
///std::rotate(m_handleIndexBuffer.data + start, m_handleIndexBuffer.data + mid, m_handleIndexBuffer.data + end);
std_rotate(this.m_handleIndexBuffer.data, start, mid, end);
for (var i = start; i < end; ++i) {
var handle = this.m_handleIndexBuffer.data[i];
if (handle) {
handle.SetIndex(newIndices(handle.GetIndex()));
}
}
}
if (this.m_expirationTimeBuffer.data) {
///std::rotate(m_expirationTimeBuffer.data + start, m_expirationTimeBuffer.data + mid, m_expirationTimeBuffer.data + end);
std_rotate(this.m_expirationTimeBuffer.data, start, mid, end);
// Update expiration time buffer indices.
var particleCount = this.GetParticleCount();
if (!this.m_indexByExpirationTimeBuffer.data) {
throw new Error();
}
var indexByExpirationTime = this.m_indexByExpirationTimeBuffer.data;
for (var i = 0; i < particleCount; ++i) {
indexByExpirationTime[i] = newIndices(indexByExpirationTime[i]);
}
}
// update proxies
for (var k = 0; k < this.m_proxyBuffer.count; k++) {
var proxy = this.m_proxyBuffer.data[k];
proxy.index = newIndices(proxy.index);
}
// update contacts
for (var k = 0; k < this.m_contactBuffer.count; k++) {
var contact = this.m_contactBuffer.data[k];
contact.indexA = newIndices(contact.indexA);
contact.indexB = newIndices(contact.indexB);
}
// update particle-body contacts
for (var k = 0; k < this.m_bodyContactBuffer.count; k++) {
var contact = this.m_bodyContactBuffer.data[k];
contact.index = newIndices(contact.index);
}
// update pairs
for (var k = 0; k < this.m_pairBuffer.count; k++) {
var pair = this.m_pairBuffer.data[k];
pair.indexA = newIndices(pair.indexA);
pair.indexB = newIndices(pair.indexB);
}
// update triads
for (var k = 0; k < this.m_triadBuffer.count; k++) {
var triad = this.m_triadBuffer.data[k];
triad.indexA = newIndices(triad.indexA);
triad.indexB = newIndices(triad.indexB);
triad.indexC = newIndices(triad.indexC);
}
// update groups
for (var group = this.m_groupList; group; group = group.GetNext()) {
group.m_firstIndex = newIndices(group.m_firstIndex);
group.m_lastIndex = newIndices(group.m_lastIndex - 1) + 1;
}
};
b2ParticleSystem.prototype.GetCriticalVelocity = function (step) {
return this.m_particleDiameter * step.inv_dt;
};
b2ParticleSystem.prototype.GetCriticalVelocitySquared = function (step) {
var velocity = this.GetCriticalVelocity(step);
return velocity * velocity;
};
b2ParticleSystem.prototype.GetCriticalPressure = function (step) {
return this.m_def.density * this.GetCriticalVelocitySquared(step);
};
b2ParticleSystem.prototype.GetParticleStride = function () {
return b2_particleStride * this.m_particleDiameter;
};
b2ParticleSystem.prototype.GetParticleMass = function () {
var stride = this.GetParticleStride();
return this.m_def.density * stride * stride;
};
b2ParticleSystem.prototype.GetParticleInvMass = function () {
///return 1.777777 * this.m_inverseDensity * this.m_inverseDiameter * this.m_inverseDiameter;
// mass = density * stride^2, so we take the inverse of this.
var inverseStride = this.m_inverseDiameter * (1.0 / b2_particleStride);
return this.m_inverseDensity * inverseStride * inverseStride;
};
/**
* Get the world's contact filter if any particles with the
* b2_contactFilterParticle flag are present in the system.
*/
b2ParticleSystem.prototype.GetFixtureContactFilter = function () {
return (this.m_allParticleFlags & exports.b2ParticleFlag.b2_fixtureContactFilterParticle) ?
this.m_world.m_contactManager.m_contactFilter : null;
};
/**
* Get the world's contact filter if any particles with the
* b2_particleContactFilterParticle flag are present in the
* system.
*/
b2ParticleSystem.prototype.GetParticleContactFilter = function () {
return (this.m_allParticleFlags & exports.b2ParticleFlag.b2_particleContactFilterParticle) ?
this.m_world.m_contactManager.m_contactFilter : null;
};
/**
* Get the world's contact listener if any particles with the
* b2_fixtureContactListenerParticle flag are present in the
* system.
*/
b2ParticleSystem.prototype.GetFixtureContactListener = function () {
return (this.m_allParticleFlags & exports.b2ParticleFlag.b2_fixtureContactListenerParticle) ?
this.m_world.m_contactManager.m_contactListener : null;
};
/**
* Get the world's contact listener if any particles with the
* b2_particleContactListenerParticle flag are present in the
* system.
*/
b2ParticleSystem.prototype.GetParticleContactListener = function () {
return (this.m_allParticleFlags & exports.b2ParticleFlag.b2_particleContactListenerParticle) ?
this.m_world.m_contactManager.m_contactListener : null;
};
b2ParticleSystem.prototype.SetUserOverridableBuffer = function (buffer, newData, newCapacity) {
// DEBUG: b2Assert(((newData !== null) && (newCapacity > 0)) || ((newData === null) && (newCapacity === 0)));
///if (!buffer.userSuppliedCapacity)
///{
///this.m_world.m_blockAllocator.Free(buffer.data, sizeof(T) * m_internalAllocatedCapacity);
///}
buffer.data = newData;
buffer.userSuppliedCapacity = newCapacity;
};
b2ParticleSystem.prototype.SetGroupFlags = function (group, newFlags) {
var oldFlags = group.m_groupFlags;
if ((oldFlags ^ newFlags) & exports.b2ParticleGroupFlag.b2_solidParticleGroup) {
// If the b2_solidParticleGroup flag changed schedule depth update.
newFlags |= exports.b2ParticleGroupFlag.b2_particleGroupNeedsUpdateDepth;
}
if (oldFlags & ~newFlags) {
// If any flags might be removed
this.m_needsUpdateAllGroupFlags = true;
}
if (~this.m_allGroupFlags & newFlags) {
// If any flags were added
if (newFlags & exports.b2ParticleGroupFlag.b2_solidParticleGroup) {
this.m_depthBuffer = this.RequestBuffer(this.m_depthBuffer);
}
this.m_allGroupFlags |= newFlags;
}
group.m_groupFlags = newFlags;
};
b2ParticleSystem.BodyContactCompare = function (lhs, rhs) {
if (lhs.index === rhs.index) {
// Subsort by weight, decreasing.
return lhs.weight > rhs.weight;
}
return lhs.index < rhs.index;
};
b2ParticleSystem.prototype.RemoveSpuriousBodyContacts = function () {
// At this point we have a list of contact candidates based on AABB
// overlap.The AABB query that generated this returns all collidable
// fixtures overlapping particle bounding boxes. This breaks down around
// vertices where two shapes intersect, such as a "ground" surface made
// of multiple b2PolygonShapes; it potentially applies a lot of spurious
// impulses from normals that should not actually contribute. See the
// Ramp example in Testbed.
//
// To correct for this, we apply this algorithm:
// * sort contacts by particle and subsort by weight (nearest to farthest)
// * for each contact per particle:
// - project a point at the contact distance along the inverse of the
// contact normal
// - if this intersects the fixture that generated the contact, apply
// it, otherwise discard as impossible
// - repeat for up to n nearest contacts, currently we get good results
// from n=3.
///std::sort(m_bodyContactBuffer.Begin(), m_bodyContactBuffer.End(), b2ParticleSystem::BodyContactCompare);
std_sort(this.m_bodyContactBuffer.data, 0, this.m_bodyContactBuffer.count, b2ParticleSystem.BodyContactCompare);
///int32 discarded = 0;
///std::remove_if(m_bodyContactBuffer.Begin(), m_bodyContactBuffer.End(), b2ParticleBodyContactRemovePredicate(this, &discarded));
///
///m_bodyContactBuffer.SetCount(m_bodyContactBuffer.GetCount() - discarded);
var s_n = b2ParticleSystem.RemoveSpuriousBodyContacts_s_n;
var s_pos = b2ParticleSystem.RemoveSpuriousBodyContacts_s_pos;
var s_normal = b2ParticleSystem.RemoveSpuriousBodyContacts_s_normal;
// Max number of contacts processed per particle, from nearest to farthest.
// This must be at least 2 for correctness with concave shapes; 3 was
// experimentally arrived at as looking reasonable.
var k_maxContactsPerPoint = 3;
var system = this;
// Index of last particle processed.
var lastIndex = -1;
// Number of contacts processed for the current particle.
var currentContacts = 0;
// Output the number of discarded contacts.
// let discarded = 0;
var b2ParticleBodyContactRemovePredicate = function (contact) {
// This implements the selection criteria described in
// RemoveSpuriousBodyContacts().
// This functor is iterating through a list of Body contacts per
// Particle, ordered from near to far. For up to the maximum number of
// contacts we allow per point per step, we verify that the contact
// normal of the Body that genenerated the contact makes physical sense
// by projecting a point back along that normal and seeing if it
// intersects the fixture generating the contact.
if (contact.index !== lastIndex) {
currentContacts = 0;
lastIndex = contact.index;
}
if (currentContacts++ > k_maxContactsPerPoint) {
// ++discarded;
return true;
}
// Project along inverse normal (as returned in the contact) to get the
// point to check.
///b2Vec2 n = contact.normal;
var n = s_n.Copy(contact.normal);
// weight is 1-(inv(diameter) * distance)
///n *= system.m_particleDiameter * (1 - contact.weight);
n.SelfMul(system.m_particleDiameter * (1 - contact.weight));
///b2Vec2 pos = system.m_positionBuffer.data[contact.index] + n;
if (!system.m_positionBuffer.data) {
throw new Error();
}
var pos = b2Vec2.AddVV(system.m_positionBuffer.data[contact.index], n, s_pos);
// pos is now a point projected back along the contact normal to the
// contact distance. If the surface makes sense for a contact, pos will
// now lie on or in the fixture generating
if (!contact.fixture.TestPoint(pos)) {
var childCount = contact.fixture.GetShape().GetChildCount();
for (var childIndex = 0; childIndex < childCount; childIndex++) {
var normal = s_normal;
var distance = contact.fixture.ComputeDistance(pos, normal, childIndex);
if (distance < b2_linearSlop) {
return false;
}
}
// ++discarded;
return true;
}
return false;
};
this.m_bodyContactBuffer.count = std_remove_if(this.m_bodyContactBuffer.data, b2ParticleBodyContactRemovePredicate, this.m_bodyContactBuffer.count);
};
b2ParticleSystem.prototype.DetectStuckParticle = function (particle) {
// Detect stuck particles
//
// The basic algorithm is to allow the user to specify an optional
// threshold where we detect whenever a particle is contacting
// more than one fixture for more than threshold consecutive
// steps. This is considered to be "stuck", and these are put
// in a list the user can query per step, if enabled, to deal with
// such particles.
if (this.m_stuckThreshold <= 0) {
return;
}
if (!this.m_bodyContactCountBuffer.data) {
throw new Error();
}
if (!this.m_consecutiveContactStepsBuffer.data) {
throw new Error();
}
if (!this.m_lastBodyContactStepBuffer.data) {
throw new Error();
}
// Get the state variables for this particle.
///int32 * const consecutiveCount = &m_consecutiveContactStepsBuffer.data[particle];
///int32 * const lastStep = &m_lastBodyContactStepBuffer.data[particle];
///int32 * const bodyCount = &m_bodyContactCountBuffer.data[particle];
// This is only called when there is a body contact for this particle.
///++(*bodyCount);
++this.m_bodyContactCountBuffer.data[particle];
// We want to only trigger detection once per step, the first time we
// contact more than one fixture in a step for a given particle.
///if (*bodyCount === 2)
if (this.m_bodyContactCountBuffer.data[particle] === 2) {
///++(*consecutiveCount);
++this.m_consecutiveContactStepsBuffer.data[particle];
///if (*consecutiveCount > m_stuckThreshold)
if (this.m_consecutiveContactStepsBuffer.data[particle] > this.m_stuckThreshold) {
///int32& newStuckParticle = m_stuckParticleBuffer.Append();
///newStuckParticle = particle;
this.m_stuckParticleBuffer.data[this.m_stuckParticleBuffer.Append()] = particle;
}
}
///*lastStep = m_timestamp;
this.m_lastBodyContactStepBuffer.data[particle] = this.m_timestamp;
};
/**
* Determine whether a particle index is valid.
*/
b2ParticleSystem.prototype.ValidateParticleIndex = function (index) {
return index >= 0 && index < this.GetParticleCount() &&
index !== b2_invalidParticleIndex;
};
/**
* Get the time elapsed in
* b2ParticleSystemDef::lifetimeGranularity.
*/
b2ParticleSystem.prototype.GetQuantizedTimeElapsed = function () {
///return (int32)(m_timeElapsed >> 32);
return Math.floor(this.m_timeElapsed / 0x100000000);
};
/**
* Convert a lifetime in seconds to an expiration time.
*/
b2ParticleSystem.prototype.LifetimeToExpirationTime = function (lifetime) {
///return m_timeElapsed + (int64)((lifetime / m_def.lifetimeGranularity) * (float32)(1LL << 32));
return this.m_timeElapsed + Math.floor(((lifetime / this.m_def.lifetimeGranularity) * 0x100000000));
};
b2ParticleSystem.prototype.ForceCanBeApplied = function (flags) {
return !(flags & exports.b2ParticleFlag.b2_wallParticle);
};
b2ParticleSystem.prototype.PrepareForceBuffer = function () {
if (!this.m_hasForce) {
///memset(m_forceBuffer, 0, sizeof(*m_forceBuffer) * m_count);
for (var i = 0; i < this.m_count; i++) {
this.m_forceBuffer[i].SetZero();
}
this.m_hasForce = true;
}
};
b2ParticleSystem.prototype.IsRigidGroup = function (group) {
return (group !== null) && ((group.m_groupFlags & exports.b2ParticleGroupFlag.b2_rigidParticleGroup) !== 0);
};
b2ParticleSystem.prototype.GetLinearVelocity = function (group, particleIndex, point, out) {
if (group && this.IsRigidGroup(group)) {
return group.GetLinearVelocityFromWorldPoint(point, out);
}
else {
if (!this.m_velocityBuffer.data) {
throw new Error();
}
///return m_velocityBuffer.data[particleIndex];
return out.Copy(this.m_velocityBuffer.data[particleIndex]);
}
};
b2ParticleSystem.prototype.InitDampingParameter = function (invMass, invInertia, tangentDistance, mass, inertia, center, point, normal) {
///*invMass = mass > 0 ? 1 / mass : 0;
invMass[0] = mass > 0 ? 1 / mass : 0;
///*invInertia = inertia > 0 ? 1 / inertia : 0;
invInertia[0] = inertia > 0 ? 1 / inertia : 0;
///*tangentDistance = b2Cross(point - center, normal);
tangentDistance[0] = b2Vec2.CrossVV(b2Vec2.SubVV(point, center, b2Vec2.s_t0), normal);
};
b2ParticleSystem.prototype.InitDampingParameterWithRigidGroupOrParticle = function (invMass, invInertia, tangentDistance, isRigidGroup, group, particleIndex, point, normal) {
if (group && isRigidGroup) {
this.InitDampingParameter(invMass, invInertia, tangentDistance, group.GetMass(), group.GetInertia(), group.GetCenter(), point, normal);
}
else {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
var flags = this.m_flagsBuffer.data[particleIndex];
this.InitDampingParameter(invMass, invInertia, tangentDistance, flags & exports.b2ParticleFlag.b2_wallParticle ? 0 : this.GetParticleMass(), 0, point, point, normal);
}
};
b2ParticleSystem.prototype.ComputeDampingImpulse = function (invMassA, invInertiaA, tangentDistanceA, invMassB, invInertiaB, tangentDistanceB, normalVelocity) {
var invMass = invMassA + invInertiaA * tangentDistanceA * tangentDistanceA +
invMassB + invInertiaB * tangentDistanceB * tangentDistanceB;
return invMass > 0 ? normalVelocity / invMass : 0;
};
b2ParticleSystem.prototype.ApplyDamping = function (invMass, invInertia, tangentDistance, isRigidGroup, group, particleIndex, impulse, normal) {
if (group && isRigidGroup) {
///group.m_linearVelocity += impulse * invMass * normal;
group.m_linearVelocity.SelfMulAdd(impulse * invMass, normal);
///group.m_angularVelocity += impulse * tangentDistance * invInertia;
group.m_angularVelocity += impulse * tangentDistance * invInertia;
}
else {
if (!this.m_velocityBuffer.data) {
throw new Error();
}
///m_velocityBuffer.data[particleIndex] += impulse * invMass * normal;
this.m_velocityBuffer.data[particleIndex].SelfMulAdd(impulse * invMass, normal);
}
};
b2ParticleSystem.xTruncBits = 12;
b2ParticleSystem.yTruncBits = 12;
b2ParticleSystem.tagBits = 8 * 4; // 8u * sizeof(uint32);
b2ParticleSystem.yOffset = 1 << (b2ParticleSystem.yTruncBits - 1);
b2ParticleSystem.yShift = b2ParticleSystem.tagBits - b2ParticleSystem.yTruncBits;
b2ParticleSystem.xShift = b2ParticleSystem.tagBits - b2ParticleSystem.yTruncBits - b2ParticleSystem.xTruncBits;
b2ParticleSystem.xScale = 1 << b2ParticleSystem.xShift;
b2ParticleSystem.xOffset = b2ParticleSystem.xScale * (1 << (b2ParticleSystem.xTruncBits - 1));
b2ParticleSystem.yMask = ((1 << b2ParticleSystem.yTruncBits) - 1) << b2ParticleSystem.yShift;
b2ParticleSystem.xMask = ~b2ParticleSystem.yMask;
b2ParticleSystem.DestroyParticlesInShape_s_aabb = new b2AABB();
b2ParticleSystem.CreateParticleGroup_s_transform = new b2Transform();
b2ParticleSystem.ComputeCollisionEnergy_s_v = new b2Vec2();
b2ParticleSystem.QueryShapeAABB_s_aabb = new b2AABB();
b2ParticleSystem.QueryPointAABB_s_aabb = new b2AABB();
b2ParticleSystem.RayCast_s_aabb = new b2AABB();
b2ParticleSystem.RayCast_s_p = new b2Vec2();
b2ParticleSystem.RayCast_s_v = new b2Vec2();
b2ParticleSystem.RayCast_s_n = new b2Vec2();
b2ParticleSystem.RayCast_s_point = new b2Vec2();
/**
* All particle types that require creating pairs
*/
b2ParticleSystem.k_pairFlags = exports.b2ParticleFlag.b2_springParticle;
/**
* All particle types that require creating triads
*/
b2ParticleSystem.k_triadFlags = exports.b2ParticleFlag.b2_elasticParticle;
/**
* All particle types that do not produce dynamic pressure
*/
b2ParticleSystem.k_noPressureFlags = exports.b2ParticleFlag.b2_powderParticle | exports.b2ParticleFlag.b2_tensileParticle;
/**
* All particle types that apply extra damping force with bodies
*/
b2ParticleSystem.k_extraDampingFlags = exports.b2ParticleFlag.b2_staticPressureParticle;
b2ParticleSystem.k_barrierWallFlags = exports.b2ParticleFlag.b2_barrierParticle | exports.b2ParticleFlag.b2_wallParticle;
b2ParticleSystem.CreateParticlesStrokeShapeForGroup_s_edge = new b2EdgeShape();
b2ParticleSystem.CreateParticlesStrokeShapeForGroup_s_d = new b2Vec2();
b2ParticleSystem.CreateParticlesStrokeShapeForGroup_s_p = new b2Vec2();
b2ParticleSystem.CreateParticlesFillShapeForGroup_s_aabb = new b2AABB();
b2ParticleSystem.CreateParticlesFillShapeForGroup_s_p = new b2Vec2();
b2ParticleSystem.UpdatePairsAndTriads_s_dab = new b2Vec2();
b2ParticleSystem.UpdatePairsAndTriads_s_dbc = new b2Vec2();
b2ParticleSystem.UpdatePairsAndTriads_s_dca = new b2Vec2();
b2ParticleSystem.AddContact_s_d = new b2Vec2();
b2ParticleSystem.UpdateBodyContacts_s_aabb = new b2AABB();
b2ParticleSystem.Solve_s_subStep = new b2TimeStep();
b2ParticleSystem.SolveCollision_s_aabb = new b2AABB();
b2ParticleSystem.SolveGravity_s_gravity = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_aabb = new b2AABB();
b2ParticleSystem.SolveBarrier_s_va = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_vb = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_pba = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_vba = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_vc = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_pca = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_vca = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_qba = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_qca = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_dv = new b2Vec2();
b2ParticleSystem.SolveBarrier_s_f = new b2Vec2();
b2ParticleSystem.SolvePressure_s_f = new b2Vec2();
b2ParticleSystem.SolveDamping_s_v = new b2Vec2();
b2ParticleSystem.SolveDamping_s_f = new b2Vec2();
b2ParticleSystem.SolveRigidDamping_s_t0 = new b2Vec2();
b2ParticleSystem.SolveRigidDamping_s_t1 = new b2Vec2();
b2ParticleSystem.SolveRigidDamping_s_p = new b2Vec2();
b2ParticleSystem.SolveRigidDamping_s_v = new b2Vec2();
b2ParticleSystem.SolveExtraDamping_s_v = new b2Vec2();
b2ParticleSystem.SolveExtraDamping_s_f = new b2Vec2();
b2ParticleSystem.SolveRigid_s_position = new b2Vec2();
b2ParticleSystem.SolveRigid_s_rotation = new b2Rot();
b2ParticleSystem.SolveRigid_s_transform = new b2Transform();
b2ParticleSystem.SolveRigid_s_velocityTransform = new b2Transform();
b2ParticleSystem.SolveElastic_s_pa = new b2Vec2();
b2ParticleSystem.SolveElastic_s_pb = new b2Vec2();
b2ParticleSystem.SolveElastic_s_pc = new b2Vec2();
b2ParticleSystem.SolveElastic_s_r = new b2Rot();
b2ParticleSystem.SolveElastic_s_t0 = new b2Vec2();
b2ParticleSystem.SolveSpring_s_pa = new b2Vec2();
b2ParticleSystem.SolveSpring_s_pb = new b2Vec2();
b2ParticleSystem.SolveSpring_s_d = new b2Vec2();
b2ParticleSystem.SolveSpring_s_f = new b2Vec2();
b2ParticleSystem.SolveTensile_s_weightedNormal = new b2Vec2();
b2ParticleSystem.SolveTensile_s_s = new b2Vec2();
b2ParticleSystem.SolveTensile_s_f = new b2Vec2();
b2ParticleSystem.SolveViscous_s_v = new b2Vec2();
b2ParticleSystem.SolveViscous_s_f = new b2Vec2();
b2ParticleSystem.SolveRepulsive_s_f = new b2Vec2();
b2ParticleSystem.SolvePowder_s_f = new b2Vec2();
b2ParticleSystem.SolveSolid_s_f = new b2Vec2();
b2ParticleSystem.RemoveSpuriousBodyContacts_s_n = new b2Vec2();
b2ParticleSystem.RemoveSpuriousBodyContacts_s_pos = new b2Vec2();
b2ParticleSystem.RemoveSpuriousBodyContacts_s_normal = new b2Vec2();
return b2ParticleSystem;
}());
(function (b2ParticleSystem) {
var UserOverridableBuffer = /** @class */ (function () {
function UserOverridableBuffer() {
this.data = null;
this.userSuppliedCapacity = 0;
}
return UserOverridableBuffer;
}());
b2ParticleSystem.UserOverridableBuffer = UserOverridableBuffer;
var Proxy = /** @class */ (function () {
function Proxy() {
this.index = b2_invalidParticleIndex;
this.tag = 0;
}
Proxy.CompareProxyProxy = function (a, b) {
return a.tag < b.tag;
};
Proxy.CompareTagProxy = function (a, b) {
return a < b.tag;
};
Proxy.CompareProxyTag = function (a, b) {
return a.tag < b;
};
return Proxy;
}());
b2ParticleSystem.Proxy = Proxy;
var InsideBoundsEnumerator = /** @class */ (function () {
/**
* InsideBoundsEnumerator enumerates all particles inside the
* given bounds.
*
* Construct an enumerator with bounds of tags and a range of
* proxies.
*/
function InsideBoundsEnumerator(system, lower, upper, first, last) {
this.m_system = system;
this.m_xLower = (lower & b2ParticleSystem.xMask) >>> 0;
this.m_xUpper = (upper & b2ParticleSystem.xMask) >>> 0;
this.m_yLower = (lower & b2ParticleSystem.yMask) >>> 0;
this.m_yUpper = (upper & b2ParticleSystem.yMask) >>> 0;
this.m_first = first;
this.m_last = last;
// DEBUG: b2Assert(this.m_first <= this.m_last);
}
/**
* Get index of the next particle. Returns
* b2_invalidParticleIndex if there are no more particles.
*/
InsideBoundsEnumerator.prototype.GetNext = function () {
while (this.m_first < this.m_last) {
var xTag = (this.m_system.m_proxyBuffer.data[this.m_first].tag & b2ParticleSystem.xMask) >>> 0;
// #if B2_ASSERT_ENABLED
// DEBUG: const yTag = (this.m_system.m_proxyBuffer.data[this.m_first].tag & b2ParticleSystem.yMask) >>> 0;
// DEBUG: b2Assert(yTag >= this.m_yLower);
// DEBUG: b2Assert(yTag <= this.m_yUpper);
// #endif
if (xTag >= this.m_xLower && xTag <= this.m_xUpper) {
return (this.m_system.m_proxyBuffer.data[this.m_first++]).index;
}
this.m_first++;
}
return b2_invalidParticleIndex;
};
return InsideBoundsEnumerator;
}());
b2ParticleSystem.InsideBoundsEnumerator = InsideBoundsEnumerator;
var ParticleListNode = /** @class */ (function () {
function ParticleListNode() {
/**
* The next node in the list.
*/
this.next = null;
/**
* Number of entries in the list. Valid only for the node at the
* head of the list.
*/
this.count = 0;
/**
* Particle index.
*/
this.index = 0;
}
return ParticleListNode;
}());
b2ParticleSystem.ParticleListNode = ParticleListNode;
/**
* @constructor
*/
var FixedSetAllocator = /** @class */ (function () {
function FixedSetAllocator() {
}
FixedSetAllocator.prototype.Allocate = function (itemSize, count) {
// TODO
return count;
};
FixedSetAllocator.prototype.Clear = function () {
// TODO
};
FixedSetAllocator.prototype.GetCount = function () {
// TODO
return 0;
};
FixedSetAllocator.prototype.Invalidate = function (itemIndex) {
// TODO
};
FixedSetAllocator.prototype.GetValidBuffer = function () {
// TODO
return [];
};
FixedSetAllocator.prototype.GetBuffer = function () {
// TODO
return [];
};
FixedSetAllocator.prototype.SetCount = function (count) {
// TODO
};
return FixedSetAllocator;
}());
b2ParticleSystem.FixedSetAllocator = FixedSetAllocator;
var FixtureParticle = /** @class */ (function () {
function FixtureParticle(fixture, particle) {
this.second = b2_invalidParticleIndex;
this.first = fixture;
this.second = particle;
}
return FixtureParticle;
}());
b2ParticleSystem.FixtureParticle = FixtureParticle;
var FixtureParticleSet = /** @class */ (function (_super) {
__extends(FixtureParticleSet, _super);
function FixtureParticleSet() {
return _super !== null && _super.apply(this, arguments) || this;
}
FixtureParticleSet.prototype.Initialize = function (bodyContactBuffer, flagsBuffer) {
// TODO
};
FixtureParticleSet.prototype.Find = function (pair) {
// TODO
return b2_invalidParticleIndex;
};
return FixtureParticleSet;
}(b2ParticleSystem.FixedSetAllocator));
b2ParticleSystem.FixtureParticleSet = FixtureParticleSet;
var ParticlePair = /** @class */ (function () {
function ParticlePair(particleA, particleB) {
this.first = b2_invalidParticleIndex;
this.second = b2_invalidParticleIndex;
this.first = particleA;
this.second = particleB;
}
return ParticlePair;
}());
b2ParticleSystem.ParticlePair = ParticlePair;
var b2ParticlePairSet = /** @class */ (function (_super) {
__extends(b2ParticlePairSet, _super);
function b2ParticlePairSet() {
return _super !== null && _super.apply(this, arguments) || this;
}
b2ParticlePairSet.prototype.Initialize = function (contactBuffer, flagsBuffer) {
// TODO
};
b2ParticlePairSet.prototype.Find = function (pair) {
// TODO
return b2_invalidParticleIndex;
};
return b2ParticlePairSet;
}(b2ParticleSystem.FixedSetAllocator));
b2ParticleSystem.b2ParticlePairSet = b2ParticlePairSet;
var ConnectionFilter = /** @class */ (function () {
function ConnectionFilter() {
}
/**
* Is the particle necessary for connection?
* A pair or a triad should contain at least one 'necessary'
* particle.
*/
ConnectionFilter.prototype.IsNecessary = function (index) {
return true;
};
/**
* An additional condition for creating a pair.
*/
ConnectionFilter.prototype.ShouldCreatePair = function (a, b) {
return true;
};
/**
* An additional condition for creating a triad.
*/
ConnectionFilter.prototype.ShouldCreateTriad = function (a, b, c) {
return true;
};
return ConnectionFilter;
}());
b2ParticleSystem.ConnectionFilter = ConnectionFilter;
var DestroyParticlesInShapeCallback = /** @class */ (function (_super) {
__extends(DestroyParticlesInShapeCallback, _super);
function DestroyParticlesInShapeCallback(system, shape, xf, callDestructionListener) {
var _this = _super.call(this) || this;
_this.m_callDestructionListener = false;
_this.m_destroyed = 0;
_this.m_system = system;
_this.m_shape = shape;
_this.m_xf = xf;
_this.m_callDestructionListener = callDestructionListener;
_this.m_destroyed = 0;
return _this;
}
DestroyParticlesInShapeCallback.prototype.ReportFixture = function (fixture) {
return false;
};
DestroyParticlesInShapeCallback.prototype.ReportParticle = function (particleSystem, index) {
if (particleSystem !== this.m_system) {
return false;
}
// DEBUG: b2Assert(index >= 0 && index < this.m_system.m_count);
if (!this.m_system.m_positionBuffer.data) {
throw new Error();
}
if (this.m_shape.TestPoint(this.m_xf, this.m_system.m_positionBuffer.data[index])) {
this.m_system.DestroyParticle(index, this.m_callDestructionListener);
this.m_destroyed++;
}
return true;
};
DestroyParticlesInShapeCallback.prototype.Destroyed = function () {
return this.m_destroyed;
};
return DestroyParticlesInShapeCallback;
}(b2QueryCallback));
b2ParticleSystem.DestroyParticlesInShapeCallback = DestroyParticlesInShapeCallback;
var JoinParticleGroupsFilter = /** @class */ (function (_super) {
__extends(JoinParticleGroupsFilter, _super);
function JoinParticleGroupsFilter(threshold) {
var _this = _super.call(this) || this;
_this.m_threshold = 0;
_this.m_threshold = threshold;
return _this;
}
/**
* An additional condition for creating a pair.
*/
JoinParticleGroupsFilter.prototype.ShouldCreatePair = function (a, b) {
return (a < this.m_threshold && this.m_threshold <= b) ||
(b < this.m_threshold && this.m_threshold <= a);
};
/**
* An additional condition for creating a triad.
*/
JoinParticleGroupsFilter.prototype.ShouldCreateTriad = function (a, b, c) {
return (a < this.m_threshold || b < this.m_threshold || c < this.m_threshold) &&
(this.m_threshold <= a || this.m_threshold <= b || this.m_threshold <= c);
};
return JoinParticleGroupsFilter;
}(b2ParticleSystem.ConnectionFilter));
b2ParticleSystem.JoinParticleGroupsFilter = JoinParticleGroupsFilter;
var CompositeShape = /** @class */ (function (_super) {
__extends(CompositeShape, _super);
function CompositeShape(shapes, shapeCount) {
if (shapeCount === void 0) { shapeCount = shapes.length; }
var _this = _super.call(this, exports.b2ShapeType.e_unknown, 0) || this;
_this.m_shapeCount = 0;
_this.m_shapes = shapes;
_this.m_shapeCount = shapeCount;
return _this;
}
CompositeShape.prototype.Clone = function () {
// DEBUG: b2Assert(false);
throw new Error();
};
CompositeShape.prototype.GetChildCount = function () {
return 1;
};
/**
* @see b2Shape::TestPoint
*/
CompositeShape.prototype.TestPoint = function (xf, p) {
for (var i = 0; i < this.m_shapeCount; i++) {
if (this.m_shapes[i].TestPoint(xf, p)) {
return true;
}
}
return false;
};
/**
* @see b2Shape::ComputeDistance
*/
CompositeShape.prototype.ComputeDistance = function (xf, p, normal, childIndex) {
// DEBUG: b2Assert(false);
return 0;
};
/**
* Implement b2Shape.
*/
CompositeShape.prototype.RayCast = function (output, input, xf, childIndex) {
// DEBUG: b2Assert(false);
return false;
};
/**
* @see b2Shape::ComputeAABB
*/
CompositeShape.prototype.ComputeAABB = function (aabb, xf, childIndex) {
var s_subaabb = new b2AABB();
aabb.lowerBound.x = +b2_maxFloat;
aabb.lowerBound.y = +b2_maxFloat;
aabb.upperBound.x = -b2_maxFloat;
aabb.upperBound.y = -b2_maxFloat;
// DEBUG: b2Assert(childIndex === 0);
for (var i = 0; i < this.m_shapeCount; i++) {
var childCount = this.m_shapes[i].GetChildCount();
for (var j = 0; j < childCount; j++) {
var subaabb = s_subaabb;
this.m_shapes[i].ComputeAABB(subaabb, xf, j);
aabb.Combine1(subaabb);
}
}
};
/**
* @see b2Shape::ComputeMass
*/
CompositeShape.prototype.ComputeMass = function (massData, density) {
// DEBUG: b2Assert(false);
};
CompositeShape.prototype.SetupDistanceProxy = function (proxy, index) {
// DEBUG: b2Assert(false);
};
CompositeShape.prototype.ComputeSubmergedArea = function (normal, offset, xf, c) {
// DEBUG: b2Assert(false);
return 0;
};
CompositeShape.prototype.Dump = function (log) {
// DEBUG: b2Assert(false);
};
return CompositeShape;
}(b2Shape));
b2ParticleSystem.CompositeShape = CompositeShape;
var ReactiveFilter = /** @class */ (function (_super) {
__extends(ReactiveFilter, _super);
function ReactiveFilter(flagsBuffer) {
var _this = _super.call(this) || this;
_this.m_flagsBuffer = flagsBuffer;
return _this;
}
ReactiveFilter.prototype.IsNecessary = function (index) {
if (!this.m_flagsBuffer.data) {
throw new Error();
}
return (this.m_flagsBuffer.data[index] & exports.b2ParticleFlag.b2_reactiveParticle) !== 0;
};
return ReactiveFilter;
}(b2ParticleSystem.ConnectionFilter));
b2ParticleSystem.ReactiveFilter = ReactiveFilter;
var UpdateBodyContactsCallback = /** @class */ (function (_super) {
__extends(UpdateBodyContactsCallback, _super);
function UpdateBodyContactsCallback(system, contactFilter) {
var _this = _super.call(this, system) || this;
_this.m_contactFilter = contactFilter;
return _this;
}
UpdateBodyContactsCallback.prototype.ShouldCollideFixtureParticle = function (fixture, particleSystem, particleIndex) {
// Call the contact filter if it's set, to determine whether to
// filter this contact. Returns true if contact calculations should
// be performed, false otherwise.
if (this.m_contactFilter) {
var flags = this.m_system.GetFlagsBuffer();
if (flags[particleIndex] & exports.b2ParticleFlag.b2_fixtureContactFilterParticle) {
return this.m_contactFilter.ShouldCollideFixtureParticle(fixture, this.m_system, particleIndex);
}
}
return true;
};
UpdateBodyContactsCallback.prototype.ReportFixtureAndParticle = function (fixture, childIndex, a) {
var s_n = b2ParticleSystem.UpdateBodyContactsCallback.ReportFixtureAndParticle_s_n;
var s_rp = b2ParticleSystem.UpdateBodyContactsCallback.ReportFixtureAndParticle_s_rp;
if (!this.m_system.m_flagsBuffer.data) {
throw new Error();
}
if (!this.m_system.m_positionBuffer.data) {
throw new Error();
}
var ap = this.m_system.m_positionBuffer.data[a];
var n = s_n;
var d = fixture.ComputeDistance(ap, n, childIndex);
if (d < this.m_system.m_particleDiameter && this.ShouldCollideFixtureParticle(fixture, this.m_system, a)) {
var b = fixture.GetBody();
var bp = b.GetWorldCenter();
var bm = b.GetMass();
var bI = b.GetInertia() - bm * b.GetLocalCenter().LengthSquared();
var invBm = bm > 0 ? 1 / bm : 0;
var invBI = bI > 0 ? 1 / bI : 0;
var invAm = this.m_system.m_flagsBuffer.data[a] &
exports.b2ParticleFlag.b2_wallParticle ? 0 : this.m_system.GetParticleInvMass();
///b2Vec2 rp = ap - bp;
var rp = b2Vec2.SubVV(ap, bp, s_rp);
var rpn = b2Vec2.CrossVV(rp, n);
var invM = invAm + invBm + invBI * rpn * rpn;
///b2ParticleBodyContact& contact = m_system.m_bodyContactBuffer.Append();
var contact = this.m_system.m_bodyContactBuffer.data[this.m_system.m_bodyContactBuffer.Append()];
contact.index = a;
contact.body = b;
contact.fixture = fixture;
contact.weight = 1 - d * this.m_system.m_inverseDiameter;
///contact.normal = -n;
contact.normal.Copy(n.SelfNeg());
contact.mass = invM > 0 ? 1 / invM : 0;
this.m_system.DetectStuckParticle(a);
}
};
UpdateBodyContactsCallback.ReportFixtureAndParticle_s_n = new b2Vec2();
UpdateBodyContactsCallback.ReportFixtureAndParticle_s_rp = new b2Vec2();
return UpdateBodyContactsCallback;
}(b2FixtureParticleQueryCallback));
b2ParticleSystem.UpdateBodyContactsCallback = UpdateBodyContactsCallback;
var SolveCollisionCallback = /** @class */ (function (_super) {
__extends(SolveCollisionCallback, _super);
function SolveCollisionCallback(system, step) {
var _this = _super.call(this, system) || this;
_this.m_step = step;
return _this;
}
SolveCollisionCallback.prototype.ReportFixtureAndParticle = function (fixture, childIndex, a) {
var s_p1 = b2ParticleSystem.SolveCollisionCallback.ReportFixtureAndParticle_s_p1;
var s_output = b2ParticleSystem.SolveCollisionCallback.ReportFixtureAndParticle_s_output;
var s_input = b2ParticleSystem.SolveCollisionCallback.ReportFixtureAndParticle_s_input;
var s_p = b2ParticleSystem.SolveCollisionCallback.ReportFixtureAndParticle_s_p;
var s_v = b2ParticleSystem.SolveCollisionCallback.ReportFixtureAndParticle_s_v;
var s_f = b2ParticleSystem.SolveCollisionCallback.ReportFixtureAndParticle_s_f;
var body = fixture.GetBody();
if (!this.m_system.m_positionBuffer.data) {
throw new Error();
}
if (!this.m_system.m_velocityBuffer.data) {
throw new Error();
}
var ap = this.m_system.m_positionBuffer.data[a];
var av = this.m_system.m_velocityBuffer.data[a];
var output = s_output;
var input = s_input;
if (this.m_system.m_iterationIndex === 0) {
// Put 'ap' in the local space of the previous frame
///b2Vec2 p1 = b2MulT(body.m_xf0, ap);
var p1 = b2Transform.MulTXV(body.m_xf0, ap, s_p1);
if (fixture.GetShape().GetType() === exports.b2ShapeType.e_circleShape) {
// Make relative to the center of the circle
///p1 -= body.GetLocalCenter();
p1.SelfSub(body.GetLocalCenter());
// Re-apply rotation about the center of the circle
///p1 = b2Mul(body.m_xf0.q, p1);
b2Rot.MulRV(body.m_xf0.q, p1, p1);
// Subtract rotation of the current frame
///p1 = b2MulT(body.m_xf.q, p1);
b2Rot.MulTRV(body.m_xf.q, p1, p1);
// Return to local space
///p1 += body.GetLocalCenter();
p1.SelfAdd(body.GetLocalCenter());
}
// Return to global space and apply rotation of current frame
///input.p1 = b2Mul(body.m_xf, p1);
b2Transform.MulXV(body.m_xf, p1, input.p1);
}
else {
///input.p1 = ap;
input.p1.Copy(ap);
}
///input.p2 = ap + m_step.dt * av;
b2Vec2.AddVMulSV(ap, this.m_step.dt, av, input.p2);
input.maxFraction = 1;
if (fixture.RayCast(output, input, childIndex)) {
var n = output.normal;
///b2Vec2 p = (1 - output.fraction) * input.p1 + output.fraction * input.p2 + b2_linearSlop * n;
var p = s_p;
p.x = (1 - output.fraction) * input.p1.x + output.fraction * input.p2.x + b2_linearSlop * n.x;
p.y = (1 - output.fraction) * input.p1.y + output.fraction * input.p2.y + b2_linearSlop * n.y;
///b2Vec2 v = m_step.inv_dt * (p - ap);
var v = s_v;
v.x = this.m_step.inv_dt * (p.x - ap.x);
v.y = this.m_step.inv_dt * (p.y - ap.y);
///m_system.m_velocityBuffer.data[a] = v;
this.m_system.m_velocityBuffer.data[a].Copy(v);
///b2Vec2 f = m_step.inv_dt * m_system.GetParticleMass() * (av - v);
var f = s_f;
f.x = this.m_step.inv_dt * this.m_system.GetParticleMass() * (av.x - v.x);
f.y = this.m_step.inv_dt * this.m_system.GetParticleMass() * (av.y - v.y);
this.m_system.ParticleApplyForce(a, f);
}
};
SolveCollisionCallback.prototype.ReportParticle = function (system, index) {
return false;
};
SolveCollisionCallback.ReportFixtureAndParticle_s_p1 = new b2Vec2();
SolveCollisionCallback.ReportFixtureAndParticle_s_output = new b2RayCastOutput();
SolveCollisionCallback.ReportFixtureAndParticle_s_input = new b2RayCastInput();
SolveCollisionCallback.ReportFixtureAndParticle_s_p = new b2Vec2();
SolveCollisionCallback.ReportFixtureAndParticle_s_v = new b2Vec2();
SolveCollisionCallback.ReportFixtureAndParticle_s_f = new b2Vec2();
return SolveCollisionCallback;
}(b2FixtureParticleQueryCallback));
b2ParticleSystem.SolveCollisionCallback = SolveCollisionCallback;
})(exports.b2ParticleSystem || (exports.b2ParticleSystem = {}));
// #endif
/*
* 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.
*/
// #endif
/// The world class manages all physics entities, dynamic simulation,
/// and asynchronous queries. The world also contains efficient memory
/// management facilities.
var b2World = /** @class */ (function () {
// #endif
/// Construct a world object.
/// @param gravity the world gravity vector.
function b2World(gravity) {
// b2BlockAllocator m_blockAllocator;
// b2StackAllocator m_stackAllocator;
this.m_newFixture = false;
this.m_locked = false;
this.m_clearForces = true;
this.m_contactManager = new b2ContactManager();
this.m_bodyList = null;
this.m_jointList = null;
// #if B2_ENABLE_PARTICLE
this.m_particleSystemList = null;
// #endif
this.m_bodyCount = 0;
this.m_jointCount = 0;
this.m_gravity = new b2Vec2();
this.m_allowSleep = true;
this.m_destructionListener = null;
this.m_debugDraw = null;
// This is used to compute the time step ratio to
// support a variable time step.
this.m_inv_dt0 = 0;
// These are for debugging the solver.
this.m_warmStarting = true;
this.m_continuousPhysics = true;
this.m_subStepping = false;
this.m_stepComplete = true;
this.m_profile = new b2Profile();
this.m_island = new b2Island();
this.s_stack = [];
// #if B2_ENABLE_CONTROLLER
this.m_controllerList = null;
this.m_controllerCount = 0;
this.m_gravity.Copy(gravity);
}
/// Register a destruction listener. The listener is owned by you and must
/// remain in scope.
b2World.prototype.SetDestructionListener = function (listener) {
this.m_destructionListener = listener;
};
/// Register a contact filter to provide specific control over collision.
/// Otherwise the default filter is used (b2_defaultFilter). The listener is
/// owned by you and must remain in scope.
b2World.prototype.SetContactFilter = function (filter) {
this.m_contactManager.m_contactFilter = filter;
};
/// Register a contact event listener. The listener is owned by you and must
/// remain in scope.
b2World.prototype.SetContactListener = function (listener) {
this.m_contactManager.m_contactListener = listener;
};
/// Register a routine for debug drawing. The debug draw functions are called
/// inside with b2World::DrawDebugData method. The debug draw object is owned
/// by you and must remain in scope.
b2World.prototype.SetDebugDraw = function (debugDraw) {
this.m_debugDraw = debugDraw;
};
/// Create a rigid body given a definition. No reference to the definition
/// is retained.
/// @warning This function is locked during callbacks.
b2World.prototype.CreateBody = function (def) {
if (def === void 0) { def = {}; }
if (this.IsLocked()) {
throw new Error();
}
var b = new b2Body(def, this);
// Add to world doubly linked list.
b.m_prev = null;
b.m_next = this.m_bodyList;
if (this.m_bodyList) {
this.m_bodyList.m_prev = b;
}
this.m_bodyList = b;
++this.m_bodyCount;
return b;
};
/// Destroy a rigid body given a definition. No reference to the definition
/// is retained. This function is locked during callbacks.
/// @warning This automatically deletes all associated shapes and joints.
/// @warning This function is locked during callbacks.
b2World.prototype.DestroyBody = function (b) {
// DEBUG: b2Assert(this.m_bodyCount > 0);
if (this.IsLocked()) {
throw new Error();
}
// Delete the attached joints.
var je = b.m_jointList;
while (je) {
var je0 = je;
je = je.next;
if (this.m_destructionListener) {
this.m_destructionListener.SayGoodbyeJoint(je0.joint);
}
this.DestroyJoint(je0.joint);
b.m_jointList = je;
}
b.m_jointList = null;
// #if B2_ENABLE_CONTROLLER
// @see b2Controller list
var coe = b.m_controllerList;
while (coe) {
var coe0 = coe;
coe = coe.nextController;
coe0.controller.RemoveBody(b);
}
// #endif
// Delete the attached contacts.
var ce = b.m_contactList;
while (ce) {
var ce0 = ce;
ce = ce.next;
this.m_contactManager.Destroy(ce0.contact);
}
b.m_contactList = null;
// Delete the attached fixtures. This destroys broad-phase proxies.
var f = b.m_fixtureList;
while (f) {
var f0 = f;
f = f.m_next;
if (this.m_destructionListener) {
this.m_destructionListener.SayGoodbyeFixture(f0);
}
f0.DestroyProxies();
f0.Destroy();
b.m_fixtureList = f;
b.m_fixtureCount -= 1;
}
b.m_fixtureList = null;
b.m_fixtureCount = 0;
// Remove world body list.
if (b.m_prev) {
b.m_prev.m_next = b.m_next;
}
if (b.m_next) {
b.m_next.m_prev = b.m_prev;
}
if (b === this.m_bodyList) {
this.m_bodyList = b.m_next;
}
--this.m_bodyCount;
};
b2World._Joint_Create = function (def, allocator) {
switch (def.type) {
case exports.b2JointType.e_distanceJoint: return new b2DistanceJoint(def);
case exports.b2JointType.e_mouseJoint: return new b2MouseJoint(def);
case exports.b2JointType.e_prismaticJoint: return new b2PrismaticJoint(def);
case exports.b2JointType.e_revoluteJoint: return new b2RevoluteJoint(def);
case exports.b2JointType.e_pulleyJoint: return new b2PulleyJoint(def);
case exports.b2JointType.e_gearJoint: return new b2GearJoint(def);
case exports.b2JointType.e_wheelJoint: return new b2WheelJoint(def);
case exports.b2JointType.e_weldJoint: return new b2WeldJoint(def);
case exports.b2JointType.e_frictionJoint: return new b2FrictionJoint(def);
case exports.b2JointType.e_ropeJoint: return new b2RopeJoint(def);
case exports.b2JointType.e_motorJoint: return new b2MotorJoint(def);
case exports.b2JointType.e_areaJoint: return new b2AreaJoint(def);
}
throw new Error();
};
b2World._Joint_Destroy = function (joint, allocator) {
};
b2World.prototype.CreateJoint = function (def) {
if (this.IsLocked()) {
throw new Error();
}
var j = b2World._Joint_Create(def, null);
// Connect to the world list.
j.m_prev = null;
j.m_next = this.m_jointList;
if (this.m_jointList) {
this.m_jointList.m_prev = j;
}
this.m_jointList = j;
++this.m_jointCount;
// Connect to the bodies' doubly linked lists.
// j.m_edgeA.joint = j;
// j.m_edgeA.other = j.m_bodyB;
j.m_edgeA.prev = null;
j.m_edgeA.next = j.m_bodyA.m_jointList;
if (j.m_bodyA.m_jointList) {
j.m_bodyA.m_jointList.prev = j.m_edgeA;
}
j.m_bodyA.m_jointList = j.m_edgeA;
// j.m_edgeB.joint = j;
// j.m_edgeB.other = j.m_bodyA;
j.m_edgeB.prev = null;
j.m_edgeB.next = j.m_bodyB.m_jointList;
if (j.m_bodyB.m_jointList) {
j.m_bodyB.m_jointList.prev = j.m_edgeB;
}
j.m_bodyB.m_jointList = j.m_edgeB;
var bodyA = def.bodyA;
var bodyB = def.bodyB;
// If the joint prevents collisions, then flag any contacts for filtering.
if (!def.collideConnected) {
var edge = bodyB.GetContactList();
while (edge) {
if (edge.other === bodyA) {
// Flag the contact for filtering at the next time step (where either
// body is awake).
edge.contact.FlagForFiltering();
}
edge = edge.next;
}
}
// Note: creating a joint doesn't wake the bodies.
return j;
};
/// Destroy a joint. This may cause the connected bodies to begin colliding.
/// @warning This function is locked during callbacks.
b2World.prototype.DestroyJoint = function (j) {
if (this.IsLocked()) {
throw new Error();
}
var collideConnected = j.m_collideConnected;
// Remove from the doubly linked list.
if (j.m_prev) {
j.m_prev.m_next = j.m_next;
}
if (j.m_next) {
j.m_next.m_prev = j.m_prev;
}
if (j === this.m_jointList) {
this.m_jointList = j.m_next;
}
// Disconnect from island graph.
var bodyA = j.m_bodyA;
var bodyB = j.m_bodyB;
// Wake up connected bodies.
bodyA.SetAwake(true);
bodyB.SetAwake(true);
// Remove from body 1.
if (j.m_edgeA.prev) {
j.m_edgeA.prev.next = j.m_edgeA.next;
}
if (j.m_edgeA.next) {
j.m_edgeA.next.prev = j.m_edgeA.prev;
}
if (j.m_edgeA === bodyA.m_jointList) {
bodyA.m_jointList = j.m_edgeA.next;
}
j.m_edgeA.prev = null;
j.m_edgeA.next = null;
// Remove from body 2
if (j.m_edgeB.prev) {
j.m_edgeB.prev.next = j.m_edgeB.next;
}
if (j.m_edgeB.next) {
j.m_edgeB.next.prev = j.m_edgeB.prev;
}
if (j.m_edgeB === bodyB.m_jointList) {
bodyB.m_jointList = j.m_edgeB.next;
}
j.m_edgeB.prev = null;
j.m_edgeB.next = null;
b2World._Joint_Destroy(j, null);
// DEBUG: b2Assert(this.m_jointCount > 0);
--this.m_jointCount;
// If the joint prevents collisions, then flag any contacts for filtering.
if (!collideConnected) {
var edge = bodyB.GetContactList();
while (edge) {
if (edge.other === bodyA) {
// Flag the contact for filtering at the next time step (where either
// body is awake).
edge.contact.FlagForFiltering();
}
edge = edge.next;
}
}
};
// #if B2_ENABLE_PARTICLE
b2World.prototype.CreateParticleSystem = function (def) {
if (this.IsLocked()) {
throw new Error();
}
var p = new exports.b2ParticleSystem(def, this);
// Add to world doubly linked list.
p.m_prev = null;
p.m_next = this.m_particleSystemList;
if (this.m_particleSystemList) {
this.m_particleSystemList.m_prev = p;
}
this.m_particleSystemList = p;
return p;
};
b2World.prototype.DestroyParticleSystem = function (p) {
if (this.IsLocked()) {
throw new Error();
}
// Remove world particleSystem list.
if (p.m_prev) {
p.m_prev.m_next = p.m_next;
}
if (p.m_next) {
p.m_next.m_prev = p.m_prev;
}
if (p === this.m_particleSystemList) {
this.m_particleSystemList = p.m_next;
}
};
b2World.prototype.CalculateReasonableParticleIterations = function (timeStep) {
if (this.m_particleSystemList === null) {
return 1;
}
function GetSmallestRadius(world) {
var smallestRadius = b2_maxFloat;
for (var system = world.GetParticleSystemList(); system !== null; system = system.m_next) {
smallestRadius = b2Min(smallestRadius, system.GetRadius());
}
return smallestRadius;
}
// Use the smallest radius, since that represents the worst-case.
return b2CalculateParticleIterations(this.m_gravity.Length(), GetSmallestRadius(this), timeStep);
};
// #if B2_ENABLE_PARTICLE
b2World.prototype.Step = function (dt, velocityIterations, positionIterations, particleIterations) {
if (particleIterations === void 0) { particleIterations = this.CalculateReasonableParticleIterations(dt); }
// #else
// public Step(dt: number, velocityIterations: number, positionIterations: number): void {
// #endif
var stepTimer = b2World.Step_s_stepTimer.Reset();
// If new fixtures were added, we need to find the new contacts.
if (this.m_newFixture) {
this.m_contactManager.FindNewContacts();
this.m_newFixture = false;
}
this.m_locked = true;
var step = b2World.Step_s_step;
step.dt = dt;
step.velocityIterations = velocityIterations;
step.positionIterations = positionIterations;
// #if B2_ENABLE_PARTICLE
step.particleIterations = particleIterations;
// #endif
if (dt > 0) {
step.inv_dt = 1 / dt;
}
else {
step.inv_dt = 0;
}
step.dtRatio = this.m_inv_dt0 * dt;
step.warmStarting = this.m_warmStarting;
// Update contacts. This is where some contacts are destroyed.
var timer = b2World.Step_s_timer.Reset();
this.m_contactManager.Collide();
this.m_profile.collide = timer.GetMilliseconds();
// Integrate velocities, solve velocity constraints, and integrate positions.
if (this.m_stepComplete && step.dt > 0) {
var timer_1 = b2World.Step_s_timer.Reset();
// #if B2_ENABLE_PARTICLE
for (var p = this.m_particleSystemList; p; p = p.m_next) {
p.Solve(step); // Particle Simulation
}
// #endif
this.Solve(step);
this.m_profile.solve = timer_1.GetMilliseconds();
}
// Handle TOI events.
if (this.m_continuousPhysics && step.dt > 0) {
var timer_2 = b2World.Step_s_timer.Reset();
this.SolveTOI(step);
this.m_profile.solveTOI = timer_2.GetMilliseconds();
}
if (step.dt > 0) {
this.m_inv_dt0 = step.inv_dt;
}
if (this.m_clearForces) {
this.ClearForces();
}
this.m_locked = false;
this.m_profile.step = stepTimer.GetMilliseconds();
};
/// Manually clear the force buffer on all bodies. By default, forces are cleared automatically
/// after each call to Step. The default behavior is modified by calling SetAutoClearForces.
/// The purpose of this function is to support sub-stepping. Sub-stepping is often used to maintain
/// a fixed sized time step under a variable frame-rate.
/// When you perform sub-stepping you will disable auto clearing of forces and instead call
/// ClearForces after all sub-steps are complete in one pass of your game loop.
/// @see SetAutoClearForces
b2World.prototype.ClearForces = function () {
for (var body = this.m_bodyList; body; body = body.m_next) {
body.m_force.SetZero();
body.m_torque = 0;
}
};
// #if B2_ENABLE_PARTICLE
b2World.prototype.DrawParticleSystem = function (system) {
if (this.m_debugDraw === null) {
return;
}
var particleCount = system.GetParticleCount();
if (particleCount) {
var radius = system.GetRadius();
var positionBuffer = system.GetPositionBuffer();
if (system.m_colorBuffer.data) {
var colorBuffer = system.GetColorBuffer();
this.m_debugDraw.DrawParticles(positionBuffer, radius, colorBuffer, particleCount);
}
else {
this.m_debugDraw.DrawParticles(positionBuffer, radius, null, particleCount);
}
}
};
b2World.prototype.DrawDebugData = function () {
if (this.m_debugDraw === null) {
return;
}
var flags = this.m_debugDraw.GetFlags();
var color = b2World.DrawDebugData_s_color.SetRGB(0, 0, 0);
if (flags & exports.b2DrawFlags.e_shapeBit) {
for (var b = this.m_bodyList; b; b = b.m_next) {
var xf = b.m_xf;
this.m_debugDraw.PushTransform(xf);
for (var f = b.GetFixtureList(); f; f = f.m_next) {
if (!b.IsActive()) {
color.SetRGB(0.5, 0.5, 0.3);
this.DrawShape(f, color);
}
else if (b.GetType() === exports.b2BodyType.b2_staticBody) {
color.SetRGB(0.5, 0.9, 0.5);
this.DrawShape(f, color);
}
else if (b.GetType() === exports.b2BodyType.b2_kinematicBody) {
color.SetRGB(0.5, 0.5, 0.9);
this.DrawShape(f, color);
}
else if (!b.IsAwake()) {
color.SetRGB(0.6, 0.6, 0.6);
this.DrawShape(f, color);
}
else {
color.SetRGB(0.9, 0.7, 0.7);
this.DrawShape(f, color);
}
}
this.m_debugDraw.PopTransform(xf);
}
}
// #if B2_ENABLE_PARTICLE
if (flags & exports.b2DrawFlags.e_particleBit) {
for (var p = this.m_particleSystemList; p; p = p.m_next) {
this.DrawParticleSystem(p);
}
}
// #endif
if (flags & exports.b2DrawFlags.e_jointBit) {
for (var j = this.m_jointList; j; j = j.m_next) {
this.DrawJoint(j);
}
}
/*
if (flags & b2DrawFlags.e_pairBit) {
color.SetRGB(0.3, 0.9, 0.9);
for (let contact = this.m_contactManager.m_contactList; contact; contact = contact.m_next) {
const fixtureA = contact.GetFixtureA();
const fixtureB = contact.GetFixtureB();
const cA = fixtureA.GetAABB().GetCenter();
const cB = fixtureB.GetAABB().GetCenter();
this.m_debugDraw.DrawSegment(cA, cB, color);
}
}
*/
if (flags & exports.b2DrawFlags.e_aabbBit) {
color.SetRGB(0.9, 0.3, 0.9);
var vs = b2World.DrawDebugData_s_vs;
for (var b = this.m_bodyList; b; b = b.m_next) {
if (!b.IsActive()) {
continue;
}
for (var f = b.GetFixtureList(); f; f = f.m_next) {
for (var i = 0; i < f.m_proxyCount; ++i) {
var proxy = f.m_proxies[i];
var aabb = proxy.treeNode.aabb;
vs[0].Set(aabb.lowerBound.x, aabb.lowerBound.y);
vs[1].Set(aabb.upperBound.x, aabb.lowerBound.y);
vs[2].Set(aabb.upperBound.x, aabb.upperBound.y);
vs[3].Set(aabb.lowerBound.x, aabb.upperBound.y);
this.m_debugDraw.DrawPolygon(vs, 4, color);
}
}
}
}
if (flags & exports.b2DrawFlags.e_centerOfMassBit) {
for (var b = this.m_bodyList; b; b = b.m_next) {
var xf = b2World.DrawDebugData_s_xf;
xf.q.Copy(b.m_xf.q);
xf.p.Copy(b.GetWorldCenter());
this.m_debugDraw.DrawTransform(xf);
}
}
// #if B2_ENABLE_CONTROLLER
// @see b2Controller list
if (flags & exports.b2DrawFlags.e_controllerBit) {
for (var c = this.m_controllerList; c; c = c.m_next) {
c.Draw(this.m_debugDraw);
}
}
// #endif
};
/// Query the world for all fixtures that potentially overlap the
/// provided AABB.
/// @param callback a user implemented callback class.
/// @param aabb the query box.
b2World.prototype.QueryAABB = function (callback, aabb, fn) {
this.m_contactManager.m_broadPhase.Query(aabb, function (proxy) {
var fixture_proxy = proxy.userData;
// DEBUG: b2Assert(fixture_proxy instanceof b2FixtureProxy);
var fixture = fixture_proxy.fixture;
if (callback) {
return callback.ReportFixture(fixture);
}
else if (fn) {
return fn(fixture);
}
return true;
});
// #if B2_ENABLE_PARTICLE
if (callback instanceof b2QueryCallback) {
for (var p = this.m_particleSystemList; p; p = p.m_next) {
if (callback.ShouldQueryParticleSystem(p)) {
p.QueryAABB(callback, aabb);
}
}
}
// #endif
};
b2World.prototype.QueryAllAABB = function (aabb, out) {
if (out === void 0) { out = []; }
this.QueryAABB(null, aabb, function (fixture) { out.push(fixture); return true; });
return out;
};
/// Query the world for all fixtures that potentially overlap the
/// provided point.
/// @param callback a user implemented callback class.
/// @param point the query point.
b2World.prototype.QueryPointAABB = function (callback, point, fn) {
this.m_contactManager.m_broadPhase.QueryPoint(point, function (proxy) {
var fixture_proxy = proxy.userData;
// DEBUG: b2Assert(fixture_proxy instanceof b2FixtureProxy);
var fixture = fixture_proxy.fixture;
if (callback) {
return callback.ReportFixture(fixture);
}
else if (fn) {
return fn(fixture);
}
return true;
});
// #if B2_ENABLE_PARTICLE
if (callback instanceof b2QueryCallback) {
for (var p = this.m_particleSystemList; p; p = p.m_next) {
if (callback.ShouldQueryParticleSystem(p)) {
p.QueryPointAABB(callback, point);
}
}
}
// #endif
};
b2World.prototype.QueryAllPointAABB = function (point, out) {
if (out === void 0) { out = []; }
this.QueryPointAABB(null, point, function (fixture) { out.push(fixture); return true; });
return out;
};
b2World.prototype.QueryFixtureShape = function (callback, shape, index, transform, fn) {
var aabb = b2World.QueryFixtureShape_s_aabb;
shape.ComputeAABB(aabb, transform, index);
this.m_contactManager.m_broadPhase.Query(aabb, function (proxy) {
var fixture_proxy = proxy.userData;
// DEBUG: b2Assert(fixture_proxy instanceof b2FixtureProxy);
var fixture = fixture_proxy.fixture;
if (b2TestOverlapShape(shape, index, fixture.GetShape(), fixture_proxy.childIndex, transform, fixture.GetBody().GetTransform())) {
if (callback) {
return callback.ReportFixture(fixture);
}
else if (fn) {
return fn(fixture);
}
}
return true;
});
// #if B2_ENABLE_PARTICLE
if (callback instanceof b2QueryCallback) {
for (var p = this.m_particleSystemList; p; p = p.m_next) {
if (callback.ShouldQueryParticleSystem(p)) {
p.QueryAABB(callback, aabb);
}
}
}
// #endif
};
b2World.prototype.QueryAllFixtureShape = function (shape, index, transform, out) {
if (out === void 0) { out = []; }
this.QueryFixtureShape(null, shape, index, transform, function (fixture) { out.push(fixture); return true; });
return out;
};
b2World.prototype.QueryFixturePoint = function (callback, point, fn) {
this.m_contactManager.m_broadPhase.QueryPoint(point, function (proxy) {
var fixture_proxy = proxy.userData;
// DEBUG: b2Assert(fixture_proxy instanceof b2FixtureProxy);
var fixture = fixture_proxy.fixture;
if (fixture.TestPoint(point)) {
if (callback) {
return callback.ReportFixture(fixture);
}
else if (fn) {
return fn(fixture);
}
}
return true;
});
// #if B2_ENABLE_PARTICLE
if (callback) {
for (var p = this.m_particleSystemList; p; p = p.m_next) {
if (callback.ShouldQueryParticleSystem(p)) {
p.QueryPointAABB(callback, point);
}
}
}
// #endif
};
b2World.prototype.QueryAllFixturePoint = function (point, out) {
if (out === void 0) { out = []; }
this.QueryFixturePoint(null, point, function (fixture) { out.push(fixture); return true; });
return out;
};
b2World.prototype.RayCast = function (callback, point1, point2, fn) {
var input = b2World.RayCast_s_input;
input.maxFraction = 1;
input.p1.Copy(point1);
input.p2.Copy(point2);
this.m_contactManager.m_broadPhase.RayCast(input, function (input, proxy) {
var fixture_proxy = proxy.userData;
// DEBUG: b2Assert(fixture_proxy instanceof b2FixtureProxy);
var fixture = fixture_proxy.fixture;
var index = fixture_proxy.childIndex;
var output = b2World.RayCast_s_output;
var hit = fixture.RayCast(output, input, index);
if (hit) {
var fraction = output.fraction;
var point = b2World.RayCast_s_point;
point.Set((1 - fraction) * point1.x + fraction * point2.x, (1 - fraction) * point1.y + fraction * point2.y);
if (callback) {
return callback.ReportFixture(fixture, point, output.normal, fraction);
}
else if (fn) {
return fn(fixture, point, output.normal, fraction);
}
}
return input.maxFraction;
});
// #if B2_ENABLE_PARTICLE
if (callback) {
for (var p = this.m_particleSystemList; p; p = p.m_next) {
if (callback.ShouldQueryParticleSystem(p)) {
p.RayCast(callback, point1, point2);
}
}
}
// #endif
};
b2World.prototype.RayCastOne = function (point1, point2) {
var result = null;
var min_fraction = 1;
this.RayCast(null, point1, point2, function (fixture, point, normal, fraction) {
if (fraction < min_fraction) {
min_fraction = fraction;
result = fixture;
}
return min_fraction;
});
return result;
};
b2World.prototype.RayCastAll = function (point1, point2, out) {
if (out === void 0) { out = []; }
this.RayCast(null, point1, point2, function (fixture, point, normal, fraction) {
out.push(fixture);
return 1;
});
return out;
};
/// Get the world body list. With the returned body, use b2Body::GetNext to get
/// the next body in the world list. A NULL body indicates the end of the list.
/// @return the head of the world body list.
b2World.prototype.GetBodyList = function () {
return this.m_bodyList;
};
/// Get the world joint list. With the returned joint, use b2Joint::GetNext to get
/// the next joint in the world list. A NULL joint indicates the end of the list.
/// @return the head of the world joint list.
b2World.prototype.GetJointList = function () {
return this.m_jointList;
};
// #if B2_ENABLE_PARTICLE
b2World.prototype.GetParticleSystemList = function () {
return this.m_particleSystemList;
};
// #endif
/// Get the world contact list. With the returned contact, use b2Contact::GetNext to get
/// the next contact in the world list. A NULL contact indicates the end of the list.
/// @return the head of the world contact list.
/// @warning contacts are created and destroyed in the middle of a time step.
/// Use b2ContactListener to avoid missing contacts.
b2World.prototype.GetContactList = function () {
return this.m_contactManager.m_contactList;
};
/// Enable/disable sleep.
b2World.prototype.SetAllowSleeping = function (flag) {
if (flag === this.m_allowSleep) {
return;
}
this.m_allowSleep = flag;
if (!this.m_allowSleep) {
for (var b = this.m_bodyList; b; b = b.m_next) {
b.SetAwake(true);
}
}
};
b2World.prototype.GetAllowSleeping = function () {
return this.m_allowSleep;
};
/// Enable/disable warm starting. For testing.
b2World.prototype.SetWarmStarting = function (flag) {
this.m_warmStarting = flag;
};
b2World.prototype.GetWarmStarting = function () {
return this.m_warmStarting;
};
/// Enable/disable continuous physics. For testing.
b2World.prototype.SetContinuousPhysics = function (flag) {
this.m_continuousPhysics = flag;
};
b2World.prototype.GetContinuousPhysics = function () {
return this.m_continuousPhysics;
};
/// Enable/disable single stepped continuous physics. For testing.
b2World.prototype.SetSubStepping = function (flag) {
this.m_subStepping = flag;
};
b2World.prototype.GetSubStepping = function () {
return this.m_subStepping;
};
/// Get the number of broad-phase proxies.
b2World.prototype.GetProxyCount = function () {
return this.m_contactManager.m_broadPhase.GetProxyCount();
};
/// Get the number of bodies.
b2World.prototype.GetBodyCount = function () {
return this.m_bodyCount;
};
/// Get the number of joints.
b2World.prototype.GetJointCount = function () {
return this.m_jointCount;
};
/// Get the number of contacts (each may have 0 or more contact points).
b2World.prototype.GetContactCount = function () {
return this.m_contactManager.m_contactCount;
};
/// Get the height of the dynamic tree.
b2World.prototype.GetTreeHeight = function () {
return this.m_contactManager.m_broadPhase.GetTreeHeight();
};
/// Get the balance of the dynamic tree.
b2World.prototype.GetTreeBalance = function () {
return this.m_contactManager.m_broadPhase.GetTreeBalance();
};
/// Get the quality metric of the dynamic tree. The smaller the better.
/// The minimum is 1.
b2World.prototype.GetTreeQuality = function () {
return this.m_contactManager.m_broadPhase.GetTreeQuality();
};
/// Change the global gravity vector.
b2World.prototype.SetGravity = function (gravity, wake) {
if (wake === void 0) { wake = true; }
if (!b2Vec2.IsEqualToV(this.m_gravity, gravity)) {
this.m_gravity.Copy(gravity);
if (wake) {
for (var b = this.m_bodyList; b; b = b.m_next) {
b.SetAwake(true);
}
}
}
};
/// Get the global gravity vector.
b2World.prototype.GetGravity = function () {
return this.m_gravity;
};
/// Is the world locked (in the middle of a time step).
b2World.prototype.IsLocked = function () {
return this.m_locked;
};
/// Set flag to control automatic clearing of forces after each time step.
b2World.prototype.SetAutoClearForces = function (flag) {
this.m_clearForces = flag;
};
/// Get the flag that controls automatic clearing of forces after each time step.
b2World.prototype.GetAutoClearForces = function () {
return this.m_clearForces;
};
/// Shift the world origin. Useful for large worlds.
/// The body shift formula is: position -= newOrigin
/// @param newOrigin the new origin with respect to the old origin
b2World.prototype.ShiftOrigin = function (newOrigin) {
if (this.IsLocked()) {
throw new Error();
}
for (var b = this.m_bodyList; b; b = b.m_next) {
b.m_xf.p.SelfSub(newOrigin);
b.m_sweep.c0.SelfSub(newOrigin);
b.m_sweep.c.SelfSub(newOrigin);
}
for (var j = this.m_jointList; j; j = j.m_next) {
j.ShiftOrigin(newOrigin);
}
this.m_contactManager.m_broadPhase.ShiftOrigin(newOrigin);
};
/// Get the contact manager for testing.
b2World.prototype.GetContactManager = function () {
return this.m_contactManager;
};
/// Get the current profile.
b2World.prototype.GetProfile = function () {
return this.m_profile;
};
/// Dump the world into the log file.
/// @warning this should be called outside of a time step.
b2World.prototype.Dump = function (log) {
if (this.m_locked) {
return;
}
log("const g: b2Vec2 = new b2Vec2(%.15f, %.15f);\n", this.m_gravity.x, this.m_gravity.y);
log("this.m_world.SetGravity(g);\n");
log("const bodies: b2Body[] = [];\n");
log("const joints: b2Joint[] = [];\n");
var i = 0;
for (var b = this.m_bodyList; b; b = b.m_next) {
b.m_islandIndex = i;
b.Dump(log);
++i;
}
i = 0;
for (var j = this.m_jointList; j; j = j.m_next) {
j.m_index = i;
++i;
}
// First pass on joints, skip gear joints.
for (var j = this.m_jointList; j; j = j.m_next) {
if (j.m_type === exports.b2JointType.e_gearJoint) {
continue;
}
log("{\n");
j.Dump(log);
log("}\n");
}
// Second pass on joints, only gear joints.
for (var j = this.m_jointList; j; j = j.m_next) {
if (j.m_type !== exports.b2JointType.e_gearJoint) {
continue;
}
log("{\n");
j.Dump(log);
log("}\n");
}
};
b2World.prototype.DrawJoint = function (joint) {
if (this.m_debugDraw === null) {
return;
}
var bodyA = joint.GetBodyA();
var bodyB = joint.GetBodyB();
var xf1 = bodyA.m_xf;
var xf2 = bodyB.m_xf;
var x1 = xf1.p;
var x2 = xf2.p;
var p1 = joint.GetAnchorA(b2World.DrawJoint_s_p1);
var p2 = joint.GetAnchorB(b2World.DrawJoint_s_p2);
var color = b2World.DrawJoint_s_color.SetRGB(0.5, 0.8, 0.8);
switch (joint.m_type) {
case exports.b2JointType.e_distanceJoint:
this.m_debugDraw.DrawSegment(p1, p2, color);
break;
case exports.b2JointType.e_pulleyJoint: {
var pulley = joint;
var s1 = pulley.GetGroundAnchorA();
var s2 = pulley.GetGroundAnchorB();
this.m_debugDraw.DrawSegment(s1, p1, color);
this.m_debugDraw.DrawSegment(s2, p2, color);
this.m_debugDraw.DrawSegment(s1, s2, color);
break;
}
case exports.b2JointType.e_mouseJoint: {
var c = b2World.DrawJoint_s_c;
c.Set(0.0, 1.0, 0.0);
this.m_debugDraw.DrawPoint(p1, 4.0, c);
this.m_debugDraw.DrawPoint(p2, 4.0, c);
c.Set(0.8, 0.8, 0.8);
this.m_debugDraw.DrawSegment(p1, p2, c);
break;
}
default:
this.m_debugDraw.DrawSegment(x1, p1, color);
this.m_debugDraw.DrawSegment(p1, p2, color);
this.m_debugDraw.DrawSegment(x2, p2, color);
}
};
b2World.prototype.DrawShape = function (fixture, color) {
if (this.m_debugDraw === null) {
return;
}
var shape = fixture.GetShape();
switch (shape.m_type) {
case exports.b2ShapeType.e_circleShape: {
var circle = shape;
var center = circle.m_p;
var radius = circle.m_radius;
var axis = b2Vec2.UNITX;
this.m_debugDraw.DrawSolidCircle(center, radius, axis, color);
break;
}
case exports.b2ShapeType.e_edgeShape: {
var edge = shape;
var v1 = edge.m_vertex1;
var v2 = edge.m_vertex2;
this.m_debugDraw.DrawSegment(v1, v2, color);
break;
}
case exports.b2ShapeType.e_chainShape: {
var chain = shape;
var count = chain.m_count;
var vertices = chain.m_vertices;
var ghostColor = b2World.DrawShape_s_ghostColor.SetRGBA(0.75 * color.r, 0.75 * color.g, 0.75 * color.b, color.a);
var v1 = vertices[0];
this.m_debugDraw.DrawPoint(v1, 4.0, color);
if (chain.m_hasPrevVertex) {
var vp = chain.m_prevVertex;
this.m_debugDraw.DrawSegment(vp, v1, ghostColor);
this.m_debugDraw.DrawCircle(vp, 0.1, ghostColor);
}
for (var i = 1; i < count; ++i) {
var v2 = vertices[i];
this.m_debugDraw.DrawSegment(v1, v2, color);
this.m_debugDraw.DrawPoint(v2, 4.0, color);
v1 = v2;
}
if (chain.m_hasNextVertex) {
var vn = chain.m_nextVertex;
this.m_debugDraw.DrawSegment(vn, v1, ghostColor);
this.m_debugDraw.DrawCircle(vn, 0.1, ghostColor);
}
break;
}
case exports.b2ShapeType.e_polygonShape: {
var poly = shape;
var vertexCount = poly.m_count;
var vertices = poly.m_vertices;
this.m_debugDraw.DrawSolidPolygon(vertices, vertexCount, color);
break;
}
}
};
b2World.prototype.Solve = function (step) {
// #if B2_ENABLE_PARTICLE
// update previous transforms
for (var b = this.m_bodyList; b; b = b.m_next) {
b.m_xf0.Copy(b.m_xf);
}
// #endif
// #if B2_ENABLE_CONTROLLER
// @see b2Controller list
for (var controller = this.m_controllerList; controller; controller = controller.m_next) {
controller.Step(step);
}
// #endif
this.m_profile.solveInit = 0;
this.m_profile.solveVelocity = 0;
this.m_profile.solvePosition = 0;
// Size the island for the worst case.
var island = this.m_island;
island.Initialize(this.m_bodyCount, this.m_contactManager.m_contactCount, this.m_jointCount, null, // this.m_stackAllocator,
this.m_contactManager.m_contactListener);
// Clear all the island flags.
for (var b = this.m_bodyList; b; b = b.m_next) {
b.m_islandFlag = false;
}
for (var c = this.m_contactManager.m_contactList; c; c = c.m_next) {
c.m_islandFlag = false;
}
for (var j = this.m_jointList; j; j = j.m_next) {
j.m_islandFlag = false;
}
// Build and simulate all awake islands.
// DEBUG: const stackSize: number = this.m_bodyCount;
var stack = this.s_stack;
for (var seed = this.m_bodyList; seed; seed = seed.m_next) {
if (seed.m_islandFlag) {
continue;
}
if (!seed.IsAwake() || !seed.IsActive()) {
continue;
}
// The seed can be dynamic or kinematic.
if (seed.GetType() === exports.b2BodyType.b2_staticBody) {
continue;
}
// Reset island and stack.
island.Clear();
var stackCount = 0;
stack[stackCount++] = seed;
seed.m_islandFlag = true;
// Perform a depth first search (DFS) on the constraint graph.
while (stackCount > 0) {
// Grab the next body off the stack and add it to the island.
var b = stack[--stackCount];
if (!b) {
throw new Error();
}
// DEBUG: b2Assert(b.IsActive());
island.AddBody(b);
// Make sure the body is awake. (without resetting sleep timer).
b.m_awakeFlag = true;
// To keep islands as small as possible, we don't
// propagate islands across static bodies.
if (b.GetType() === exports.b2BodyType.b2_staticBody) {
continue;
}
// Search all contacts connected to this body.
for (var ce = b.m_contactList; ce; ce = ce.next) {
var contact = ce.contact;
// Has this contact already been added to an island?
if (contact.m_islandFlag) {
continue;
}
// Is this contact solid and touching?
if (!contact.IsEnabled() || !contact.IsTouching()) {
continue;
}
// Skip sensors.
var sensorA = contact.m_fixtureA.m_isSensor;
var sensorB = contact.m_fixtureB.m_isSensor;
if (sensorA || sensorB) {
continue;
}
island.AddContact(contact);
contact.m_islandFlag = true;
var other = ce.other;
if (!other) {
throw new Error();
}
// Was the other body already added to this island?
if (other.m_islandFlag) {
continue;
}
// DEBUG: b2Assert(stackCount < stackSize);
stack[stackCount++] = other;
other.m_islandFlag = true;
}
// Search all joints connect to this body.
for (var je = b.m_jointList; je; je = je.next) {
if (je.joint.m_islandFlag) {
continue;
}
var other = je.other;
// Don't simulate joints connected to inactive bodies.
if (!other.IsActive()) {
continue;
}
island.AddJoint(je.joint);
je.joint.m_islandFlag = true;
if (other.m_islandFlag) {
continue;
}
// DEBUG: b2Assert(stackCount < stackSize);
stack[stackCount++] = other;
other.m_islandFlag = true;
}
}
var profile = new b2Profile();
island.Solve(profile, step, this.m_gravity, this.m_allowSleep);
this.m_profile.solveInit += profile.solveInit;
this.m_profile.solveVelocity += profile.solveVelocity;
this.m_profile.solvePosition += profile.solvePosition;
// Post solve cleanup.
for (var i = 0; i < island.m_bodyCount; ++i) {
// Allow static bodies to participate in other islands.
var b = island.m_bodies[i];
if (b.GetType() === exports.b2BodyType.b2_staticBody) {
b.m_islandFlag = false;
}
}
}
for (var i = 0; i < stack.length; ++i) {
if (!stack[i]) {
break;
}
stack[i] = null;
}
var timer = new b2Timer();
// Synchronize fixtures, check for out of range bodies.
for (var b = this.m_bodyList; b; b = b.m_next) {
// If a body was not in an island then it did not move.
if (!b.m_islandFlag) {
continue;
}
if (b.GetType() === exports.b2BodyType.b2_staticBody) {
continue;
}
// Update fixtures (for broad-phase).
b.SynchronizeFixtures();
}
// Look for new contacts.
this.m_contactManager.FindNewContacts();
this.m_profile.broadphase = timer.GetMilliseconds();
};
b2World.prototype.SolveTOI = function (step) {
// b2Island island(2 * b2_maxTOIContacts, b2_maxTOIContacts, 0, &m_stackAllocator, m_contactManager.m_contactListener);
var island = this.m_island;
island.Initialize(2 * b2_maxTOIContacts, b2_maxTOIContacts, 0, null, this.m_contactManager.m_contactListener);
if (this.m_stepComplete) {
for (var b = this.m_bodyList; b; b = b.m_next) {
b.m_islandFlag = false;
b.m_sweep.alpha0 = 0;
}
for (var c = this.m_contactManager.m_contactList; c; c = c.m_next) {
// Invalidate TOI
c.m_toiFlag = false;
c.m_islandFlag = false;
c.m_toiCount = 0;
c.m_toi = 1;
}
}
// Find TOI events and solve them.
for (;;) {
// Find the first TOI.
var minContact = null;
var minAlpha = 1;
for (var c = this.m_contactManager.m_contactList; c; c = c.m_next) {
// Is this contact disabled?
if (!c.IsEnabled()) {
continue;
}
// Prevent excessive sub-stepping.
if (c.m_toiCount > b2_maxSubSteps) {
continue;
}
var alpha = 1;
if (c.m_toiFlag) {
// This contact has a valid cached TOI.
alpha = c.m_toi;
}
else {
var fA_1 = c.GetFixtureA();
var fB_1 = c.GetFixtureB();
// Is there a sensor?
if (fA_1.IsSensor() || fB_1.IsSensor()) {
continue;
}
var bA_1 = fA_1.GetBody();
var bB_1 = fB_1.GetBody();
var typeA = bA_1.m_type;
var typeB = bB_1.m_type;
// DEBUG: b2Assert(typeA !== b2BodyType.b2_staticBody || typeB !== b2BodyType.b2_staticBody);
var activeA = bA_1.IsAwake() && typeA !== exports.b2BodyType.b2_staticBody;
var activeB = bB_1.IsAwake() && typeB !== exports.b2BodyType.b2_staticBody;
// Is at least one body active (awake and dynamic or kinematic)?
if (!activeA && !activeB) {
continue;
}
var collideA = bA_1.IsBullet() || typeA !== exports.b2BodyType.b2_dynamicBody;
var collideB = bB_1.IsBullet() || typeB !== exports.b2BodyType.b2_dynamicBody;
// Are these two non-bullet dynamic bodies?
if (!collideA && !collideB) {
continue;
}
// Compute the TOI for this contact.
// Put the sweeps onto the same time interval.
var alpha0 = bA_1.m_sweep.alpha0;
if (bA_1.m_sweep.alpha0 < bB_1.m_sweep.alpha0) {
alpha0 = bB_1.m_sweep.alpha0;
bA_1.m_sweep.Advance(alpha0);
}
else if (bB_1.m_sweep.alpha0 < bA_1.m_sweep.alpha0) {
alpha0 = bA_1.m_sweep.alpha0;
bB_1.m_sweep.Advance(alpha0);
}
// DEBUG: b2Assert(alpha0 < 1);
var indexA = c.GetChildIndexA();
var indexB = c.GetChildIndexB();
// Compute the time of impact in interval [0, minTOI]
var input = b2World.SolveTOI_s_toi_input;
input.proxyA.SetShape(fA_1.GetShape(), indexA);
input.proxyB.SetShape(fB_1.GetShape(), indexB);
input.sweepA.Copy(bA_1.m_sweep);
input.sweepB.Copy(bB_1.m_sweep);
input.tMax = 1;
var output = b2World.SolveTOI_s_toi_output;
b2TimeOfImpact(output, input);
// Beta is the fraction of the remaining portion of the .
var beta = output.t;
if (output.state === exports.b2TOIOutputState.e_touching) {
alpha = b2Min(alpha0 + (1 - alpha0) * beta, 1);
}
else {
alpha = 1;
}
c.m_toi = alpha;
c.m_toiFlag = true;
}
if (alpha < minAlpha) {
// This is the minimum TOI found so far.
minContact = c;
minAlpha = alpha;
}
}
if (minContact === null || 1 - 10 * b2_epsilon < minAlpha) {
// No more TOI events. Done!
this.m_stepComplete = true;
break;
}
// Advance the bodies to the TOI.
var fA = minContact.GetFixtureA();
var fB = minContact.GetFixtureB();
var bA = fA.GetBody();
var bB = fB.GetBody();
var backup1 = b2World.SolveTOI_s_backup1.Copy(bA.m_sweep);
var backup2 = b2World.SolveTOI_s_backup2.Copy(bB.m_sweep);
bA.Advance(minAlpha);
bB.Advance(minAlpha);
// The TOI contact likely has some new contact points.
minContact.Update(this.m_contactManager.m_contactListener);
minContact.m_toiFlag = false;
++minContact.m_toiCount;
// Is the contact solid?
if (!minContact.IsEnabled() || !minContact.IsTouching()) {
// Restore the sweeps.
minContact.SetEnabled(false);
bA.m_sweep.Copy(backup1);
bB.m_sweep.Copy(backup2);
bA.SynchronizeTransform();
bB.SynchronizeTransform();
continue;
}
bA.SetAwake(true);
bB.SetAwake(true);
// Build the island
island.Clear();
island.AddBody(bA);
island.AddBody(bB);
island.AddContact(minContact);
bA.m_islandFlag = true;
bB.m_islandFlag = true;
minContact.m_islandFlag = true;
// Get contacts on bodyA and bodyB.
// const bodies: b2Body[] = [bA, bB];
for (var i = 0; i < 2; ++i) {
var body = (i === 0) ? (bA) : (bB); // bodies[i];
if (body.m_type === exports.b2BodyType.b2_dynamicBody) {
for (var ce = body.m_contactList; ce; ce = ce.next) {
if (island.m_bodyCount === island.m_bodyCapacity) {
break;
}
if (island.m_contactCount === island.m_contactCapacity) {
break;
}
var contact = ce.contact;
// Has this contact already been added to the island?
if (contact.m_islandFlag) {
continue;
}
// Only add static, kinematic, or bullet bodies.
var other = ce.other;
if (other.m_type === exports.b2BodyType.b2_dynamicBody &&
!body.IsBullet() && !other.IsBullet()) {
continue;
}
// Skip sensors.
var sensorA = contact.m_fixtureA.m_isSensor;
var sensorB = contact.m_fixtureB.m_isSensor;
if (sensorA || sensorB) {
continue;
}
// Tentatively advance the body to the TOI.
var backup = b2World.SolveTOI_s_backup.Copy(other.m_sweep);
if (!other.m_islandFlag) {
other.Advance(minAlpha);
}
// Update the contact points
contact.Update(this.m_contactManager.m_contactListener);
// Was the contact disabled by the user?
if (!contact.IsEnabled()) {
other.m_sweep.Copy(backup);
other.SynchronizeTransform();
continue;
}
// Are there contact points?
if (!contact.IsTouching()) {
other.m_sweep.Copy(backup);
other.SynchronizeTransform();
continue;
}
// Add the contact to the island
contact.m_islandFlag = true;
island.AddContact(contact);
// Has the other body already been added to the island?
if (other.m_islandFlag) {
continue;
}
// Add the other body to the island.
other.m_islandFlag = true;
if (other.m_type !== exports.b2BodyType.b2_staticBody) {
other.SetAwake(true);
}
island.AddBody(other);
}
}
}
var subStep = b2World.SolveTOI_s_subStep;
subStep.dt = (1 - minAlpha) * step.dt;
subStep.inv_dt = 1 / subStep.dt;
subStep.dtRatio = 1;
subStep.positionIterations = 20;
subStep.velocityIterations = step.velocityIterations;
// #if B2_ENABLE_PARTICLE
subStep.particleIterations = step.particleIterations;
// #endif
subStep.warmStarting = false;
island.SolveTOI(subStep, bA.m_islandIndex, bB.m_islandIndex);
// Reset island flags and synchronize broad-phase proxies.
for (var i = 0; i < island.m_bodyCount; ++i) {
var body = island.m_bodies[i];
body.m_islandFlag = false;
if (body.m_type !== exports.b2BodyType.b2_dynamicBody) {
continue;
}
body.SynchronizeFixtures();
// Invalidate all contact TOIs on this displaced body.
for (var ce = body.m_contactList; ce; ce = ce.next) {
ce.contact.m_toiFlag = false;
ce.contact.m_islandFlag = false;
}
}
// Commit fixture proxy movements to the broad-phase so that new contacts are created.
// Also, some contacts can be destroyed.
this.m_contactManager.FindNewContacts();
if (this.m_subStepping) {
this.m_stepComplete = false;
break;
}
}
};
// #if B2_ENABLE_CONTROLLER
b2World.prototype.AddController = function (controller) {
// b2Assert(controller.m_world === null, "Controller can only be a member of one world");
// controller.m_world = this;
controller.m_next = this.m_controllerList;
controller.m_prev = null;
if (this.m_controllerList) {
this.m_controllerList.m_prev = controller;
}
this.m_controllerList = controller;
++this.m_controllerCount;
return controller;
};
b2World.prototype.RemoveController = function (controller) {
// b2Assert(controller.m_world === this, "Controller is not a member of this world");
if (controller.m_prev) {
controller.m_prev.m_next = controller.m_next;
}
if (controller.m_next) {
controller.m_next.m_prev = controller.m_prev;
}
if (this.m_controllerList === controller) {
this.m_controllerList = controller.m_next;
}
--this.m_controllerCount;
controller.m_prev = null;
controller.m_next = null;
// delete controller.m_world; // = null;
return controller;
};
// #endif
/// Take a time step. This performs collision detection, integration,
/// and constraint solution.
/// @param timeStep the amount of time to simulate, this should not vary.
/// @param velocityIterations for the velocity constraint solver.
/// @param positionIterations for the position constraint solver.
b2World.Step_s_step = new b2TimeStep();
b2World.Step_s_stepTimer = new b2Timer();
b2World.Step_s_timer = new b2Timer();
// #endif
/// Call this to draw shapes and other debug draw data.
b2World.DrawDebugData_s_color = new b2Color(0, 0, 0);
b2World.DrawDebugData_s_vs = b2Vec2.MakeArray(4);
b2World.DrawDebugData_s_xf = new b2Transform();
b2World.QueryFixtureShape_s_aabb = new b2AABB();
/// Ray-cast the world for all fixtures in the path of the ray. Your callback
/// controls whether you get the closest point, any point, or n-points.
/// The ray-cast ignores shapes that contain the starting point.
/// @param callback a user implemented callback class.
/// @param point1 the ray starting point
/// @param point2 the ray ending point
b2World.RayCast_s_input = new b2RayCastInput();
b2World.RayCast_s_output = new b2RayCastOutput();
b2World.RayCast_s_point = new b2Vec2();
b2World.DrawJoint_s_p1 = new b2Vec2();
b2World.DrawJoint_s_p2 = new b2Vec2();
b2World.DrawJoint_s_color = new b2Color(0.5, 0.8, 0.8);
b2World.DrawJoint_s_c = new b2Color();
b2World.DrawShape_s_ghostColor = new b2Color();
b2World.SolveTOI_s_subStep = new b2TimeStep();
b2World.SolveTOI_s_backup = new b2Sweep();
b2World.SolveTOI_s_backup1 = new b2Sweep();
b2World.SolveTOI_s_backup2 = new b2Sweep();
b2World.SolveTOI_s_toi_input = new b2TOIInput();
b2World.SolveTOI_s_toi_output = new b2TOIOutput();
return b2World;
}());
/*
* Copyright (c) 2006-2009 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.
*/
/**
* A controller edge is used to connect bodies and controllers
* together in a bipartite graph.
*/
var b2ControllerEdge = /** @class */ (function () {
function b2ControllerEdge(controller, body) {
this.prevBody = null; ///< the previous controller edge in the controllers's joint list
this.nextBody = null; ///< the next controller edge in the controllers's joint list
this.prevController = null; ///< the previous controller edge in the body's joint list
this.nextController = null; ///< the next controller edge in the body's joint list
this.controller = controller;
this.body = body;
}
return b2ControllerEdge;
}());
/**
* Base class for controllers. Controllers are a convience for
* encapsulating common per-step functionality.
*/
var b2Controller = /** @class */ (function () {
function b2Controller() {
// m_world: b2World;
this.m_bodyList = null;
this.m_bodyCount = 0;
this.m_prev = null;
this.m_next = null;
}
/**
* Get the next controller in the world's body list.
*/
b2Controller.prototype.GetNext = function () {
return this.m_next;
};
/**
* Get the previous controller in the world's body list.
*/
b2Controller.prototype.GetPrev = function () {
return this.m_prev;
};
/**
* Get the parent world of this body.
*/
// GetWorld() {
// return this.m_world;
// }
/**
* Get the attached body list
*/
b2Controller.prototype.GetBodyList = function () {
return this.m_bodyList;
};
/**
* Adds a body to the controller list.
*/
b2Controller.prototype.AddBody = function (body) {
var edge = new b2ControllerEdge(this, body);
//Add edge to controller list
edge.nextBody = this.m_bodyList;
edge.prevBody = null;
if (this.m_bodyList) {
this.m_bodyList.prevBody = edge;
}
this.m_bodyList = edge;
++this.m_bodyCount;
//Add edge to body list
edge.nextController = body.m_controllerList;
edge.prevController = null;
if (body.m_controllerList) {
body.m_controllerList.prevController = edge;
}
body.m_controllerList = edge;
++body.m_controllerCount;
};
/**
* Removes a body from the controller list.
*/
b2Controller.prototype.RemoveBody = function (body) {
//Assert that the controller is not empty
if (this.m_bodyCount <= 0) {
throw new Error();
}
//Find the corresponding edge
/*b2ControllerEdge*/
var edge = this.m_bodyList;
while (edge && edge.body !== body) {
edge = edge.nextBody;
}
//Assert that we are removing a body that is currently attached to the controller
if (edge === null) {
throw new Error();
}
//Remove edge from controller list
if (edge.prevBody) {
edge.prevBody.nextBody = edge.nextBody;
}
if (edge.nextBody) {
edge.nextBody.prevBody = edge.prevBody;
}
if (this.m_bodyList === edge) {
this.m_bodyList = edge.nextBody;
}
--this.m_bodyCount;
//Remove edge from body list
if (edge.nextController) {
edge.nextController.prevController = edge.prevController;
}
if (edge.prevController) {
edge.prevController.nextController = edge.nextController;
}
if (body.m_controllerList === edge) {
body.m_controllerList = edge.nextController;
}
--body.m_controllerCount;
};
/**
* Removes all bodies from the controller list.
*/
b2Controller.prototype.Clear = function () {
while (this.m_bodyList) {
this.RemoveBody(this.m_bodyList.body);
}
this.m_bodyCount = 0;
};
return b2Controller;
}());
// #endif
/*
* Copyright (c) 2006-2009 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.
*/
/**
* Calculates buoyancy forces for fluids in the form of a half
* plane.
*/
var b2BuoyancyController = /** @class */ (function (_super) {
__extends(b2BuoyancyController, _super);
function b2BuoyancyController() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* The outer surface normal
*/
_this.normal = new b2Vec2(0, 1);
/**
* The height of the fluid surface along the normal
*/
_this.offset = 0;
/**
* The fluid density
*/
_this.density = 0;
/**
* Fluid velocity, for drag calculations
*/
_this.velocity = new b2Vec2(0, 0);
/**
* Linear drag co-efficient
*/
_this.linearDrag = 0;
/**
* Angular drag co-efficient
*/
_this.angularDrag = 0;
/**
* If false, bodies are assumed to be uniformly dense, otherwise
* use the shapes densities
*/
_this.useDensity = false; //False by default to prevent a gotcha
/**
* If true, gravity is taken from the world instead of the
*/
_this.useWorldGravity = true;
/**
* Gravity vector, if the world's gravity is not used
*/
_this.gravity = new b2Vec2(0, 0);
return _this;
}
b2BuoyancyController.prototype.Step = function (step) {
if (!this.m_bodyList) {
return;
}
if (this.useWorldGravity) {
this.gravity.Copy(this.m_bodyList.body.GetWorld().GetGravity());
}
for (var i = this.m_bodyList; i; i = i.nextBody) {
var body = i.body;
if (!body.IsAwake()) {
//Buoyancy force is just a function of position,
//so unlike most forces, it is safe to ignore sleeping bodes
continue;
}
var areac = new b2Vec2();
var massc = new b2Vec2();
var area = 0;
var mass = 0;
for (var fixture = body.GetFixtureList(); fixture; fixture = fixture.m_next) {
var sc = new b2Vec2();
var sarea = fixture.GetShape().ComputeSubmergedArea(this.normal, this.offset, body.GetTransform(), sc);
area += sarea;
areac.x += sarea * sc.x;
areac.y += sarea * sc.y;
var shapeDensity = 0;
if (this.useDensity) {
//TODO: Expose density publicly
shapeDensity = fixture.GetDensity();
}
else {
shapeDensity = 1;
}
mass += sarea * shapeDensity;
massc.x += sarea * sc.x * shapeDensity;
massc.y += sarea * sc.y * shapeDensity;
}
areac.x /= area;
areac.y /= area;
// b2Vec2 localCentroid = b2MulT(body->GetXForm(),areac);
massc.x /= mass;
massc.y /= mass;
if (area < b2_epsilon) {
continue;
}
//Buoyancy
var buoyancyForce = this.gravity.Clone().SelfNeg();
buoyancyForce.SelfMul(this.density * area);
body.ApplyForce(buoyancyForce, massc);
//Linear drag
var dragForce = body.GetLinearVelocityFromWorldPoint(areac, new b2Vec2());
dragForce.SelfSub(this.velocity);
dragForce.SelfMul((-this.linearDrag * area));
body.ApplyForce(dragForce, areac);
//Angular drag
//TODO: Something that makes more physical sense?
body.ApplyTorque((-body.GetInertia() / body.GetMass() * area * body.GetAngularVelocity() * this.angularDrag));
}
};
b2BuoyancyController.prototype.Draw = function (debugDraw) {
var r = 100;
var p1 = new b2Vec2();
var p2 = new b2Vec2();
p1.x = this.normal.x * this.offset + this.normal.y * r;
p1.y = this.normal.y * this.offset - this.normal.x * r;
p2.x = this.normal.x * this.offset - this.normal.y * r;
p2.y = this.normal.y * this.offset + this.normal.x * r;
var color = new b2Color(0, 0, 0.8);
debugDraw.DrawSegment(p1, p2, color);
};
return b2BuoyancyController;
}(b2Controller));
// #endif
/*
* Copyright (c) 2006-2009 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.
*/
/**
* Applies a force every frame
*/
var b2ConstantAccelController = /** @class */ (function (_super) {
__extends(b2ConstantAccelController, _super);
function b2ConstantAccelController() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* The acceleration to apply
*/
_this.A = new b2Vec2(0, 0);
return _this;
}
b2ConstantAccelController.prototype.Step = function (step) {
var dtA = b2Vec2.MulSV(step.dt, this.A, b2ConstantAccelController.Step_s_dtA);
for (var i = this.m_bodyList; i; i = i.nextBody) {
var body = i.body;
if (!body.IsAwake()) {
continue;
}
body.SetLinearVelocity(b2Vec2.AddVV(body.GetLinearVelocity(), dtA, b2Vec2.s_t0));
}
};
b2ConstantAccelController.prototype.Draw = function (draw) { };
b2ConstantAccelController.Step_s_dtA = new b2Vec2();
return b2ConstantAccelController;
}(b2Controller));
// #endif
/*
* Copyright (c) 2006-2009 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.
*/
/**
* Applies a force every frame
*/
var b2ConstantForceController = /** @class */ (function (_super) {
__extends(b2ConstantForceController, _super);
function b2ConstantForceController() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* The force to apply
*/
_this.F = new b2Vec2(0, 0);
return _this;
}
b2ConstantForceController.prototype.Step = function (step) {
for (var i = this.m_bodyList; i; i = i.nextBody) {
var body = i.body;
if (!body.IsAwake()) {
continue;
}
body.ApplyForce(this.F, body.GetWorldCenter());
}
};
b2ConstantForceController.prototype.Draw = function (draw) { };
return b2ConstantForceController;
}(b2Controller));
// #endif
/*
* Copyright (c) 2006-2009 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.
*/
/**
* Applies simplified gravity between every pair of bodies
*/
var b2GravityController = /** @class */ (function (_super) {
__extends(b2GravityController, _super);
function b2GravityController() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* Specifies the strength of the gravitiation force
*/
_this.G = 1;
/**
* If true, gravity is proportional to r^-2, otherwise r^-1
*/
_this.invSqr = true;
return _this;
}
/**
* @see b2Controller::Step
*/
b2GravityController.prototype.Step = function (step) {
if (this.invSqr) {
for (var i = this.m_bodyList; i; i = i.nextBody) {
var body1 = i.body;
var p1 = body1.GetWorldCenter();
var mass1 = body1.GetMass();
for (var j = this.m_bodyList; j && j !== i; j = j.nextBody) {
var body2 = j.body;
var p2 = body2.GetWorldCenter();
var mass2 = body2.GetMass();
var dx = p2.x - p1.x;
var dy = p2.y - p1.y;
var r2 = dx * dx + dy * dy;
if (r2 < b2_epsilon) {
continue;
}
var f = b2GravityController.Step_s_f.Set(dx, dy);
f.SelfMul(this.G / r2 / b2Sqrt(r2) * mass1 * mass2);
if (body1.IsAwake()) {
body1.ApplyForce(f, p1);
}
if (body2.IsAwake()) {
body2.ApplyForce(f.SelfMul(-1), p2);
}
}
}
}
else {
for (var i = this.m_bodyList; i; i = i.nextBody) {
var body1 = i.body;
var p1 = body1.GetWorldCenter();
var mass1 = body1.GetMass();
for (var j = this.m_bodyList; j && j !== i; j = j.nextBody) {
var body2 = j.body;
var p2 = body2.GetWorldCenter();
var mass2 = body2.GetMass();
var dx = p2.x - p1.x;
var dy = p2.y - p1.y;
var r2 = dx * dx + dy * dy;
if (r2 < b2_epsilon) {
continue;
}
var f = b2GravityController.Step_s_f.Set(dx, dy);
f.SelfMul(this.G / r2 * mass1 * mass2);
if (body1.IsAwake()) {
body1.ApplyForce(f, p1);
}
if (body2.IsAwake()) {
body2.ApplyForce(f.SelfMul(-1), p2);
}
}
}
}
};
b2GravityController.prototype.Draw = function (draw) { };
b2GravityController.Step_s_f = new b2Vec2();
return b2GravityController;
}(b2Controller));
// #endif
/*
* Copyright (c) 2006-2007 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.
*/
/**
* Applies top down linear damping to the controlled bodies
* The damping is calculated by multiplying velocity by a matrix
* in local co-ordinates.
*/
var b2TensorDampingController = /** @class */ (function (_super) {
__extends(b2TensorDampingController, _super);
function b2TensorDampingController() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/// Tensor to use in damping model
_this.T = new b2Mat22();
/*Some examples (matrixes in format (row1; row2))
(-a 0; 0 -a) Standard isotropic damping with strength a
( 0 a; -a 0) Electron in fixed field - a force at right angles to velocity with proportional magnitude
(-a 0; 0 -b) Differing x and y damping. Useful e.g. for top-down wheels.
*/
//By the way, tensor in this case just means matrix, don't let the terminology get you down.
/// Set this to a positive number to clamp the maximum amount of damping done.
_this.maxTimestep = 0;
return _this;
}
// Typically one wants maxTimestep to be 1/(max eigenvalue of T), so that damping will never cause something to reverse direction
/**
* @see b2Controller::Step
*/
b2TensorDampingController.prototype.Step = function (step) {
var timestep = step.dt;
if (timestep <= b2_epsilon) {
return;
}
if (timestep > this.maxTimestep && this.maxTimestep > 0) {
timestep = this.maxTimestep;
}
for (var i = this.m_bodyList; i; i = i.nextBody) {
var body = i.body;
if (!body.IsAwake()) {
continue;
}
var damping = body.GetWorldVector(b2Mat22.MulMV(this.T, body.GetLocalVector(body.GetLinearVelocity(), b2Vec2.s_t0), b2Vec2.s_t1), b2TensorDampingController.Step_s_damping);
// body->SetLinearVelocity(body->GetLinearVelocity() + timestep * damping);
body.SetLinearVelocity(b2Vec2.AddVV(body.GetLinearVelocity(), b2Vec2.MulSV(timestep, damping, b2Vec2.s_t0), b2Vec2.s_t1));
}
};
b2TensorDampingController.prototype.Draw = function (draw) { };
/**
* Sets damping independantly along the x and y axes
*/
b2TensorDampingController.prototype.SetAxisAligned = function (xDamping, yDamping) {
this.T.ex.x = (-xDamping);
this.T.ex.y = 0;
this.T.ey.x = 0;
this.T.ey.y = (-yDamping);
if (xDamping > 0 || yDamping > 0) {
this.maxTimestep = 1 / b2Max(xDamping, yDamping);
}
else {
this.maxTimestep = 0;
}
};
b2TensorDampingController.Step_s_damping = new b2Vec2();
return b2TensorDampingController;
}(b2Controller));
// #endif
/*
* Copyright (c) 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.
*/
///
var b2RopeDef = /** @class */ (function () {
function b2RopeDef() {
///
this.vertices = [];
///
this.count = 0;
///
this.masses = [];
///
this.gravity = new b2Vec2(0, 0);
///
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;
}
return b2RopeDef;
}());
///
var b2Rope = /** @class */ (function () {
function b2Rope() {
this.m_count = 0;
this.m_ps = [];
this.m_p0s = [];
this.m_vs = [];
this.m_ims = [];
this.m_Ls = [];
this.m_as = [];
this.m_gravity = new b2Vec2();
this.m_damping = 0;
this.m_k2 = 1;
this.m_k3 = 0.1;
}
b2Rope.prototype.GetVertexCount = function () {
return this.m_count;
};
b2Rope.prototype.GetVertices = function () {
return this.m_ps;
};
///
b2Rope.prototype.Initialize = function (def) {
// DEBUG: b2Assert(def.count >= 3);
this.m_count = def.count;
// this.m_ps = (b2Vec2*)b2Alloc(this.m_count * sizeof(b2Vec2));
this.m_ps = b2Vec2.MakeArray(this.m_count);
// this.m_p0s = (b2Vec2*)b2Alloc(this.m_count * sizeof(b2Vec2));
this.m_p0s = b2Vec2.MakeArray(this.m_count);
// this.m_vs = (b2Vec2*)b2Alloc(this.m_count * sizeof(b2Vec2));
this.m_vs = b2Vec2.MakeArray(this.m_count);
// this.m_ims = (float32*)b2Alloc(this.m_count * sizeof(float32));
this.m_ims = b2MakeNumberArray(this.m_count);
for (var i = 0; i < this.m_count; ++i) {
this.m_ps[i].Copy(def.vertices[i]);
this.m_p0s[i].Copy(def.vertices[i]);
this.m_vs[i].SetZero();
var m = def.masses[i];
if (m > 0) {
this.m_ims[i] = 1 / m;
}
else {
this.m_ims[i] = 0;
}
}
var count2 = this.m_count - 1;
var count3 = this.m_count - 2;
// this.m_Ls = (float32*)be2Alloc(count2 * sizeof(float32));
this.m_Ls = b2MakeNumberArray(count2);
// this.m_as = (float32*)b2Alloc(count3 * sizeof(float32));
this.m_as = b2MakeNumberArray(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] = b2Vec2.DistanceVV(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 = b2Vec2.SubVV(p2, p1, b2Vec2.s_t0);
var d2 = b2Vec2.SubVV(p3, p2, b2Vec2.s_t1);
var a = b2Vec2.CrossVV(d1, d2);
var b = b2Vec2.DotVV(d1, d2);
this.m_as[i] = b2Atan2(a, b);
}
this.m_gravity.Copy(def.gravity);
this.m_damping = def.damping;
this.m_k2 = def.k2;
this.m_k3 = def.k3;
};
///
b2Rope.prototype.Step = function (h, iterations) {
if (h === 0) {
return;
}
var d = Math.exp(-h * this.m_damping);
for (var i = 0; i < this.m_count; ++i) {
this.m_p0s[i].Copy(this.m_ps[i]);
if (this.m_ims[i] > 0) {
this.m_vs[i].SelfMulAdd(h, this.m_gravity);
}
this.m_vs[i].SelfMul(d);
this.m_ps[i].SelfMulAdd(h, this.m_vs[i]);
}
for (var i = 0; i < iterations; ++i) {
this.SolveC2();
this.SolveC3();
this.SolveC2();
}
var inv_h = 1 / h;
for (var i = 0; i < this.m_count; ++i) {
b2Vec2.MulSV(inv_h, b2Vec2.SubVV(this.m_ps[i], this.m_p0s[i], b2Vec2.s_t0), this.m_vs[i]);
}
};
b2Rope.prototype.SolveC2 = function () {
var count2 = this.m_count - 1;
for (var i = 0; i < count2; ++i) {
var p1 = this.m_ps[i];
var p2 = this.m_ps[i + 1];
var d = b2Vec2.SubVV(p2, p1, b2Rope.s_d);
var L = d.Normalize();
var im1 = this.m_ims[i];
var im2 = this.m_ims[i + 1];
if (im1 + im2 === 0) {
continue;
}
var s1 = im1 / (im1 + im2);
var s2 = im2 / (im1 + im2);
p1.SelfMulSub(this.m_k2 * s1 * (this.m_Ls[i] - L), d);
p2.SelfMulAdd(this.m_k2 * s2 * (this.m_Ls[i] - L), d);
// this.m_ps[i] = p1;
// this.m_ps[i + 1] = p2;
}
};
b2Rope.prototype.SetAngle = function (angle) {
var count3 = this.m_count - 2;
for (var i = 0; i < count3; ++i) {
this.m_as[i] = angle;
}
};
b2Rope.prototype.SolveC3 = function () {
var count3 = this.m_count - 2;
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 m1 = this.m_ims[i];
var m2 = this.m_ims[i + 1];
var m3 = this.m_ims[i + 2];
var d1 = b2Vec2.SubVV(p2, p1, b2Rope.s_d1);
var d2 = b2Vec2.SubVV(p3, p2, b2Rope.s_d2);
var L1sqr = d1.LengthSquared();
var L2sqr = d2.LengthSquared();
if (L1sqr * L2sqr === 0) {
continue;
}
var a = b2Vec2.CrossVV(d1, d2);
var b = b2Vec2.DotVV(d1, d2);
var angle = b2Atan2(a, b);
var Jd1 = b2Vec2.MulSV((-1 / L1sqr), d1.SelfSkew(), b2Rope.s_Jd1);
var Jd2 = b2Vec2.MulSV((1 / L2sqr), d2.SelfSkew(), b2Rope.s_Jd2);
var J1 = b2Vec2.NegV(Jd1, b2Rope.s_J1);
var J2 = b2Vec2.SubVV(Jd1, Jd2, b2Rope.s_J2);
var J3 = Jd2;
var mass = m1 * b2Vec2.DotVV(J1, J1) + m2 * b2Vec2.DotVV(J2, J2) + m3 * b2Vec2.DotVV(J3, J3);
if (mass === 0) {
continue;
}
mass = 1 / mass;
var C = angle - this.m_as[i];
while (C > b2_pi) {
angle -= 2 * b2_pi;
C = angle - this.m_as[i];
}
while (C < -b2_pi) {
angle += 2 * b2_pi;
C = angle - this.m_as[i];
}
var impulse = -this.m_k3 * mass * C;
p1.SelfMulAdd((m1 * impulse), J1);
p2.SelfMulAdd((m2 * impulse), J2);
p3.SelfMulAdd((m3 * impulse), J3);
// this.m_ps[i] = p1;
// this.m_ps[i + 1] = p2;
// this.m_ps[i + 2] = p3;
}
};
b2Rope.prototype.Draw = function (draw) {
var c = new b2Color(0.4, 0.5, 0.7);
for (var i = 0; i < this.m_count - 1; ++i) {
draw.DrawSegment(this.m_ps[i], this.m_ps[i + 1], c);
}
};
///
b2Rope.s_d = new b2Vec2();
b2Rope.s_d1 = new b2Vec2();
b2Rope.s_d2 = new b2Vec2();
b2Rope.s_Jd1 = new b2Vec2();
b2Rope.s_Jd2 = new b2Vec2();
b2Rope.s_J1 = new b2Vec2();
b2Rope.s_J2 = new b2Vec2();
return b2Rope;
}());
/*
* Copyright (c) 2006-2009 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.
*/
exports.b2Assert = b2Assert;
exports.b2Maybe = b2Maybe;
exports.b2_maxFloat = b2_maxFloat;
exports.b2_epsilon = b2_epsilon;
exports.b2_epsilon_sq = b2_epsilon_sq;
exports.b2_pi = b2_pi;
exports.b2_maxManifoldPoints = b2_maxManifoldPoints;
exports.b2_maxPolygonVertices = b2_maxPolygonVertices;
exports.b2_aabbExtension = b2_aabbExtension;
exports.b2_aabbMultiplier = b2_aabbMultiplier;
exports.b2_linearSlop = b2_linearSlop;
exports.b2_angularSlop = b2_angularSlop;
exports.b2_polygonRadius = b2_polygonRadius;
exports.b2_maxSubSteps = b2_maxSubSteps;
exports.b2_maxTOIContacts = b2_maxTOIContacts;
exports.b2_velocityThreshold = b2_velocityThreshold;
exports.b2_maxLinearCorrection = b2_maxLinearCorrection;
exports.b2_maxAngularCorrection = b2_maxAngularCorrection;
exports.b2_maxTranslation = b2_maxTranslation;
exports.b2_maxTranslationSquared = b2_maxTranslationSquared;
exports.b2_maxRotation = b2_maxRotation;
exports.b2_maxRotationSquared = b2_maxRotationSquared;
exports.b2_baumgarte = b2_baumgarte;
exports.b2_toiBaumgarte = b2_toiBaumgarte;
exports.b2_invalidParticleIndex = b2_invalidParticleIndex;
exports.b2_maxParticleIndex = b2_maxParticleIndex;
exports.b2_particleStride = b2_particleStride;
exports.b2_minParticleWeight = b2_minParticleWeight;
exports.b2_maxParticlePressure = b2_maxParticlePressure;
exports.b2_maxParticleForce = b2_maxParticleForce;
exports.b2_maxTriadDistance = b2_maxTriadDistance;
exports.b2_maxTriadDistanceSquared = b2_maxTriadDistanceSquared;
exports.b2_minParticleSystemBufferCapacity = b2_minParticleSystemBufferCapacity;
exports.b2_barrierCollisionTime = b2_barrierCollisionTime;
exports.b2_timeToSleep = b2_timeToSleep;
exports.b2_linearSleepTolerance = b2_linearSleepTolerance;
exports.b2_angularSleepTolerance = b2_angularSleepTolerance;
exports.b2Alloc = b2Alloc;
exports.b2Free = b2Free;
exports.b2Log = b2Log;
exports.b2Version = b2Version;
exports.b2_version = b2_version;
exports.b2_branch = b2_branch;
exports.b2_commit = b2_commit;
exports.b2ParseInt = b2ParseInt;
exports.b2ParseUInt = b2ParseUInt;
exports.b2MakeArray = b2MakeArray;
exports.b2MakeNullArray = b2MakeNullArray;
exports.b2MakeNumberArray = b2MakeNumberArray;
exports.b2_pi_over_180 = b2_pi_over_180;
exports.b2_180_over_pi = b2_180_over_pi;
exports.b2_two_pi = b2_two_pi;
exports.b2Abs = b2Abs;
exports.b2Min = b2Min;
exports.b2Max = b2Max;
exports.b2Clamp = b2Clamp;
exports.b2Swap = b2Swap;
exports.b2IsValid = b2IsValid;
exports.b2Sq = b2Sq;
exports.b2InvSqrt = b2InvSqrt;
exports.b2Sqrt = b2Sqrt;
exports.b2Pow = b2Pow;
exports.b2DegToRad = b2DegToRad;
exports.b2RadToDeg = b2RadToDeg;
exports.b2Cos = b2Cos;
exports.b2Sin = b2Sin;
exports.b2Acos = b2Acos;
exports.b2Asin = b2Asin;
exports.b2Atan2 = b2Atan2;
exports.b2NextPowerOfTwo = b2NextPowerOfTwo;
exports.b2IsPowerOfTwo = b2IsPowerOfTwo;
exports.b2Random = b2Random;
exports.b2RandomRange = b2RandomRange;
exports.b2Vec2 = b2Vec2;
exports.b2Vec2_zero = b2Vec2_zero;
exports.b2Vec3 = b2Vec3;
exports.b2Mat22 = b2Mat22;
exports.b2Mat33 = b2Mat33;
exports.b2Rot = b2Rot;
exports.b2Transform = b2Transform;
exports.b2Sweep = b2Sweep;
exports.b2Color = b2Color;
exports.b2Draw = b2Draw;
exports.b2Timer = b2Timer;
exports.b2Counter = b2Counter;
exports.b2GrowableStack = b2GrowableStack;
exports.b2BlockAllocator = b2BlockAllocator;
exports.b2StackAllocator = b2StackAllocator;
exports.b2ContactFeature = b2ContactFeature;
exports.b2ContactID = b2ContactID;
exports.b2ManifoldPoint = b2ManifoldPoint;
exports.b2Manifold = b2Manifold;
exports.b2WorldManifold = b2WorldManifold;
exports.b2GetPointStates = b2GetPointStates;
exports.b2ClipVertex = b2ClipVertex;
exports.b2RayCastInput = b2RayCastInput;
exports.b2RayCastOutput = b2RayCastOutput;
exports.b2AABB = b2AABB;
exports.b2TestOverlapAABB = b2TestOverlapAABB;
exports.b2ClipSegmentToLine = b2ClipSegmentToLine;
exports.b2TestOverlapShape = b2TestOverlapShape;
exports.b2DistanceProxy = b2DistanceProxy;
exports.b2SimplexCache = b2SimplexCache;
exports.b2DistanceInput = b2DistanceInput;
exports.b2DistanceOutput = b2DistanceOutput;
exports.b2ShapeCastInput = b2ShapeCastInput;
exports.b2ShapeCastOutput = b2ShapeCastOutput;
exports.b2_gjk_reset = b2_gjk_reset;
exports.b2SimplexVertex = b2SimplexVertex;
exports.b2Simplex = b2Simplex;
exports.b2Distance = b2Distance;
exports.b2ShapeCast = b2ShapeCast;
exports.b2Pair = b2Pair;
exports.b2BroadPhase = b2BroadPhase;
exports.b2PairLessThan = b2PairLessThan;
exports.b2TreeNode = b2TreeNode;
exports.b2DynamicTree = b2DynamicTree;
exports.b2_toi_reset = b2_toi_reset;
exports.b2TOIInput = b2TOIInput;
exports.b2TOIOutput = b2TOIOutput;
exports.b2SeparationFunction = b2SeparationFunction;
exports.b2TimeOfImpact = b2TimeOfImpact;
exports.b2CollideCircles = b2CollideCircles;
exports.b2CollidePolygonAndCircle = b2CollidePolygonAndCircle;
exports.b2CollidePolygons = b2CollidePolygons;
exports.b2CollideEdgeAndCircle = b2CollideEdgeAndCircle;
exports.b2CollideEdgeAndPolygon = b2CollideEdgeAndPolygon;
exports.b2MassData = b2MassData;
exports.b2Shape = b2Shape;
exports.b2CircleShape = b2CircleShape;
exports.b2PolygonShape = b2PolygonShape;
exports.b2EdgeShape = b2EdgeShape;
exports.b2ChainShape = b2ChainShape;
exports.b2Filter = b2Filter;
exports.b2FixtureDef = b2FixtureDef;
exports.b2FixtureProxy = b2FixtureProxy;
exports.b2Fixture = b2Fixture;
exports.b2BodyDef = b2BodyDef;
exports.b2Body = b2Body;
exports.b2World = b2World;
exports.b2DestructionListener = b2DestructionListener;
exports.b2ContactFilter = b2ContactFilter;
exports.b2ContactImpulse = b2ContactImpulse;
exports.b2ContactListener = b2ContactListener;
exports.b2QueryCallback = b2QueryCallback;
exports.b2RayCastCallback = b2RayCastCallback;
exports.b2Island = b2Island;
exports.b2Profile = b2Profile;
exports.b2TimeStep = b2TimeStep;
exports.b2Position = b2Position;
exports.b2Velocity = b2Velocity;
exports.b2SolverData = b2SolverData;
exports.b2ContactManager = b2ContactManager;
exports.b2MixFriction = b2MixFriction;
exports.b2MixRestitution = b2MixRestitution;
exports.b2ContactEdge = b2ContactEdge;
exports.b2Contact = b2Contact;
exports.b2ContactRegister = b2ContactRegister;
exports.b2ContactFactory = b2ContactFactory;
exports.g_blockSolve = g_blockSolve;
exports.b2VelocityConstraintPoint = b2VelocityConstraintPoint;
exports.b2ContactVelocityConstraint = b2ContactVelocityConstraint;
exports.b2ContactPositionConstraint = b2ContactPositionConstraint;
exports.b2ContactSolverDef = b2ContactSolverDef;
exports.b2PositionSolverManifold = b2PositionSolverManifold;
exports.b2ContactSolver = b2ContactSolver;
exports.b2CircleContact = b2CircleContact;
exports.b2PolygonContact = b2PolygonContact;
exports.b2PolygonAndCircleContact = b2PolygonAndCircleContact;
exports.b2EdgeAndCircleContact = b2EdgeAndCircleContact;
exports.b2EdgeAndPolygonContact = b2EdgeAndPolygonContact;
exports.b2ChainAndCircleContact = b2ChainAndCircleContact;
exports.b2ChainAndPolygonContact = b2ChainAndPolygonContact;
exports.b2Jacobian = b2Jacobian;
exports.b2JointEdge = b2JointEdge;
exports.b2JointDef = b2JointDef;
exports.b2Joint = b2Joint;
exports.b2AreaJointDef = b2AreaJointDef;
exports.b2AreaJoint = b2AreaJoint;
exports.b2DistanceJointDef = b2DistanceJointDef;
exports.b2DistanceJoint = b2DistanceJoint;
exports.b2FrictionJointDef = b2FrictionJointDef;
exports.b2FrictionJoint = b2FrictionJoint;
exports.b2GearJointDef = b2GearJointDef;
exports.b2GearJoint = b2GearJoint;
exports.b2MotorJointDef = b2MotorJointDef;
exports.b2MotorJoint = b2MotorJoint;
exports.b2MouseJointDef = b2MouseJointDef;
exports.b2MouseJoint = b2MouseJoint;
exports.b2PrismaticJointDef = b2PrismaticJointDef;
exports.b2PrismaticJoint = b2PrismaticJoint;
exports.b2_minPulleyLength = b2_minPulleyLength;
exports.b2PulleyJointDef = b2PulleyJointDef;
exports.b2PulleyJoint = b2PulleyJoint;
exports.b2RevoluteJointDef = b2RevoluteJointDef;
exports.b2RevoluteJoint = b2RevoluteJoint;
exports.b2RopeJointDef = b2RopeJointDef;
exports.b2RopeJoint = b2RopeJoint;
exports.b2WeldJointDef = b2WeldJointDef;
exports.b2WeldJoint = b2WeldJoint;
exports.b2WheelJointDef = b2WheelJointDef;
exports.b2WheelJoint = b2WheelJoint;
exports.b2ControllerEdge = b2ControllerEdge;
exports.b2Controller = b2Controller;
exports.b2BuoyancyController = b2BuoyancyController;
exports.b2ConstantAccelController = b2ConstantAccelController;
exports.b2ConstantForceController = b2ConstantForceController;
exports.b2GravityController = b2GravityController;
exports.b2TensorDampingController = b2TensorDampingController;
exports.b2ParticleDef = b2ParticleDef;
exports.b2CalculateParticleIterations = b2CalculateParticleIterations;
exports.b2ParticleHandle = b2ParticleHandle;
exports.b2ParticleGroupDef = b2ParticleGroupDef;
exports.b2ParticleGroup = b2ParticleGroup;
exports.b2GrowableBuffer = b2GrowableBuffer;
exports.b2FixtureParticleQueryCallback = b2FixtureParticleQueryCallback;
exports.b2ParticleContact = b2ParticleContact;
exports.b2ParticleBodyContact = b2ParticleBodyContact;
exports.b2ParticlePair = b2ParticlePair;
exports.b2ParticleTriad = b2ParticleTriad;
exports.b2ParticleSystemDef = b2ParticleSystemDef;
exports.b2RopeDef = b2RopeDef;
exports.b2Rope = b2Rope;
Object.defineProperty(exports, '__esModule', { value: true });
})));