/* * 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.AABB; import org.jbox2d.collision.MassData; import org.jbox2d.collision.Segment; import org.jbox2d.collision.SegmentCollide; import org.jbox2d.common.Mat22; import org.jbox2d.common.MathUtils; import org.jbox2d.common.RaycastResult; import org.jbox2d.common.Settings; import org.jbox2d.common.Vec2; import org.jbox2d.common.XForm; import org.jbox2d.dynamics.Body; import org.jbox2d.pooling.TLVec2; //Updated to rev 56->108->139 of b2Shape.cpp/.h /** * A circle shape. Create using {@link Body#createShape(ShapeDef)} with a * {@link CircleDef}, not the constructor here. * * @see Body#createShape(ShapeDef) * @see CircleDef */ public class CircleShape extends Shape { public float m_radius; public final Vec2 m_localPosition; /** * this is used internally, instead use {@link Body#createShape(ShapeDef)} * with a {@link CircleDef} * * @see Body#createShape(ShapeDef) * @see CircleDef * @param def */ public CircleShape(final ShapeDef def) { super(def); assert (def.type == ShapeType.CIRCLE_SHAPE); final CircleDef circleDef = (CircleDef) def; m_type = ShapeType.CIRCLE_SHAPE; m_localPosition = circleDef.localPosition.clone(); m_radius = circleDef.radius; } /** * @see Shape#updateSweepRadius(Vec2) */ @Override public void updateSweepRadius(final Vec2 center) { // Update the sweep radius (maximum radius) as measured from // a local center point. // Vec2 d = m_localPosition.sub(center); final float dx = m_localPosition.x - center.x; final float dy = m_localPosition.y - center.y; m_sweepRadius = MathUtils.sqrt(dx * dx + dy * dy) + m_radius - Settings.toiSlop; } // djm pooling private static final TLVec2 tlCenter = new TLVec2(); /** * checks to see if the point is in this shape. * * @see Shape#testPoint(XForm, Vec2) */ @Override public boolean testPoint(final XForm transform, final Vec2 p) { final Vec2 center = tlCenter.get(); Mat22.mulToOut(transform.R, m_localPosition, center); center.addLocal(transform.position); final Vec2 d = center.subLocal(p).negateLocal(); boolean ret = Vec2.dot(d, d) <= m_radius * m_radius; return ret; } // djm pooling private static final TLVec2 tlS = new TLVec2(); private static final TLVec2 tlPosition = new TLVec2(); private static final TLVec2 tlR = new TLVec2(); // Collision Detection in Interactive 3D Environments by Gino van den Bergen // From Section 3.1.2 // x = s + a * r // norm(x) = radius /** * @see Shape#testSegment(XForm, RaycastResult, Segment, float) */ @Override public SegmentCollide testSegment(final XForm xf, final RaycastResult out, final Segment segment, final float maxLambda) { Vec2 position = tlPosition.get(); Vec2 s = tlS.get(); Mat22.mulToOut(xf.R, m_localPosition, position); position.addLocal(xf.position); s.set(segment.p1); s.subLocal(position); final float b = Vec2.dot(s, s) - m_radius * m_radius; // Does the segment start inside the circle? if (b < 0.0f) { return SegmentCollide.STARTS_INSIDE_COLLIDE; } Vec2 r = tlR.get(); // Solve quadratic equation. r.set(segment.p2).subLocal(segment.p1); final float c = Vec2.dot(s, r); final float rr = Vec2.dot(r, r); final float sigma = c * c - rr * b; // Check for negative discriminant and short segment. if (sigma < 0.0f || rr < Settings.EPSILON) { return SegmentCollide.MISS_COLLIDE; } // Find the point of intersection of the line with the circle. float a = -(c + MathUtils.sqrt(sigma)); // System.out.println(a + "; " + maxLambda + "; " + rr + " ; " + (a <= maxLambda * rr)); // Is the intersection point on the segment? if (0.0f <= a && a <= maxLambda * rr) { // System.out.println("Got here"); a /= rr; out.lambda = a; out.normal.set(r).mulLocal(a).addLocal(s); out.normal.normalize(); return SegmentCollide.HIT_COLLIDE; } return SegmentCollide.HIT_COLLIDE; } // djm pooling private static final TLVec2 tlP = new TLVec2(); /** * @see Shape#computeAABB(AABB, XForm) */ @Override public void computeAABB(final AABB aabb, final XForm transform) { final Vec2 p = tlP.get(); Mat22.mulToOut(transform.R, m_localPosition, p); p.addLocal(transform.position); aabb.lowerBound.x = p.x - m_radius; aabb.lowerBound.y = p.y - m_radius; aabb.upperBound.x = p.x + m_radius; aabb.upperBound.y = p.y + m_radius; } /** * @see Shape#computeSweptAABB(AABB, XForm, XForm) */ @Override public void computeSweptAABB(final AABB aabb, final XForm transform1, final XForm transform2) { // INLINED // Vec2 p1 = transform1.position.add(Mat22.mul(transform1.R, // m_localPosition)); // Vec2 p2 = transform2.position.add(Mat22.mul(transform2.R, // m_localPosition)); // Vec2 lower = Vec2.min(p1, p2); // Vec2 upper = Vec2.max(p1, p2); // aabb.lowerBound.set(lower.x - m_radius, lower.y - m_radius); // aabb.upperBound.set(upper.x + m_radius, upper.y + m_radius); final float p1x = transform1.position.x + transform1.R.col1.x * m_localPosition.x + transform1.R.col2.x * m_localPosition.y; final float p1y = transform1.position.y + transform1.R.col1.y * m_localPosition.x + transform1.R.col2.y * m_localPosition.y; final float p2x = transform2.position.x + transform2.R.col1.x * m_localPosition.x + transform2.R.col2.x * m_localPosition.y; final float p2y = transform2.position.y + transform2.R.col1.y * m_localPosition.x + transform2.R.col2.y * m_localPosition.y; final float lowerx = p1x < p2x ? p1x : p2x; final float lowery = p1y < p2y ? p1y : p2y; final float upperx = p1x > p2x ? p1x : p2x; final float uppery = p1y > p2y ? p1y : p2y; aabb.lowerBound.x = lowerx - m_radius; aabb.lowerBound.y = lowery - m_radius; aabb.upperBound.x = upperx + m_radius; aabb.upperBound.y = uppery + m_radius; // System.out.println("Circle swept AABB: " + aabb.lowerBound + " " + // aabb.upperBound); // System.out.println("Transforms: "+transform1.position+ " " + // transform2.position+"\n"); } /** * @see Shape#computeMass(MassData) */ @Override public void computeMass(final MassData massData) { massData.mass = m_density * MathUtils.PI * m_radius * m_radius; massData.center.set(m_localPosition); // inertia about the local origin massData.I = massData.mass * (0.5f * m_radius * m_radius + Vec2.dot(m_localPosition, m_localPosition)); } public float getRadius() { return m_radius; } /** * Returns a copy of the local position * * @return */ public Vec2 getLocalPosition() { return m_localPosition.clone(); } /** * Returns the member variable of the local position. Don't change this. * * @return */ public Vec2 getMemberLocalPosition() { return m_localPosition; } // djm pooling from above /** * @see Shape#computeSubmergedArea(Vec2, float, XForm, Vec2) */ public float computeSubmergedArea(final Vec2 normal, float offset, XForm xf, Vec2 c) { // pooling final Vec2 p = tlP.get(); XForm.mulToOut(xf, m_localPosition, p); float l = -(Vec2.dot(normal, p) - offset); if (l < -m_radius + Settings.EPSILON) { // Completely dry return 0; } if (l > m_radius) { // Completely wet c.set(p); return Settings.pi * m_radius * m_radius; } // Magic float r2 = m_radius * m_radius; float l2 = l * l; float area = (float) (r2 * (Math.asin(l / m_radius) + Settings.pi / 2.0f) + l * MathUtils.sqrt(r2 - l2)); float com = (float) (-2.0f / 3.0f * MathUtils.pow(r2 - l2, 1.5f) / area); c.x = p.x + normal.x * com; c.y = p.y + normal.y * com; return area; } }