UNPKG

@box2d/debug-draw

Version:

Debug drawing helper for @box2d

369 lines (368 loc) 13.2 kB
"use strict"; // MIT License Object.defineProperty(exports, "__esModule", { value: true }); exports.b2Contact = exports.b2ContactEdge = exports.b2MixRestitutionThreshold = exports.b2MixRestitution = exports.b2MixFriction = void 0; // Copyright (c) 2019 Erin Catto // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. const b2_common_1 = require("../common/b2_common"); const b2_collision_1 = require("../collision/b2_collision"); /** * 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 Math.sqrt(friction1 * friction2); } exports.b2MixFriction = b2MixFriction; /** * 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; } exports.b2MixRestitution = b2MixRestitution; /** * Restitution mixing law. This picks the lowest value. */ function b2MixRestitutionThreshold(threshold1, threshold2) { return threshold1 < threshold2 ? threshold1 : threshold2; } exports.b2MixRestitutionThreshold = b2MixRestitutionThreshold; /** * A contact edge is used to connect bodies and contacts together * in a contact graph where each body is a node and each contact * is an edge. A contact edge belongs to a doubly linked list * maintained in each attached body. Each contact has two contact * nodes, one for each attached body. */ class b2ContactEdge { get other() { (0, b2_common_1.b2Assert)(this.m_other !== null); return this.m_other; } set other(value) { (0, b2_common_1.b2Assert)(this.m_other === null); this.m_other = value; } constructor(contact) { /** Provides quick access to the other body attached. */ this.m_other = null; /** The previous contact edge in the body's contact list */ this.prev = null; /** The next contact edge in the body's contact list */ this.next = null; this.contact = contact; } Reset() { this.m_other = null; this.prev = null; this.next = null; } } exports.b2ContactEdge = b2ContactEdge; /** * The class manages contact between two shapes. A contact exists for each overlapping * AABB in the broad-phase (except if filtered). Therefore a contact object may exist * that has no contact points. */ class b2Contact { constructor() { /** * Used when crawling contact graph when forming islands. * * @internal protected */ this.m_islandFlag = false; /** * Set when the shapes are touching. * * @internal protected */ this.m_touchingFlag = false; /** * This contact can be disabled (by user) * * @internal protected */ this.m_enabledFlag = false; /** * This contact needs filtering because a fixture filter was changed. * * @internal protected */ this.m_filterFlag = false; /** * This bullet contact had a TOI event * * @internal protected */ this.m_bulletHitFlag = false; /** * This contact has a valid TOI in m_toi * * @internal protected */ this.m_toiFlag = false; /** * World pool and list pointers. * * @internal protected */ this.m_prev = null; /** @internal protected */ this.m_next = null; /** * Nodes for connecting bodies. * * @internal protected */ this.m_nodeA = new b2ContactEdge(this); /** @internal protected */ this.m_nodeB = new b2ContactEdge(this); /** @internal protected */ this.m_indexA = 0; /** @internal protected */ this.m_indexB = 0; /** @internal protected */ this.m_manifold = new b2_collision_1.b2Manifold(); // TODO: readonly /** @internal protected */ this.m_toiCount = 0; /** @internal protected */ this.m_toi = 0; /** @internal protected */ this.m_friction = 0; /** @internal protected */ this.m_restitution = 0; /** @internal protected */ this.m_restitutionThreshold = 0; /** @internal protected */ this.m_tangentSpeed = 0; this.m_oldManifold = new b2_collision_1.b2Manifold(); // TODO: readonly } /** * Get the contact manifold. * Do not modify the manifold unless you understand the internals of Box2D. */ GetManifold() { return this.m_manifold; } /** Get the world manifold. */ GetWorldManifold(worldManifold) { const bodyA = this.m_fixtureA.GetBody(); const bodyB = this.m_fixtureB.GetBody(); const shapeA = this.GetShapeA(); const shapeB = this.GetShapeB(); worldManifold.Initialize(this.m_manifold, bodyA.GetTransform(), shapeA.m_radius, bodyB.GetTransform(), shapeB.m_radius); } /** Is this contact touching? */ IsTouching() { return this.m_touchingFlag; } /** * Enable/disable this contact. This can be used inside the pre-solve * contact listener. The contact is only disabled for the current * time step (or sub-step in continuous collisions). */ SetEnabled(flag) { this.m_enabledFlag = flag; } /** Has this contact been disabled? */ IsEnabled() { return this.m_enabledFlag; } /** Get the next contact in the world's contact list. */ GetNext() { return this.m_next; } /** Get fixture A in this contact. */ GetFixtureA() { return this.m_fixtureA; } /** Get the child primitive index for fixture A. */ GetChildIndexA() { return this.m_indexA; } /** Get fixture A in this contact. */ GetShapeA() { return this.m_fixtureA.GetShape(); } /** Get fixture B in this contact. */ GetFixtureB() { return this.m_fixtureB; } /** Get the child primitive index for fixture B. */ GetChildIndexB() { return this.m_indexB; } GetShapeB() { return this.m_fixtureB.GetShape(); } /** * Flag this contact for filtering. Filtering will occur the next time step. * * @internal protected */ FlagForFiltering() { this.m_filterFlag = true; } /** * Override the default friction mixture. * You can call this in b2ContactListener::PreSolve. * This value persists until set or reset. */ SetFriction(friction) { this.m_friction = friction; } /** Get the friction. */ GetFriction() { return this.m_friction; } /** Reset the friction mixture to the default value. */ ResetFriction() { this.m_friction = b2MixFriction(this.m_fixtureA.m_friction, this.m_fixtureB.m_friction); } /** * Override the default restitution mixture. * You can call this in b2ContactListener::PreSolve. * The value persists until you set or reset. */ SetRestitution(restitution) { this.m_restitution = restitution; } /** Get the restitution. */ GetRestitution() { return this.m_restitution; } /** Reset the restitution to the default value. */ ResetRestitution() { this.m_restitution = b2MixRestitution(this.m_fixtureA.m_restitution, this.m_fixtureB.m_restitution); } /** * Override the default restitution velocity threshold mixture. You can call this in b2ContactListener::PreSolve. * The value persists until you set or reset. */ SetRestitutionThreshold(threshold) { this.m_restitutionThreshold = threshold; } /** * Get the restitution threshold. */ GetRestitutionThreshold() { return this.m_restitutionThreshold; } /** * Reset the restitution threshold to the default value. */ ResetRestitutionThreshold() { this.m_restitutionThreshold = b2MixRestitutionThreshold(this.m_fixtureA.m_restitutionThreshold, this.m_fixtureB.m_restitutionThreshold); } /** Set the desired tangent speed for a conveyor belt behavior. In meters per second. */ SetTangentSpeed(speed) { this.m_tangentSpeed = speed; } /** Get the desired tangent speed. In meters per second. */ GetTangentSpeed() { return this.m_tangentSpeed; } Reset(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; this.m_nodeA.Reset(); this.m_nodeB.Reset(); 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); this.m_restitutionThreshold = b2MixRestitutionThreshold(this.m_fixtureA.m_restitutionThreshold, this.m_fixtureB.m_restitutionThreshold); } /** * Update the contact manifold and touching status. * Note: do not assume the fixture AABBs are overlapping or are valid. * * @internal protected */ Update(listener) { const tManifold = this.m_oldManifold; this.m_oldManifold = this.m_manifold; this.m_manifold = tManifold; // Re-enable this contact. this.m_enabledFlag = true; let touching = false; const wasTouching = this.m_touchingFlag; const sensorA = this.m_fixtureA.IsSensor(); const sensorB = this.m_fixtureB.IsSensor(); const sensor = sensorA || sensorB; const bodyA = this.m_fixtureA.GetBody(); const bodyB = this.m_fixtureB.GetBody(); const xfA = bodyA.GetTransform(); const xfB = bodyB.GetTransform(); // Is this contact a sensor? if (sensor) { const shapeA = this.GetShapeA(); const shapeB = this.GetShapeB(); touching = (0, b2_collision_1.b2TestOverlap)(shapeA, this.m_indexA, shapeB, this.m_indexB, xfA, xfB); // Sensors don't generate manifolds. this.m_manifold.pointCount = 0; } else { 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 (let i = 0; i < this.m_manifold.pointCount; ++i) { const mp2 = this.m_manifold.points[i]; mp2.normalImpulse = 0; mp2.tangentImpulse = 0; const id2 = mp2.id; for (let j = 0; j < this.m_oldManifold.pointCount; ++j) { const mp1 = this.m_oldManifold.points[j]; if (mp1.id.key === id2.key) { mp2.normalImpulse = mp1.normalImpulse; mp2.tangentImpulse = mp1.tangentImpulse; break; } } } 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); } } } exports.b2Contact = b2Contact;