@awayfl/awayfl-player
Version:
Flash Player emulator for executing SWF files (published for FP versions 6 and up) in javascript
874 lines (757 loc) • 31.7 kB
text/typescript
import { b2Shape } from '../../Collision/Shapes/b2Shape';
import { b2Mat22, b2Vec2, b2Math } from '../../Common/Math';
import { b2Fixture } from '../b2Fixture';
import { b2Body } from '../b2Body';
import { b2Settings } from '../../Common/b2Settings';
import { b2Manifold } from '../../Collision/b2Manifold';
import { b2WorldManifold } from '../../Collision/b2WorldManifold';
import { b2TimeStep } from '../b2TimeStep';
import { b2ManifoldPoint } from '../../Collision/b2ManifoldPoint';
import { b2Contact, b2ContactConstraintPoint, b2ContactConstraint, b2PositionSolverManifold } from '../Contacts';
/**
* @private
*/
export class b2ContactSolver {
public constructor() {
}
private static s_worldManifold: b2WorldManifold = new b2WorldManifold();
public Initialize(step: b2TimeStep, contacts: Array<b2Contact>, contactCount: number /** int */, allocator: any): void {
let contact: b2Contact;
this.m_step.Set(step);
this.m_allocator = allocator;
let i: number /** int */;
let tVec: b2Vec2;
let tMat: b2Mat22;
this.m_constraintCount = contactCount;
// fill vector to hold enough constraints
while (this.m_constraints.length < this.m_constraintCount) {
this.m_constraints[this.m_constraints.length] = new b2ContactConstraint();
}
for (i = 0; i < contactCount; ++i) {
contact = contacts[i];
const fixtureA: b2Fixture = contact.m_fixtureA;
const fixtureB: b2Fixture = contact.m_fixtureB;
const shapeA: b2Shape = fixtureA.m_shape;
const shapeB: b2Shape = fixtureB.m_shape;
const radiusA: number = shapeA.m_radius;
const radiusB: number = shapeB.m_radius;
const bodyA: b2Body = fixtureA.m_body;
const bodyB: b2Body = fixtureB.m_body;
const manifold: b2Manifold = contact.GetManifold();
const friction: number = b2Settings.b2MixFriction(fixtureA.GetFriction(), fixtureB.GetFriction());
const restitution: number = b2Settings.b2MixRestitution(fixtureA.GetRestitution(), fixtureB.GetRestitution());
//var vA:b2Vec2 = bodyA.m_linearVelocity.Copy();
const vAX: number = bodyA.m_linearVelocity.x;
const vAY: number = bodyA.m_linearVelocity.y;
//var vB:b2Vec2 = bodyB.m_linearVelocity.Copy();
const vBX: number = bodyB.m_linearVelocity.x;
const vBY: number = bodyB.m_linearVelocity.y;
const wA: number = bodyA.m_angularVelocity;
const wB: number = bodyB.m_angularVelocity;
b2Settings.b2Assert(manifold.m_pointCount > 0);
b2ContactSolver.s_worldManifold.Initialize(manifold, bodyA.m_xf, radiusA, bodyB.m_xf, radiusB);
const normalX: number = b2ContactSolver.s_worldManifold.m_normal.x;
const normalY: number = b2ContactSolver.s_worldManifold.m_normal.y;
const cc: b2ContactConstraint = this.m_constraints[ i ];
cc.bodyA = bodyA; //p
cc.bodyB = bodyB; //p
cc.manifold = manifold; //p
//c.normal = normal;
cc.normal.x = normalX;
cc.normal.y = normalY;
cc.pointCount = manifold.m_pointCount;
cc.friction = friction;
cc.restitution = restitution;
cc.localPlaneNormal.x = manifold.m_localPlaneNormal.x;
cc.localPlaneNormal.y = manifold.m_localPlaneNormal.y;
cc.localPoint.x = manifold.m_localPoint.x;
cc.localPoint.y = manifold.m_localPoint.y;
cc.radius = radiusA + radiusB;
cc.type = manifold.m_type;
for (let k: number /** uint */ = 0; k < cc.pointCount; ++k) {
const cp: b2ManifoldPoint = manifold.m_points[ k ];
const ccp: b2ContactConstraintPoint = cc.points[ k ];
ccp.normalImpulse = cp.m_normalImpulse;
ccp.tangentImpulse = cp.m_tangentImpulse;
ccp.localPoint.SetV(cp.m_localPoint);
const rAX: number = ccp.rA.x = b2ContactSolver.s_worldManifold.m_points[k].x - bodyA.m_sweep.c.x;
const rAY: number = ccp.rA.y = b2ContactSolver.s_worldManifold.m_points[k].y - bodyA.m_sweep.c.y;
const rBX: number = ccp.rB.x = b2ContactSolver.s_worldManifold.m_points[k].x - bodyB.m_sweep.c.x;
const rBY: number = ccp.rB.y = b2ContactSolver.s_worldManifold.m_points[k].y - bodyB.m_sweep.c.y;
let rnA: number = rAX * normalY - rAY * normalX;//b2Math.b2Cross(r1, normal);
let rnB: number = rBX * normalY - rBY * normalX;//b2Math.b2Cross(r2, normal);
rnA *= rnA;
rnB *= rnB;
const kNormal: number = bodyA.m_invMass + bodyB.m_invMass + bodyA.m_invI * rnA + bodyB.m_invI * rnB;
//b2Settings.b2Assert(kNormal > Number.MIN_VALUE);
ccp.normalMass = 1.0 / kNormal;
let kEqualized: number = bodyA.m_mass * bodyA.m_invMass + bodyB.m_mass * bodyB.m_invMass;
kEqualized += bodyA.m_mass * bodyA.m_invI * rnA + bodyB.m_mass * bodyB.m_invI * rnB;
//b2Assert(kEqualized > Number.MIN_VALUE);
ccp.equalizedMass = 1.0 / kEqualized;
//var tangent:b2Vec2 = b2Math.b2CrossVF(normal, 1.0);
const tangentX: number = normalY;
const tangentY: number = -normalX;
//var rtA:number = b2Math.b2Cross(rA, tangent);
let rtA: number = rAX * tangentY - rAY * tangentX;
//var rtB:number = b2Math.b2Cross(rB, tangent);
let rtB: number = rBX * tangentY - rBY * tangentX;
rtA *= rtA;
rtB *= rtB;
const kTangent: number = bodyA.m_invMass + bodyB.m_invMass + bodyA.m_invI * rtA + bodyB.m_invI * rtB;
//b2Settings.b2Assert(kTangent > Number.MIN_VALUE);
ccp.tangentMass = 1.0 / kTangent;
// Setup a velocity bias for restitution.
ccp.velocityBias = 0.0;
//b2Dot(c.normal, vB + b2Cross(wB, rB) - vA - b2Cross(wA, rA));
const tX: number = vBX + (-wB * rBY) - vAX - (-wA * rAY);
const tY: number = vBY + (wB * rBX) - vAY - (wA * rAX);
//var vRel:number = b2Dot(cc.normal, t);
const vRel: number = cc.normal.x * tX + cc.normal.y * tY;
if (vRel < -b2Settings.b2_velocityThreshold) {
ccp.velocityBias += -cc.restitution * vRel;
}
}
// If we have two points, then prepare the block solver.
if (cc.pointCount == 2) {
const ccp1: b2ContactConstraintPoint = cc.points[0];
const ccp2: b2ContactConstraintPoint = cc.points[1];
const invMassA: number = bodyA.m_invMass;
const invIA: number = bodyA.m_invI;
const invMassB: number = bodyB.m_invMass;
const invIB: number = bodyB.m_invI;
//var rn1A:number = b2Cross(ccp1.rA, normal);
//var rn1B:number = b2Cross(ccp1.rB, normal);
//var rn2A:number = b2Cross(ccp2.rA, normal);
//var rn2B:number = b2Cross(ccp2.rB, normal);
const rn1A: number = ccp1.rA.x * normalY - ccp1.rA.y * normalX;
const rn1B: number = ccp1.rB.x * normalY - ccp1.rB.y * normalX;
const rn2A: number = ccp2.rA.x * normalY - ccp2.rA.y * normalX;
const rn2B: number = ccp2.rB.x * normalY - ccp2.rB.y * normalX;
const k11: number = invMassA + invMassB + invIA * rn1A * rn1A + invIB * rn1B * rn1B;
const k22: number = invMassA + invMassB + invIA * rn2A * rn2A + invIB * rn2B * rn2B;
const k12: number = invMassA + invMassB + invIA * rn1A * rn2A + invIB * rn1B * rn2B;
// Ensure a reasonable condition number.
const k_maxConditionNumber: number = 100.0;
if (k11 * k11 < k_maxConditionNumber * (k11 * k22 - k12 * k12)) {
// K is safe to invert.
cc.K.col1.Set(k11, k12);
cc.K.col2.Set(k12, k22);
cc.K.GetInverse(cc.normalMass);
} else {
// The constraints are redundant, just use one.
// TODO_ERIN use deepest?
cc.pointCount = 1;
}
}
}
//b2Settings.b2Assert(count == m_constraintCount);
}
//~b2ContactSolver();
public InitVelocityConstraints(step: b2TimeStep): void {
let tVec: b2Vec2;
let tVec2: b2Vec2;
let tMat: b2Mat22;
// Warm start.
for (let i: number /** int */ = 0; i < this.m_constraintCount; ++i) {
const c: b2ContactConstraint = this.m_constraints[ i ];
const bodyA: b2Body = c.bodyA;
const bodyB: b2Body = c.bodyB;
const invMassA: number = bodyA.m_invMass;
const invIA: number = bodyA.m_invI;
const invMassB: number = bodyB.m_invMass;
const invIB: number = bodyB.m_invI;
//var normal:b2Vec2 = new b2Vec2(c.normal.x, c.normal.y);
const normalX: number = c.normal.x;
const normalY: number = c.normal.y;
//var tangent:b2Vec2 = b2Math.b2CrossVF(normal, 1.0);
const tangentX: number = normalY;
const tangentY: number = -normalX;
var tX: number;
var j: number /** int */;
var tCount: number /** int */;
if (step.warmStarting) {
tCount = c.pointCount;
for (j = 0; j < tCount; ++j) {
const ccp: b2ContactConstraintPoint = c.points[ j ];
ccp.normalImpulse *= step.dtRatio;
ccp.tangentImpulse *= step.dtRatio;
//b2Vec2 P = ccp->normalImpulse * normal + ccp->tangentImpulse * tangent;
const PX: number = ccp.normalImpulse * normalX + ccp.tangentImpulse * tangentX;
const PY: number = ccp.normalImpulse * normalY + ccp.tangentImpulse * tangentY;
//bodyA.m_angularVelocity -= invIA * b2Math.b2CrossVV(rA, P);
bodyA.m_angularVelocity -= invIA * (ccp.rA.x * PY - ccp.rA.y * PX);
//bodyA.m_linearVelocity.Subtract( b2Math.MulFV(invMassA, P) );
bodyA.m_linearVelocity.x -= invMassA * PX;
bodyA.m_linearVelocity.y -= invMassA * PY;
//bodyB.m_angularVelocity += invIB * b2Math.b2CrossVV(rB, P);
bodyB.m_angularVelocity += invIB * (ccp.rB.x * PY - ccp.rB.y * PX);
//bodyB.m_linearVelocity.Add( b2Math.MulFV(invMassB, P) );
bodyB.m_linearVelocity.x += invMassB * PX;
bodyB.m_linearVelocity.y += invMassB * PY;
}
} else {
tCount = c.pointCount;
for (j = 0; j < tCount; ++j) {
const ccp2: b2ContactConstraintPoint = c.points[ j ];
ccp2.normalImpulse = 0.0;
ccp2.tangentImpulse = 0.0;
}
}
}
}
public SolveVelocityConstraints(): void {
let j: number /** int */;
let ccp: b2ContactConstraintPoint;
let rAX: number;
let rAY: number;
let rBX: number;
let rBY: number;
let dvX: number;
let dvY: number;
let vn: number;
let vt: number;
let lambda: number;
let maxFriction: number;
let newImpulse: number;
let PX: number;
let PY: number;
let dX: number;
let dY: number;
let P1X: number;
let P1Y: number;
let P2X: number;
let P2Y: number;
let tMat: b2Mat22;
let tVec: b2Vec2;
for (let i: number /** int */ = 0; i < this.m_constraintCount; ++i) {
const c: b2ContactConstraint = this.m_constraints[ i ];
const bodyA: b2Body = c.bodyA;
const bodyB: b2Body = c.bodyB;
let wA: number = bodyA.m_angularVelocity;
let wB: number = bodyB.m_angularVelocity;
const vA: b2Vec2 = bodyA.m_linearVelocity;
const vB: b2Vec2 = bodyB.m_linearVelocity;
const invMassA: number = bodyA.m_invMass;
const invIA: number = bodyA.m_invI;
const invMassB: number = bodyB.m_invMass;
const invIB: number = bodyB.m_invI;
//var normal:b2Vec2 = new b2Vec2(c.normal.x, c.normal.y);
const normalX: number = c.normal.x;
const normalY: number = c.normal.y;
//var tangent:b2Vec2 = b2Math.b2CrossVF(normal, 1.0);
const tangentX: number = normalY;
const tangentY: number = -normalX;
const friction: number = c.friction;
var tX: number;
//b2Settings.b2Assert(c.pointCount == 1 || c.pointCount == 2);
// Solve the tangent constraints
for (j = 0; j < c.pointCount; j++) {
ccp = c.points[j];
// Relative velocity at contact
//b2Vec2 dv = vB + b2Cross(wB, ccp->rB) - vA - b2Cross(wA, ccp->rA);
dvX = vB.x - wB * ccp.rB.y - vA.x + wA * ccp.rA.y;
dvY = vB.y + wB * ccp.rB.x - vA.y - wA * ccp.rA.x;
// Compute tangent force
vt = dvX * tangentX + dvY * tangentY;
lambda = ccp.tangentMass * -vt;
// b2Clamp the accumulated force
maxFriction = friction * ccp.normalImpulse;
newImpulse = b2Math.Clamp(ccp.tangentImpulse + lambda, -maxFriction, maxFriction);
lambda = newImpulse - ccp.tangentImpulse;
// Apply contact impulse
PX = lambda * tangentX;
PY = lambda * tangentY;
vA.x -= invMassA * PX;
vA.y -= invMassA * PY;
wA -= invIA * (ccp.rA.x * PY - ccp.rA.y * PX);
vB.x += invMassB * PX;
vB.y += invMassB * PY;
wB += invIB * (ccp.rB.x * PY - ccp.rB.y * PX);
ccp.tangentImpulse = newImpulse;
}
// Solve the normal constraints
const tCount: number /** int */ = c.pointCount;
if (c.pointCount == 1) {
ccp = c.points[ 0 ];
// Relative velocity at contact
//b2Vec2 dv = vB + b2Cross(wB, ccp->rB) - vA - b2Cross(wA, ccp->rA);
dvX = vB.x + (-wB * ccp.rB.y) - vA.x - (-wA * ccp.rA.y);
dvY = vB.y + (wB * ccp.rB.x) - vA.y - (wA * ccp.rA.x);
// Compute normal impulse
//var vn:number = b2Math.b2Dot(dv, normal);
vn = dvX * normalX + dvY * normalY;
lambda = -ccp.normalMass * (vn - ccp.velocityBias);
// b2Clamp the accumulated impulse
//newImpulse = b2Math.b2Max(ccp.normalImpulse + lambda, 0.0);
newImpulse = ccp.normalImpulse + lambda;
newImpulse = newImpulse > 0 ? newImpulse : 0.0;
lambda = newImpulse - ccp.normalImpulse;
// Apply contact impulse
//b2Vec2 P = lambda * normal;
PX = lambda * normalX;
PY = lambda * normalY;
//vA.Subtract( b2Math.MulFV( invMassA, P ) );
vA.x -= invMassA * PX;
vA.y -= invMassA * PY;
wA -= invIA * (ccp.rA.x * PY - ccp.rA.y * PX);//invIA * b2Math.b2CrossVV(ccp.rA, P);
//vB.Add( b2Math.MulFV( invMass2, P ) );
vB.x += invMassB * PX;
vB.y += invMassB * PY;
wB += invIB * (ccp.rB.x * PY - ccp.rB.y * PX);//invIB * b2Math.b2CrossVV(ccp.rB, P);
ccp.normalImpulse = newImpulse;
} 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, , 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 = vn_0 - 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 = x' - a
//
// Plug into above equation:
//
// vn = A * x + b
// = A * (x' - a) + b
// = A * x' + b - A * a
// = A * x' + b'
// b' = b - A * a;
const cp1: b2ContactConstraintPoint = c.points[ 0 ];
const cp2: b2ContactConstraintPoint = c.points[ 1 ];
const aX: number = cp1.normalImpulse;
const aY: number = cp2.normalImpulse;
//b2Settings.b2Assert( aX >= 0.0f && aY >= 0.0f );
// Relative velocity at contact
//var dv1:b2Vec2 = vB + b2Cross(wB, cp1.rB) - vA - b2Cross(wA, cp1.rA);
const dv1X: number = vB.x - wB * cp1.rB.y - vA.x + wA * cp1.rA.y;
const dv1Y: number = vB.y + wB * cp1.rB.x - vA.y - wA * cp1.rA.x;
//var dv2:b2Vec2 = vB + b2Cross(wB, cpB.r2) - vA - b2Cross(wA, cp2.rA);
const dv2X: number = vB.x - wB * cp2.rB.y - vA.x + wA * cp2.rA.y;
const dv2Y: number = vB.y + wB * cp2.rB.x - vA.y - wA * cp2.rA.x;
// Compute normal velocity
//var vn1:number = b2Dot(dv1, normal);
let vn1: number = dv1X * normalX + dv1Y * normalY;
//var vn2:number = b2Dot(dv2, normal);
let vn2: number = dv2X * normalX + dv2Y * normalY;
let bX: number = vn1 - cp1.velocityBias;
let bY: number = vn2 - cp2.velocityBias;
//b -= b2Mul(c.K,a);
tMat = c.K;
bX -= tMat.col1.x * aX + tMat.col2.x * aY;
bY -= tMat.col1.y * aX + tMat.col2.y * aY;
const k_errorTol: number = 0.001;
for (;;) {
//
// Case 1: vn = 0
//
// 0 = A * x' + b'
//
// Solve for x':
//
// x' = -inv(A) * b'
//
//var x:b2Vec2 = - b2Mul(c->normalMass, b);
tMat = c.normalMass;
let xX: number = -(tMat.col1.x * bX + tMat.col2.x * bY);
let xY: number = -(tMat.col1.y * bX + tMat.col2.y * bY);
if (xX >= 0.0 && xY >= 0.0) {
// Resubstitute for the incremental impulse
//d = x - a;
dX = xX - aX;
dY = xY - aY;
//Aply incremental impulse
//P1 = d.x * normal;
P1X = dX * normalX;
P1Y = dX * normalY;
//P2 = d.y * normal;
P2X = dY * normalX;
P2Y = dY * normalY;
//vA -= invMass1 * (P1 + P2)
vA.x -= invMassA * (P1X + P2X);
vA.y -= invMassA * (P1Y + P2Y);
//wA -= invIA * (b2Cross(cp1.rA, P1) + b2Cross(cp2.rA, P2));
wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X);
//vB += invMassB * (P1 + P2)
vB.x += invMassB * (P1X + P2X);
vB.y += invMassB * (P1Y + P2Y);
//wB += invIB * (b2Cross(cp1.rB, P1) + b2Cross(cp2.rB, P2));
wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X);
// Accumulate
cp1.normalImpulse = xX;
cp2.normalImpulse = xY;
//#if B2_DEBUG_SOLVER == 1
// // Post conditions
// //dv1 = vB + b2Cross(wB, cp1.rB) - vA - b2Cross(wA, cp1.rA);
// dv1X = vB.x - wB * cp1.rB.y - vA.x + wA * cp1.rA.y;
// dv1Y = vB.y + wB * cp1.rB.x - vA.y - wA * cp1.rA.x;
// //dv2 = vB + b2Cross(wB, cp2.rB) - vA - b2Cross(wA, cp2.rA);
// dv1X = vB.x - wB * cp2.rB.y - vA.x + wA * cp2.rA.y;
// dv1Y = vB.y + wB * cp2.rB.x - vA.y - wA * cp2.rA.x;
// // Compute normal velocity
// //vn1 = b2Dot(dv1, normal);
// vn1 = dv1X * normalX + dv1Y * normalY;
// //vn2 = b2Dot(dv2, normal);
// vn2 = dv2X * normalX + dv2Y * normalY;
//
// //b2Settings.b2Assert(b2Abs(vn1 - cp1.velocityBias) < k_errorTol);
// //b2Settings.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'
//
xX = -cp1.normalMass * bX;
xY = 0.0;
vn1 = 0.0;
vn2 = c.K.col1.y * xX + bY;
if (xX >= 0.0 && vn2 >= 0.0) {
// Resubstitute for the incremental impulse
//d = x - a;
dX = xX - aX;
dY = xY - aY;
//Aply incremental impulse
//P1 = d.x * normal;
P1X = dX * normalX;
P1Y = dX * normalY;
//P2 = d.y * normal;
P2X = dY * normalX;
P2Y = dY * normalY;
//vA -= invMassA * (P1 + P2)
vA.x -= invMassA * (P1X + P2X);
vA.y -= invMassA * (P1Y + P2Y);
//wA -= invIA * (b2Cross(cp1.rA, P1) + b2Cross(cp2.rA, P2));
wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X);
//vB += invMassB * (P1 + P2)
vB.x += invMassB * (P1X + P2X);
vB.y += invMassB * (P1Y + P2Y);
//wB += invIB * (b2Cross(cp1.rB, P1) + b2Cross(cp2.rB, P2));
wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X);
// Accumulate
cp1.normalImpulse = xX;
cp2.normalImpulse = xY;
//#if B2_DEBUG_SOLVER == 1
// // Post conditions
// //dv1 = vB + b2Cross(wB, cp1.rB) - vA - b2Cross(wA, cp1.rA);
// dv1X = vB.x - wB * cp1.rB.y - vA.x + wA * cp1.rA.y;
// dv1Y = vB.y + wB * cp1.rB.x - vA.y - wA * cp1.rA.x;
// //dv2 = vB + b2Cross(wB, cp2.rB) - vA - b2Cross(wA, cp2.rA);
// dv1X = vB.x - wB * cp2.rB.y - vA.x + wA * cp2.rA.y;
// dv1Y = vB.y + wB * cp2.rB.x - vA.y - wA * cp2.rA.x;
// // Compute normal velocity
// //vn1 = b2Dot(dv1, normal);
// vn1 = dv1X * normalX + dv1Y * normalY;
// //vn2 = b2Dot(dv2, normal);
// vn2 = dv2X * normalX + dv2Y * normalY;
//
// //b2Settings.b2Assert(b2Abs(vn1 - cp1.velocityBias) < k_errorTol);
// //b2Settings.b2Assert(b2Abs(vn2 - cp2.velocityBias) < k_errorTol);
//#endif
break;
}
//
// Case 3: wB = 0 and x1 = 0
//
// vn1 = a11 * 0 + a12 * x2' + b1'
// 0 = a21 * 0 + a22 * x2' + b2'
//
xX = 0.0;
xY = -cp2.normalMass * bY;
vn1 = c.K.col2.x * xY + bX;
vn2 = 0.0;
if (xY >= 0.0 && vn1 >= 0.0) {
// Resubstitute for the incremental impulse
//d = x - a;
dX = xX - aX;
dY = xY - aY;
//Aply incremental impulse
//P1 = d.x * normal;
P1X = dX * normalX;
P1Y = dX * normalY;
//P2 = d.y * normal;
P2X = dY * normalX;
P2Y = dY * normalY;
//vA -= invMassA * (P1 + P2)
vA.x -= invMassA * (P1X + P2X);
vA.y -= invMassA * (P1Y + P2Y);
//wA -= invIA * (b2Cross(cp1.rA, P1) + b2Cross(cp2.rA, P2));
wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X);
//vB += invMassB * (P1 + P2)
vB.x += invMassB * (P1X + P2X);
vB.y += invMassB * (P1Y + P2Y);
//wB += invIB * (b2Cross(cp1.rB, P1) + b2Cross(cp2.rB, P2));
wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X);
// Accumulate
cp1.normalImpulse = xX;
cp2.normalImpulse = xY;
//#if B2_DEBUG_SOLVER == 1
// // Post conditions
// //dv1 = vB + b2Cross(wB, cp1.rB) - vA - b2Cross(wA, cp1.rA);
// dv1X = vB.x - wB * cp1.rB.y - vA.x + wA * cp1.rA.y;
// dv1Y = vB.y + wB * cp1.rB.x - vA.y - wA * cp1.rA.x;
// //dv2 = vB + b2Cross(wB, cp2.rB) - vA - b2Cross(wA, cp2.rA);
// dv1X = vB.x - wB * cp2.rB.y - vA.x + wA * cp2.rA.y;
// dv1Y = vB.y + wB * cp2.rB.x - vA.y - wA * cp2.rA.x;
// // Compute normal velocity
// //vn1 = b2Dot(dv1, normal);
// vn1 = dv1X * normalX + dv1Y * normalY;
// //vn2 = b2Dot(dv2, normal);
// vn2 = dv2X * normalX + dv2Y * normalY;
//
// //b2Settings.b2Assert(b2Abs(vn1 - cp1.velocityBias) < k_errorTol);
// //b2Settings.b2Assert(b2Abs(vn2 - cp2.velocityBias) < k_errorTol);
//#endif
break;
}
//
// Case 4: x1 = 0 and x2 = 0
//
// vn1 = b1
// vn2 = b2
xX = 0.0;
xY = 0.0;
vn1 = bX;
vn2 = bY;
if (vn1 >= 0.0 && vn2 >= 0.0) {
// Resubstitute for the incremental impulse
//d = x - a;
dX = xX - aX;
dY = xY - aY;
//Aply incremental impulse
//P1 = d.x * normal;
P1X = dX * normalX;
P1Y = dX * normalY;
//P2 = d.y * normal;
P2X = dY * normalX;
P2Y = dY * normalY;
//vA -= invMassA * (P1 + P2)
vA.x -= invMassA * (P1X + P2X);
vA.y -= invMassA * (P1Y + P2Y);
//wA -= invIA * (b2Cross(cp1.rA, P1) + b2Cross(cp2.rA, P2));
wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X);
//vB += invMassB * (P1 + P2)
vB.x += invMassB * (P1X + P2X);
vB.y += invMassB * (P1Y + P2Y);
//wB += invIB * (b2Cross(cp1.rB, P1) + b2Cross(cp2.rB, P2));
wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X);
// Accumulate
cp1.normalImpulse = xX;
cp2.normalImpulse = xY;
//#if B2_DEBUG_SOLVER == 1
// // Post conditions
// //dv1 = vB + b2Cross(wB, cp1.rB) - vA - b2Cross(wA, cp1.rA);
// dv1X = vB.x - wB * cp1.rB.y - vA.x + wA * cp1.rA.y;
// dv1Y = vB.y + wB * cp1.rB.x - vA.y - wA * cp1.rA.x;
// //dv2 = vB + b2Cross(wB, cp2.rB) - vA - b2Cross(wA, cp2.rA);
// dv1X = vB.x - wB * cp2.rB.y - vA.x + wA * cp2.rA.y;
// dv1Y = vB.y + wB * cp2.rB.x - vA.y - wA * cp2.rA.x;
// // Compute normal velocity
// //vn1 = b2Dot(dv1, normal);
// vn1 = dv1X * normalX + dv1Y * normalY;
// //vn2 = b2Dot(dv2, normal);
// vn2 = dv2X * normalX + dv2Y * normalY;
//
// //b2Settings.b2Assert(b2Abs(vn1 - cp1.velocityBias) < k_errorTol);
// //b2Settings.b2Assert(b2Abs(vn2 - cp2.velocityBias) < k_errorTol);
//#endif
break;
}
// No solution, give up. This is hit sometimes, but it doesn't seem to matter.
break;
}
}
// b2Vec2s in AS3 are copied by reference. The originals are
// references to the same things here and there is no need to
// copy them back, unlike in C++ land where b2Vec2s are
// copied by value.
/*bodyA->m_linearVelocity = vA;
bodyB->m_linearVelocity = vB;*/
bodyA.m_angularVelocity = wA;
bodyB.m_angularVelocity = wB;
}
}
public FinalizeVelocityConstraints(): void {
for (let i: number /** int */ = 0; i < this.m_constraintCount; ++i) {
const c: b2ContactConstraint = this.m_constraints[ i ];
const m: b2Manifold = c.manifold;
for (let j: number /** int */ = 0; j < c.pointCount; ++j) {
const point1: b2ManifoldPoint = m.m_points[j];
const point2: b2ContactConstraintPoint = c.points[j];
point1.m_normalImpulse = point2.normalImpulse;
point1.m_tangentImpulse = point2.tangentImpulse;
}
}
}
//#if 1
// Sequential solver
// public SolvePositionConstraints(baumgarte:number):boolean{
// var minSeparation:number = 0.0;
//
// var tMat:b2Mat22;
// var tVec:b2Vec2;
//
// for (var i:number /** int */ = 0; i < m_constraintCount; ++i)
// {
// var c:b2ContactConstraint = m_constraints[ i ];
// var bodyA:b2Body = c.bodyA;
// var bodyB:b2Body = c.bodyB;
// var bA_sweep_c:b2Vec2 = bodyA.m_sweep.c;
// var bA_sweep_a:number = bodyA.m_sweep.a;
// var bB_sweep_c:b2Vec2 = bodyB.m_sweep.c;
// var bB_sweep_a:number = bodyB.m_sweep.a;
//
// var invMassa:number = bodyA.m_mass * bodyA.m_invMass;
// var invIa:number = bodyA.m_mass * bodyA.m_invI;
// var invMassb:number = bodyB.m_mass * bodyB.m_invMass;
// var invIb:number = bodyB.m_mass * bodyB.m_invI;
// //var normal:b2Vec2 = new b2Vec2(c.normal.x, c.normal.y);
// var normalX:number = c.normal.x;
// var normalY:number = c.normal.y;
//
// // Solver normal constraints
// var tCount:number /** int */ = c.pointCount;
// for (var j:number /** int */ = 0; j < tCount; ++j)
// {
// var ccp:b2ContactConstraintPoint = c.points[ j ];
//
// //r1 = b2Mul(bodyA->m_xf.R, ccp->localAnchor1 - bodyA->GetLocalCenter());
// tMat = bodyA.m_xf.R;
// tVec = bodyA.m_sweep.localCenter;
// var r1X:number = ccp.localAnchor1.x - tVec.x;
// var r1Y:number = ccp.localAnchor1.y - tVec.y;
// tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y);
// r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y);
// r1X = tX;
//
// //r2 = b2Mul(bodyB->m_xf.R, ccp->localAnchor2 - bodyB->GetLocalCenter());
// tMat = bodyB.m_xf.R;
// tVec = bodyB.m_sweep.localCenter;
// var r2X:number = ccp.localAnchor2.x - tVec.x;
// var r2Y:number = ccp.localAnchor2.y - tVec.y;
// var tX:number = (tMat.col1.x * r2X + tMat.col2.x * r2Y);
// r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y);
// r2X = tX;
//
// //b2Vec2 p1 = bodyA->m_sweep.c + r1;
// var p1X:number = b1_sweep_c.x + r1X;
// var p1Y:number = b1_sweep_c.y + r1Y;
//
// //b2Vec2 p2 = bodyB->m_sweep.c + r2;
// var p2X:number = b2_sweep_c.x + r2X;
// var p2Y:number = b2_sweep_c.y + r2Y;
//
// //var dp:b2Vec2 = b2Math.SubtractVV(p2, p1);
// var dpX:number = p2X - p1X;
// var dpY:number = p2Y - p1Y;
//
// // Approximate the current separation.
// //var separation:number = b2Math.b2Dot(dp, normal) + ccp.separation;
// var separation:number = (dpX*normalX + dpY*normalY) + ccp.separation;
//
// // Track max constraint error.
// minSeparation = b2Math.b2Min(minSeparation, separation);
//
// // Prevent large corrections and allow slop.
// var C:number = b2Math.b2Clamp(baumgarte * (separation + b2Settings.b2_linearSlop), -b2Settings.b2_maxLinearCorrection, 0.0);
//
// // Compute normal impulse
// var dImpulse:number = -ccp.equalizedMass * C;
//
// //var P:b2Vec2 = b2Math.MulFV( dImpulse, normal );
// var PX:number = dImpulse * normalX;
// var PY:number = dImpulse * normalY;
//
// //bodyA.m_position.Subtract( b2Math.MulFV( invMass1, impulse ) );
// b1_sweep_c.x -= invMass1 * PX;
// b1_sweep_c.y -= invMass1 * PY;
// b1_sweep_a -= invI1 * (r1X * PY - r1Y * PX);//b2Math.b2CrossVV(r1, P);
// bodyA.m_sweep.a = b1_sweep_a;
// bodyA.SynchronizeTransform();
//
// //bodyB.m_position.Add( b2Math.MulFV( invMass2, P ) );
// b2_sweep_c.x += invMass2 * PX;
// b2_sweep_c.y += invMass2 * PY;
// b2_sweep_a += invI2 * (r2X * PY - r2Y * PX);//b2Math.b2CrossVV(r2, P);
// bodyB.m_sweep.a = b2_sweep_a;
// bodyB.SynchronizeTransform();
// }
// // Update body rotations
// //bodyA.m_sweep.a = b1_sweep_a;
// //bodyB.m_sweep.a = b2_sweep_a;
// }
//
// // We can't expect minSpeparation >= -b2_linearSlop because we don't
// // push the separation above -b2_linearSlop.
// return minSeparation >= -1.5 * b2Settings.b2_linearSlop;
// }
//#else
// Sequential solver.
private static s_psm: b2PositionSolverManifold = new b2PositionSolverManifold();
public SolvePositionConstraints(baumgarte: number): boolean {
let minSeparation: number = 0.0;
for (let i: number /** int */ = 0; i < this.m_constraintCount; i++) {
const c: b2ContactConstraint = this.m_constraints[i];
const bodyA: b2Body = c.bodyA;
const bodyB: b2Body = c.bodyB;
const invMassA: number = bodyA.m_mass * bodyA.m_invMass;
const invIA: number = bodyA.m_mass * bodyA.m_invI;
const invMassB: number = bodyB.m_mass * bodyB.m_invMass;
const invIB: number = bodyB.m_mass * bodyB.m_invI;
b2ContactSolver.s_psm.Initialize(c);
const normal: b2Vec2 = b2ContactSolver.s_psm.m_normal;
// Solve normal constraints
for (let j: number /** int */ = 0; j < c.pointCount; j++) {
const ccp: b2ContactConstraintPoint = c.points[j];
const point: b2Vec2 = b2ContactSolver.s_psm.m_points[j];
const separation: number = b2ContactSolver.s_psm.m_separations[j];
const rAX: number = point.x - bodyA.m_sweep.c.x;
const rAY: number = point.y - bodyA.m_sweep.c.y;
const rBX: number = point.x - bodyB.m_sweep.c.x;
const rBY: number = point.y - bodyB.m_sweep.c.y;
// Track max constraint error.
minSeparation = minSeparation < separation ? minSeparation : separation;
// Prevent large corrections and allow slop.
const C: number = b2Math.Clamp(baumgarte * (separation + b2Settings.b2_linearSlop), -b2Settings.b2_maxLinearCorrection, 0.0);
// Compute normal impulse
const impulse: number = -ccp.equalizedMass * C;
const PX: number = impulse * normal.x;
const PY: number = impulse * normal.y;
//bodyA.m_sweep.c -= invMassA * P;
bodyA.m_sweep.c.x -= invMassA * PX;
bodyA.m_sweep.c.y -= invMassA * PY;
//bodyA.m_sweep.a -= invIA * b2Cross(rA, P);
bodyA.m_sweep.a -= invIA * (rAX * PY - rAY * PX);
bodyA.SynchronizeTransform();
//bodyB.m_sweep.c += invMassB * P;
bodyB.m_sweep.c.x += invMassB * PX;
bodyB.m_sweep.c.y += invMassB * PY;
//bodyB.m_sweep.a += invIB * b2Cross(rB, P);
bodyB.m_sweep.a += invIB * (rBX * PY - rBY * PX);
bodyB.SynchronizeTransform();
}
}
// We can't expect minSpeparation >= -b2_linearSlop because we don't
// push the separation above -b2_linearSlop.
return minSeparation > -1.5 * b2Settings.b2_linearSlop;
}
//#endif
private m_step: b2TimeStep = new b2TimeStep();
private m_allocator: any;
public m_constraints: Array<b2ContactConstraint> = new Array<b2ContactConstraint> ();
private m_constraintCount: number /** int */;
}