/*
* JBox2D - A Java Port of Erin Catto's Box2D
*
* JBox2D homepage: http://jbox2d.sourceforge.net/
* Box2D homepage: 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.
*/
package org.jbox2d.collision.shapes;
import org.jbox2d.collision.Collision;
import org.jbox2d.collision.Manifold;
import org.jbox2d.common.Mat22;
import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Settings;
import org.jbox2d.common.Vec2;
import org.jbox2d.common.XForm;
//Updated to rev 139 of b2CollideCircle.cpp
/**
* Circle/circle and circle/polygon overlap solver -
* for internal use only.
*/
public class CollideCircle {
// djm pooled
private final Vec2 colCCP1 = new Vec2();
private final Vec2 colCCP2 = new Vec2();
private final Vec2 colCCD = new Vec2();
private final Vec2 colCCP = new Vec2();
/**
* puts collision information of the two circles in the manifold
* @param manifold
* @param circle1
* @param xf1
* @param circle2
* @param xf2
*/
// djm optimized
public final void collideCircles(final Manifold manifold,
final CircleShape circle1, final XForm xf1,
final CircleShape circle2, final XForm xf2) {
manifold.pointCount = 0;
XForm.mulToOut(xf1, circle1.getMemberLocalPosition(), colCCP1);
XForm.mulToOut(xf2, circle2.getMemberLocalPosition(), colCCP2);
colCCD.x = colCCP2.x - colCCP1.x;
colCCD.y = colCCP2.y - colCCP1.y;
final float distSqr = Vec2.dot(colCCD, colCCD);
final float r1 = circle1.getRadius();
final float r2 = circle2.getRadius();
final float radiusSum = r1+r2;
if (distSqr > radiusSum * radiusSum) {
return;
}
float separation;
if (distSqr < Settings.EPSILON) {
separation = -radiusSum;
manifold.normal.set(0.0f, 1.0f);
}
else {
final float dist = MathUtils.sqrt(distSqr);
separation = dist - radiusSum;
final float a = 1.0f / dist;
manifold.normal.x = a * colCCD.x;
manifold.normal.y = a * colCCD.y;
}
manifold.pointCount = 1;
//manifold.points[0].id.key = 0;
manifold.points[0].id.zero(); //use this instead of zeroing through key
manifold.points[0].separation = separation;
// let this one slide
colCCP1.addLocal(manifold.normal.mul(r1));
colCCP2.subLocal(manifold.normal.mul(r2));
colCCP.x = 0.5f * (colCCP1.x + colCCP2.x);
colCCP.y = 0.5f * (colCCP1.y + colCCP2.y);
XForm.mulTransToOut(xf1, colCCP, manifold.points[0].localPoint1);
XForm.mulTransToOut(xf2, colCCP, manifold.points[0].localPoint2);
}
// djm pooled
private final Vec2 colPCP1 = new Vec2();
private final Vec2 colPCP2 = new Vec2();
private final Vec2 colPCD = new Vec2();
private final Vec2 colPCP = new Vec2();
/**
* Puts collision information in the manifold about a collision between a point and a circle
* @param manifold
* @param point1
* @param xf1
* @param circle2
* @param xf2
*/
// djm optimized
public final void collidePointAndCircle(final Manifold manifold,
final PointShape point1, final XForm xf1,
final CircleShape circle2, final XForm xf2) {
manifold.pointCount = 0;
XForm.mulToOut(xf1, point1.getMemberLocalPosition(), colPCP1);
XForm.mulToOut(xf2, circle2.getMemberLocalPosition(), colPCP2);
colPCD.x = colPCP2.x - colPCP1.x;
colPCD.y = colPCP2.y - colPCP1.y;
final float distSqr = Vec2.dot(colPCD, colPCD);
final float r2 = circle2.getRadius();
if (distSqr > r2*r2) {
return;
}
float separation;
if (distSqr < Settings.EPSILON) {
separation = -r2;
manifold.normal.set(0.0f, 1.0f);
}
else {
final float dist = MathUtils.sqrt(distSqr);
separation = dist - r2;
final float a = 1.0f / dist;
manifold.normal.x = a * colPCD.x;
manifold.normal.y = a * colPCD.y;
}
manifold.pointCount = 1;
//manifold.points[0].id.key = 0;
manifold.points[0].id.zero(); //use this instead of zeroing through key
manifold.points[0].separation = separation;
// leave this for now
colPCP2.subLocal(manifold.normal.mul(r2));
colPCP.x = 0.5f * (colPCP1.x + colPCP2.x);
colPCP.y = 0.5f * (colPCP1.y + colPCP2.y);
XForm.mulTransToOut(xf1, colPCP, manifold.points[0].localPoint1);
XForm.mulTransToOut(xf2, colPCP, manifold.points[0].localPoint2);
}
/**
* puts collision information about the collision of a polygon and a circle
* @param manifold
* @param polygon
* @param xf1
* @param circle
* @param xf2
*/
public final void collidePolygonAndCircle(final Manifold manifold,
final PolygonShape polygon, final XForm xf1,
final CircleShape circle, final XForm xf2) {
manifold.pointCount = 0;
// Compute circle position in the frame of the polygon.
// INLINED
//Vec2 c = XForm.mul(xf2, circle.getLocalPosition());
//Vec2 cLocal = XForm.mulT(xf1, c);
final float cx = xf2.position.x + xf2.R.col1.x * circle.m_localPosition.x + xf2.R.col2.x * circle.m_localPosition.y;
final float cy = xf2.position.y + xf2.R.col1.y * circle.m_localPosition.x + xf2.R.col2.y * circle.m_localPosition.y;
final float v1x = cx - xf1.position.x;
final float v1y = cy - xf1.position.y;
final float cLocalx = v1x * xf1.R.col1.x + v1y * xf1.R.col1.y;
final float cLocaly = v1x * xf1.R.col2.x + v1y * xf1.R.col2.y;
// Find edge with maximum separation.
int normalIndex = 0;
float separation = -Float.MAX_VALUE;
final float radius = circle.getRadius();
final int vertexCount = polygon.getVertexCount();
final Vec2[] vertices = polygon.getVertices();
final Vec2[] normals = polygon.getNormals();
for (int i = 0; i < vertexCount; ++i) {
// INLINED
//float s = Vec2.dot(normals[i], cLocal.sub(vertices[i]));
final float s = normals[i].x * (cLocalx - vertices[i].x) + normals[i].y * (cLocaly - vertices[i].y);
if (s > circle.m_radius) {
// Early out.
return;
}
if (s > separation) {
normalIndex = i;
separation = s;
}
}
// If the center is inside the polygon ...
if (separation < Settings.EPSILON) {
manifold.pointCount = 1;
// INLINED
//manifold.normal = Mat22.mul(xf1.R, normals[normalIndex]);
manifold.normal.x = xf1.R.col1.x * normals[normalIndex].x + xf1.R.col2.x * normals[normalIndex].y;
manifold.normal.y = xf1.R.col1.y * normals[normalIndex].x + xf1.R.col2.y * normals[normalIndex].y;
manifold.points[0].id.features.incidentEdge = normalIndex;
manifold.points[0].id.features.incidentVertex = Collision.NULL_FEATURE;
manifold.points[0].id.features.referenceEdge = 0;
manifold.points[0].id.features.flip = 0;
// INLINED
//Vec2 position = c.sub(manifold.normal.mul(radius));
//manifold.points[0].localPoint1 = XForm.mulT(xf1, position);
//manifold.points[0].localPoint2 = XForm.mulT(xf2, position);
final float positionx = cx - manifold.normal.x * radius;
final float positiony = cy - manifold.normal.y * radius;
final float v1x1 = positionx-xf1.position.x;
final float v1y1 = positiony-xf1.position.y;
manifold.points[0].localPoint1.x = (v1x1 * xf1.R.col1.x + v1y1 * xf1.R.col1.y);
manifold.points[0].localPoint1.y = (v1x1 * xf1.R.col2.x + v1y1 * xf1.R.col2.y);
final float v1x2 = positionx-xf2.position.x;
final float v1y2 = positiony-xf2.position.y;
manifold.points[0].localPoint2.x = (v1x2 * xf2.R.col1.x + v1y2 * xf2.R.col1.y);
manifold.points[0].localPoint2.y = (v1x2 * xf2.R.col2.x + v1y2 * xf2.R.col2.y);
manifold.points[0].separation = separation - radius;
return;
}
// Project the circle center onto the edge segment.
final int vertIndex1 = normalIndex;
final int vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0;
// INLINED
//Vec2 e = vertices[vertIndex2].sub(vertices[vertIndex1]);
//float length = e.normalize();
float ex = vertices[vertIndex2].x - vertices[vertIndex1].x;
float ey = vertices[vertIndex2].y - vertices[vertIndex1].y;
final float length = MathUtils.sqrt(ex * ex + ey * ey);
assert(length > Settings.EPSILON);
final float invLength = 1.0f / length;
ex *= invLength;
ey *= invLength;
// Project the center onto the edge.
// INLINED
//float u = Vec2.dot(cLocal.sub(vertices[vertIndex1]), e);
final float u = (cLocalx - vertices[vertIndex1].x) * ex + (cLocaly - vertices[vertIndex1].y) * ey;
float px;
float py;
if (u <= 0.0f) {
px = vertices[vertIndex1].x;
py = vertices[vertIndex1].y;
manifold.points[0].id.features.incidentEdge = Collision.NULL_FEATURE;
manifold.points[0].id.features.incidentVertex = vertIndex1;
}
else if (u >= length) {
px = vertices[vertIndex2].x;
py = vertices[vertIndex2].y;
manifold.points[0].id.features.incidentEdge = Collision.NULL_FEATURE;
manifold.points[0].id.features.incidentVertex = vertIndex2;
}
else {
px = vertices[vertIndex1].x;
py = vertices[vertIndex1].y;
px += u * ex;
py += u * ey;
manifold.points[0].id.features.incidentEdge = normalIndex;
manifold.points[0].id.features.incidentVertex = Collision.NULL_FEATURE;
}
// INLINED
//Vec2 d = cLocal.sub(p);
//float dist = d.normalize();
float dx = cLocalx - px;
float dy = cLocaly - py;
final float dist = MathUtils.sqrt(dx * dx + dy * dy);
if (dist > radius) {
return;
}
if (dist >= Settings.EPSILON) {
final float invDist = 1.0f / dist;
dx *= invDist;
dy *= invDist;
}
manifold.pointCount = 1;
// INLINED
//manifold.normal = Mat22.mul(xf1.R, d);
//Vec2 position = c.sub(manifold.normal.mul(radius));
//manifold.points[0].localPoint1 = XForm.mulT(xf1, position);
//manifold.points[0].localPoint2 = XForm.mulT(xf2, position);
manifold.normal.x = xf1.R.col1.x * dx + xf1.R.col2.x * dy;
manifold.normal.y = xf1.R.col1.y * dx + xf1.R.col2.y * dy;
final float positionx = cx - manifold.normal.x * radius;
final float positiony = cy - manifold.normal.y * radius;
final float v1x1 = positionx - xf1.position.x;
final float v1y1 = positiony - xf1.position.y;
manifold.points[0].localPoint1.x = (v1x1 * xf1.R.col1.x + v1y1 * xf1.R.col1.y);
manifold.points[0].localPoint1.y = (v1x1 * xf1.R.col2.x + v1y1 * xf1.R.col2.y);
final float v1x2 = positionx - xf2.position.x;
final float v1y2 = positiony - xf2.position.y;
manifold.points[0].localPoint2.x = (v1x2 * xf2.R.col1.x + v1y2 * xf2.R.col1.y);
manifold.points[0].localPoint2.y = (v1x2 * xf2.R.col2.x + v1y2 * xf2.R.col2.y);
manifold.points[0].separation = dist - radius;
manifold.points[0].id.features.referenceEdge = 0;
manifold.points[0].id.features.flip = 0;
}
private final Vec2 ECd = new Vec2();
private final Vec2 ECc = new Vec2();
//private Vec2 ECcLocalSubV1 = new Vec2();
private final Vec2 ECcLocal = new Vec2();
/**
* puts collision information into the manifold from a circle and edge collision
* @param manifold
* @param edge
* @param xf1
* @param circle
* @param xf2
*/
public final void collideEdgeAndCircle(final Manifold manifold,
final EdgeShape edge, final XForm xf1,
final CircleShape circle, final XForm xf2) {
manifold.pointCount = 0;
XForm.mulToOut(xf2, circle.getMemberLocalPosition(), ECc);
XForm.mulTransToOut(xf1, ECc, ECcLocal);
final Vec2 n = edge.getNormalVector();
final Vec2 v1 = edge.getVertex1();
final Vec2 v2 = edge.getVertex2();
final float radius = circle.getRadius();
float separation;
ECd.set(ECcLocal);
ECd.subLocal(v1);
final float dirDist = Vec2.dot(ECd, edge.getDirectionVector());
if (dirDist <= 0) {
if (Vec2.dot(ECd, edge.getCorner1Vector()) < 0) {
return;
}
XForm.mulToOut(xf1, v1, ECd);
ECd.subLocal(ECc);
ECd.negateLocal();
// d = c.sub(XForm.mul(xf1, v1));
} else if (dirDist >= edge.getLength()) {
ECd.set(ECcLocal);
ECd.subLocal(v2);
if (Vec2.dot(ECd, edge.getCorner2Vector()) > 0) {
return;
}
XForm.mulToOut(xf1, v2, ECd);
ECd.subLocal(ECc);
ECd.negateLocal();
//d = c.sub(XForm.mul(xf1, v2));
} else {
separation = Vec2.dot(ECd, n);
if (separation > radius || separation < -radius) {
return;
}
separation -= radius;
Mat22.mulToOut(xf1.R, n, manifold.normal);
manifold.pointCount = 1;
manifold.points[0].id.zero();// key = 0;
manifold.points[0].separation = separation;
// just use d as temp vec here, we don't need it any more
ECd.set(manifold.normal);
ECd.mulLocal(radius);
ECc.subLocal(ECd);
XForm.mulTransToOut(xf1, ECc, manifold.points[0].localPoint1);
XForm.mulTransToOut(xf2, ECc, manifold.points[0].localPoint2);
return;
}
final float distSqr = Vec2.dot(ECd,ECd);
if (distSqr > radius * radius) {
return;
}
if (distSqr < Settings.EPSILON) {
separation = -radius;
Mat22.mulToOut(xf1.R, n, manifold.normal);
} else {
separation = ECd.normalize() - radius;
manifold.normal.set(ECd);
}
manifold.pointCount = 1;
manifold.points[0].id.zero();//key = 0;
manifold.points[0].separation = separation;
// just use d as temp vec here, we don't need it any more
ECd.set(manifold.normal);
ECd.mulLocal(radius);
ECc.subLocal(ECd);
//c.subLocal(manifold.normal.mul(radius));
XForm.mulTransToOut(xf1, ECc, manifold.points[0].localPoint1);
XForm.mulTransToOut(xf2, ECc, manifold.points[0].localPoint2);
}
}