@box2d/debug-draw
Version:
Debug drawing helper for @box2d
221 lines (220 loc) • 9.23 kB
JavaScript
"use strict";
// MIT License
Object.defineProperty(exports, "__esModule", { value: true });
exports.b2ContactManager = 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.
// DEBUG: import { b2Assert } from "../common/b2_common";
const b2_broad_phase_1 = require("../collision/b2_broad_phase");
const b2_contact_factory_1 = require("./b2_contact_factory");
const b2_body_1 = require("./b2_body");
const b2_world_callbacks_1 = require("./b2_world_callbacks");
/** Delegate of b2World. */
class b2ContactManager {
constructor() {
this.m_broadPhase = new b2_broad_phase_1.b2BroadPhase();
this.m_contactList = null;
this.m_contactCount = 0;
this.m_contactFilter = b2_world_callbacks_1.b2ContactFilter.b2_defaultFilter;
this.m_contactListener = b2_world_callbacks_1.b2ContactListener.b2_defaultListener;
this.m_contactFactory = new b2_contact_factory_1.b2ContactFactory();
/** Broad-phase callback. */
this.AddPair = (proxyA, proxyB) => {
// DEBUG: b2Assert(proxyA instanceof b2FixtureProxy);
// DEBUG: b2Assert(proxyB instanceof b2FixtureProxy);
let fixtureA = proxyA.fixture;
let fixtureB = proxyB.fixture;
let indexA = proxyA.childIndex;
let indexB = proxyB.childIndex;
let bodyA = fixtureA.GetBody();
let 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?
let edge = bodyB.GetContactList();
while (edge) {
if (edge.other === bodyA) {
const fA = edge.contact.GetFixtureA();
const fB = edge.contact.GetFixtureB();
const iA = edge.contact.GetChildIndexA();
const 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;
}
// Does a joint override collision? Is at least one body dynamic?
if (bodyB.ShouldCollide(bodyA) === false) {
return;
}
// Check user filtering.
if (this.m_contactFilter && !this.m_contactFilter.ShouldCollide(fixtureA, fixtureB)) {
return;
}
// Call the factory.
const 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.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.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;
++this.m_contactCount;
};
}
FindNewContacts() {
this.m_broadPhase.UpdatePairs(this.AddPair);
}
Destroy(c) {
const fixtureA = c.GetFixtureA();
const fixtureB = c.GetFixtureB();
const bodyA = fixtureA.GetBody();
const 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;
}
// moved this from b2ContactFactory:Destroy
if (c.m_manifold.pointCount > 0 && !fixtureA.IsSensor() && !fixtureB.IsSensor()) {
fixtureA.GetBody().SetAwake(true);
fixtureB.GetBody().SetAwake(true);
}
// 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.
*/
Collide() {
// Update awake contacts.
let c = this.m_contactList;
while (c) {
const fixtureA = c.GetFixtureA();
const fixtureB = c.GetFixtureB();
const indexA = c.GetChildIndexA();
const indexB = c.GetChildIndexB();
const bodyA = fixtureA.GetBody();
const bodyB = fixtureB.GetBody();
// Is this contact flagged for filtering?
if (c.m_filterFlag) {
if (
// Should these bodies collide?
!bodyB.ShouldCollide(bodyA) ||
// Check user filtering.
(this.m_contactFilter && !this.m_contactFilter.ShouldCollide(fixtureA, fixtureB))) {
const cNuke = c;
c = cNuke.m_next;
this.Destroy(cNuke);
continue;
}
// Clear the filtering flag.
c.m_filterFlag = false;
}
const activeA = bodyA.IsAwake() && bodyA.m_type !== b2_body_1.b2BodyType.b2_staticBody;
const activeB = bodyB.IsAwake() && bodyB.m_type !== b2_body_1.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;
}
const treeNodeA = fixtureA.m_proxies[indexA].treeNode;
const treeNodeB = fixtureB.m_proxies[indexB].treeNode;
const overlap = treeNodeA.aabb.TestOverlap(treeNodeB.aabb);
// Here we destroy contacts that cease to overlap in the broad-phase.
if (!overlap) {
const cNuke = c;
c = cNuke.m_next;
this.Destroy(cNuke);
continue;
}
// The contact persists.
c.Update(this.m_contactListener);
c = c.m_next;
}
}
}
exports.b2ContactManager = b2ContactManager;