planck-js
Version:
2D JavaScript/TypeScript physics engine for cross-platform HTML5 game development
165 lines (131 loc) • 5.33 kB
text/typescript
/*
* Planck.js
*
* Copyright (c) Erin Catto, Ali Shakiba
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { TransformValue } from "../../common/Transform";
import * as matrix from "../../common/Matrix";
import { Contact } from "../../dynamics/Contact";
import { EdgeShape } from "./EdgeShape";
import { ChainShape } from "./ChainShape";
import { CircleShape } from "./CircleShape";
import { Manifold, ContactFeatureType, ManifoldType } from "../Manifold";
import { Fixture } from "../../dynamics/Fixture";
/** @internal */ const _ASSERT = typeof ASSERT === "undefined" ? false : ASSERT;
Contact.addType(EdgeShape.TYPE, CircleShape.TYPE, EdgeCircleContact);
Contact.addType(ChainShape.TYPE, CircleShape.TYPE, ChainCircleContact);
/** @internal */ function EdgeCircleContact(manifold: Manifold, xfA: TransformValue, fixtureA: Fixture, indexA: number, xfB: TransformValue, fixtureB: Fixture, indexB: number): void {
if (_ASSERT) console.assert(fixtureA.getType() == EdgeShape.TYPE);
if (_ASSERT) console.assert(fixtureB.getType() == CircleShape.TYPE);
const shapeA = fixtureA.getShape() as EdgeShape;
const shapeB = fixtureB.getShape() as CircleShape;
CollideEdgeCircle(manifold, shapeA, xfA, shapeB, xfB);
}
function ChainCircleContact(manifold: Manifold, xfA: TransformValue, fixtureA: Fixture, indexA: number, xfB: TransformValue, fixtureB: Fixture, indexB: number): void {
if (_ASSERT) console.assert(fixtureA.getType() == ChainShape.TYPE);
if (_ASSERT) console.assert(fixtureB.getType() == CircleShape.TYPE);
const chain = fixtureA.getShape() as ChainShape;
const edge = new EdgeShape();
chain.getChildEdge(edge, indexA);
const shapeA = edge;
const shapeB = fixtureB.getShape() as CircleShape;
CollideEdgeCircle(manifold, shapeA, xfA, shapeB, xfB);
}
/** @internal */ const e = matrix.vec2(0, 0);
/** @internal */ const temp = matrix.vec2(0, 0);
/** @internal */ const e1 = matrix.vec2(0, 0);
/** @internal */ const e2 = matrix.vec2(0, 0);
/** @internal */ const Q = matrix.vec2(0, 0);
/** @internal */ const P = matrix.vec2(0, 0);
/** @internal */ const n = matrix.vec2(0, 0);
// Compute contact points for edge versus circle.
// This accounts for edge connectivity.
export const CollideEdgeCircle = function (manifold: Manifold, edgeA: EdgeShape, xfA: TransformValue, circleB: CircleShape, xfB: TransformValue): void {
manifold.pointCount = 0;
// Compute circle in frame of edge
matrix.retransformVec2(Q, xfB, xfA, circleB.m_p);
const A = edgeA.m_vertex1;
const B = edgeA.m_vertex2;
matrix.subVec2(e, B, A);
// Barycentric coordinates
const u = matrix.dotVec2(e, B) - matrix.dotVec2(e, Q);
const v = matrix.dotVec2(e, Q) - matrix.dotVec2(e, A);
const radius = edgeA.m_radius + circleB.m_radius;
// Region A
if (v <= 0.0) {
matrix.copyVec2(P, A);
const dd = matrix.distSqrVec2(Q, A);
if (dd > radius * radius) {
return;
}
// Is there an edge connected to A?
if (edgeA.m_hasVertex0) {
const A1 = edgeA.m_vertex0;
const B1 = A;
matrix.subVec2(e1, B1, A1);
const u1 = matrix.dotVec2(e1, B1) - matrix.dotVec2(e1, Q);
// Is the circle in Region AB of the previous edge?
if (u1 > 0.0) {
return;
}
}
manifold.type = ManifoldType.e_circles;
matrix.zeroVec2(manifold.localNormal);
matrix.copyVec2(manifold.localPoint, P);
manifold.pointCount = 1;
matrix.copyVec2(manifold.points[0].localPoint, circleB.m_p);
// manifold.points[0].id.key = 0;
manifold.points[0].id.setFeatures(0, ContactFeatureType.e_vertex, 0, ContactFeatureType.e_vertex);
return;
}
// Region B
if (u <= 0.0) {
matrix.copyVec2(P, B);
const dd = matrix.distSqrVec2(Q, P);
if (dd > radius * radius) {
return;
}
// Is there an edge connected to B?
if (edgeA.m_hasVertex3) {
const B2 = edgeA.m_vertex3;
const A2 = B;
matrix.subVec2(e2, B2, A2);
const v2 = matrix.dotVec2(e2, Q) - matrix.dotVec2(e2, A2);
// Is the circle in Region AB of the next edge?
if (v2 > 0.0) {
return;
}
}
manifold.type = ManifoldType.e_circles;
matrix.zeroVec2(manifold.localNormal);
matrix.copyVec2(manifold.localPoint, P);
manifold.pointCount = 1;
matrix.copyVec2(manifold.points[0].localPoint, circleB.m_p);
// manifold.points[0].id.key = 0;
manifold.points[0].id.setFeatures(1, ContactFeatureType.e_vertex, 0, ContactFeatureType.e_vertex);
return;
}
// Region AB
const den = matrix.lengthSqrVec2(e);
if (_ASSERT) console.assert(den > 0.0);
matrix.combine2Vec2(P, u / den, A, v / den, B);
const dd = matrix.distSqrVec2(Q, P);
if (dd > radius * radius) {
return;
}
matrix.crossNumVec2(n, 1, e);
if (matrix.dotVec2(n, Q) - matrix.dotVec2(n, A) < 0.0) {
matrix.negVec2(n);
}
matrix.normalizeVec2(n);
manifold.type = ManifoldType.e_faceA;
matrix.copyVec2(manifold.localNormal, n);
matrix.copyVec2(manifold.localPoint, A);
manifold.pointCount = 1;
matrix.copyVec2(manifold.points[0].localPoint, circleB.m_p);
// manifold.points[0].id.key = 0;
manifold.points[0].id.setFeatures(0, ContactFeatureType.e_face, 0, ContactFeatureType.e_vertex);
};