All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jbox2d.collision.shapes.PolygonShape Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2011, Daniel Murphy
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the  nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL DANIEL MURPHY BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/
/*
 * 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.RayCastInput;
import org.jbox2d.collision.RayCastOutput;
import org.jbox2d.common.Mat22;
import org.jbox2d.common.Settings;
import org.jbox2d.common.Transform;
import org.jbox2d.common.Vec2;

//Updated to rev 100

/** A convex polygon shape. Create using Body.createShape(ShapeDef), not the ructor here. */
public class PolygonShape extends Shape {
	/** Dump lots of debug information. */
	private static boolean m_debug = false;
	
	/**
	 * Local position of the shape centroid in parent body frame.
	 */
	public final Vec2 m_centroid = new Vec2();
	
	/**
	 * The vertices of the shape. Note: use getVertexCount(), not m_vertices.length, to
	 * get number of active vertices.
	 */
	public final Vec2 m_vertices[];
	
	/**
	 * The normals of the shape. Note: use getVertexCount(), not m_normals.length, to get
	 * number of active normals.
	 */
	public final Vec2 m_normals[];
	
	/**
	 * Number of active vertices in the shape.
	 */
	public int m_vertexCount;
	
	// pooling
	private final Vec2 pool1 = new Vec2();
	private final Vec2 pool2 = new Vec2();
	private final Vec2 pool3 = new Vec2();
	private final Vec2 pool4 = new Vec2();
	private final Vec2 pool5 = new Vec2();
	private final Vec2 pool6 = new Vec2();
	private Transform poolt1 = new Transform();
	
	public PolygonShape() {
		m_type = ShapeType.POLYGON;
		
		m_vertexCount = 0;
		m_vertices = new Vec2[Settings.maxPolygonVertices];
		for (int i = 0; i < m_vertices.length; i++) {
			m_vertices[i] = new Vec2();
		}
		m_normals = new Vec2[Settings.maxPolygonVertices];
		for (int i = 0; i < m_normals.length; i++) {
			m_normals[i] = new Vec2();
		}
		m_radius = Settings.polygonRadius;
		m_centroid.setZero();
	}
	
	public final Shape clone() {
		PolygonShape shape = new PolygonShape();
		shape.m_centroid.set(this.m_centroid);
		for (int i = 0; i < shape.m_normals.length; i++) {
			shape.m_normals[i].set(m_normals[i]);
			shape.m_vertices[i].set(m_vertices[i]);
		}
		shape.m_radius = this.m_radius;
		shape.m_vertexCount = this.m_vertexCount;
		return shape;
	}
	
	/**
	 * Get the supporting vertex index in the given direction.
	 * 
	 * @param d
	 * @return
	 */
	public final int getSupport(final Vec2 d) {
		int bestIndex = 0;
		float bestValue = Vec2.dot(m_vertices[0], d);
		for (int i = 1; i < m_vertexCount; i++) {
			float value = Vec2.dot(m_vertices[i], d);
			if (value > bestValue) {
				bestIndex = i;
				bestValue = value;
			}
		}
		return bestIndex;
	}
	
	/**
	 * Get the supporting vertex in the given direction.
	 * 
	 * @param d
	 * @return
	 */
	public final Vec2 getSupportVertex(final Vec2 d) {
		int bestIndex = 0;
		float bestValue = Vec2.dot(m_vertices[0], d);
		for (int i = 1; i < m_vertexCount; i++) {
			float value = Vec2.dot(m_vertices[i], d);
			if (value > bestValue) {
				bestIndex = i;
				bestValue = value;
			}
		}
		return m_vertices[bestIndex];
	}
	
	/**
	 * Copy vertices. This assumes the vertices define a convex polygon.
	 * It is assumed that the exterior is the the right of each edge.
	 */
	public final void set(final Vec2[] vertices, final int count) {
		assert (2 <= count && count <= Settings.maxPolygonVertices);
		m_vertexCount = count;
		
		// Copy vertices.
		for (int i = 0; i < m_vertexCount; ++i) {
			if(m_vertices[i] == null){
				m_vertices[i] = new Vec2();
			}
			m_vertices[i].set(vertices[i]);
		}
		
		final Vec2 edge = pool1;
		
		// Compute normals. Ensure the edges have non-zero length.
		for (int i = 0; i < m_vertexCount; ++i) {
			final int i1 = i;
			final int i2 = i + 1 < m_vertexCount ? i + 1 : 0;
			edge.set(m_vertices[i2]).subLocal(m_vertices[i1]);
			
			assert (edge.lengthSquared() > Settings.EPSILON * Settings.EPSILON);
			Vec2.crossToOut(edge, 1f, m_normals[i]);
			m_normals[i].normalize();
		}
		
		if (m_debug) {
			
			final Vec2 r = pool2;
			
			// Ensure the polygon is convex and the interior
			// is to the left of each edge.
			for (int i = 0; i < m_vertexCount; ++i) {
				final int i1 = i;
				final int i2 = i + 1 < m_vertexCount ? i + 1 : 0;
				edge.set(m_vertices[i2]).subLocal(m_vertices[i1]);
				
				for (int j = 0; j < m_vertexCount; ++j) {
					// Don't check vertices on the current edge.
					if (j == i1 || j == i2) {
						continue;
					}
					
					r.set(m_vertices[j]).subLocal(m_vertices[i1]);
					
					// Your polygon is non-convex (it has an indentation) or
					// has colinear edges.
					final float s = Vec2.cross(edge, r);
					assert (s > 0.0f);
				}
			}
		}
		
		// Compute the polygon centroid.
		computeCentroidToOut(m_vertices, m_vertexCount, m_centroid);
	}
	
	/**
	 * Build vertices to represent an axis-aligned box.
	 * 
	 * @param hx
	 *            the half-width.
	 * @param hy
	 *            the half-height.
	 */
	public final void setAsBox(final float hx, final float hy) {
		m_vertexCount = 4;
		m_vertices[0].set(-hx, -hy);
		m_vertices[1].set(hx, -hy);
		m_vertices[2].set(hx, hy);
		m_vertices[3].set(-hx, hy);
		m_normals[0].set(0.0f, -1.0f);
		m_normals[1].set(1.0f, 0.0f);
		m_normals[2].set(0.0f, 1.0f);
		m_normals[3].set(-1.0f, 0.0f);
		m_centroid.setZero();
	}
	
	/**
	 * Build vertices to represent an oriented box.
	 * 
	 * @param hx
	 *            the half-width.
	 * @param hy
	 *            the half-height.
	 * @param center
	 *            the center of the box in local coordinates.
	 * @param angle
	 *            the rotation of the box in local coordinates.
	 */
	public final void setAsBox(final float hx, final float hy, final Vec2 center, final float angle) {
		m_vertexCount = 4;
		m_vertices[0].set(-hx, -hy);
		m_vertices[1].set(hx, -hy);
		m_vertices[2].set(hx, hy);
		m_vertices[3].set(-hx, hy);
		m_normals[0].set(0.0f, -1.0f);
		m_normals[1].set(1.0f, 0.0f);
		m_normals[2].set(0.0f, 1.0f);
		m_normals[3].set(-1.0f, 0.0f);
		m_centroid.set(center);
		
		final Transform xf = poolt1;
		xf.position.set(center);
		xf.R.set(angle);
		
		// Transform vertices and normals.
		for (int i = 0; i < m_vertexCount; ++i) {
			Transform.mulToOut(xf, m_vertices[i], m_vertices[i]);
			Mat22.mulToOut(xf.R, m_normals[i], m_normals[i]);
		}
	}
	
	/**
	 * Set this as a single edge.
	 * 
	 * @param v1
	 * @param v2
	 */
	public final void setAsEdge(final Vec2 v1, final Vec2 v2) {
		m_vertexCount = 2;
		m_vertices[0].set(v1);
		m_vertices[1].set(v2);
		m_centroid.set(v1).addLocal(v2).mulLocal(0.5f);
		// = 0.5f * (v1 + v2);
		m_normals[0].set(v2).subLocal(v1);
		Vec2.crossToOut(m_normals[0], 1f, m_normals[0]);
		// m_normals[0] = Cross(v2 - v1, 1.0f);
		m_normals[0].normalize();
		m_normals[1].set(m_normals[0]).negateLocal();
	}
	
	/**
	 * @see Shape#testPoint(Transform, Vec2)
	 */
	@Override
	public final boolean testPoint(final Transform xf, final Vec2 p) {
		
		final Vec2 pLocal = pool1;
		
		pLocal.set(p).subLocal(xf.position);
		Mat22.mulTransToOut(xf.R, pLocal, pLocal);
		
		if (m_debug) {
			System.out.println("--testPoint debug--");
			System.out.println("Vertices: ");
			for (int i = 0; i < m_vertexCount; ++i) {
				System.out.println(m_vertices[i]);
			}
			System.out.println("pLocal: " + pLocal);
		}
		
		final Vec2 temp = pool2;
		
		for (int i = 0; i < m_vertexCount; ++i) {
			temp.set(pLocal).subLocal(m_vertices[i]);
			final float dot = Vec2.dot(m_normals[i], temp);
			if (dot > 0.0f) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * @see Shape#computeAABB(AABB, Transform, int)
	 */
	@Override
	public final void computeAABB(final AABB argAabb, final Transform argXf) {
		
		final Vec2 lower = pool1;
		final Vec2 upper = pool2;
		final Vec2 v = pool3;
		
		Transform.mulToOut(argXf, m_vertices[0], lower);
		upper.set(lower);
		
		for (int i = 1; i < m_vertexCount; ++i) {
			Transform.mulToOut(argXf, m_vertices[i], v);
			// Vec2 v = Mul(xf, m_vertices[i]);
			Vec2.minToOut(lower, v, lower);
			Vec2.maxToOut(upper, v, upper);
		}
		
		// Vec2 r(m_radius, m_radius);
		// aabb->lowerBound = lower - r;
		// aabb->upperBound = upper + r;
		
		argAabb.lowerBound.x = lower.x - m_radius;
		argAabb.lowerBound.y = lower.y - m_radius;
		argAabb.upperBound.x = upper.x + m_radius;
		argAabb.upperBound.y = upper.y + m_radius;
	}
	
	// djm pooling, and from above
	/*
	 * private static final TLVec2 tlNormalL = new TLVec2();
	 * private static final TLMassData tlMd = new TLMassData();
	 * private static final FloatArray tldepths = new FloatArray();
	 * private static final TLVec2 tlIntoVec = new TLVec2();
	 * private static final TLVec2 tlOutoVec = new TLVec2();
	 * private static final TLVec2 tlP2b = new TLVec2();
	 * private static final TLVec2 tlP3 = new TLVec2();
	 * private static final TLVec2 tlcenter = new TLVec2();
	 * /*
	 * @see Shape#computeSubmergedArea(Vec2, float, XForm, Vec2)
	 * public float computeSubmergedArea(final Vec2 normal, float offset, Transform xf,
	 * Vec2 c) {
	 * final Vec2 normalL = tlNormalL.get();
	 * final MassData md = tlMd.get();
	 * //Transform plane into shape co-ordinates
	 * Mat22.mulTransToOut(xf.R,normal, normalL);
	 * float offsetL = offset - Vec2.dot(normal,xf.position);
	 * final Float[] depths = tldepths.get(Settings.maxPolygonVertices);
	 * int diveCount = 0;
	 * int intoIndex = -1;
	 * int outoIndex = -1;
	 * boolean lastSubmerged = false;
	 * int i = 0;
	 * for (i = 0; i < m_vertexCount; ++i){
	 * depths[i] = Vec2.dot(normalL,m_vertices[i]) - offsetL;
	 * boolean isSubmerged = depths[i]<-Settings.EPSILON;
	 * if (i > 0){
	 * if (isSubmerged){
	 * if (!lastSubmerged){
	 * intoIndex = i-1;
	 * diveCount++;
	 * }
	 * }
	 * else{
	 * if (lastSubmerged){
	 * outoIndex = i-1;
	 * diveCount++;
	 * }
	 * }
	 * }
	 * lastSubmerged = isSubmerged;
	 * }
	 * switch(diveCount){
	 * case 0:
	 * if (lastSubmerged){
	 * //Completely submerged
	 * computeMass(md, 1.0f);
	 * Transform.mulToOut(xf,md.center, c);
	 * return md.mass;
	 * }
	 * else{
	 * return 0;
	 * }
	 * case 1:
	 * if(intoIndex==-1){
	 * intoIndex = m_vertexCount-1;
	 * }
	 * else{
	 * outoIndex = m_vertexCount-1;
	 * }
	 * break;
	 * }
	 * final Vec2 intoVec = tlIntoVec.get();
	 * final Vec2 outoVec = tlOutoVec.get();
	 * final Vec2 e1 = tle1.get();
	 * final Vec2 e2 = tle2.get();
	 * int intoIndex2 = (intoIndex+1) % m_vertexCount;
	 * int outoIndex2 = (outoIndex+1) % m_vertexCount;
	 * float intoLambda = (0 - depths[intoIndex]) / (depths[intoIndex2] -
	 * depths[intoIndex]);
	 * float outoLambda = (0 - depths[outoIndex]) / (depths[outoIndex2] -
	 * depths[outoIndex]);
	 * intoVec.set(m_vertices[intoIndex].x*(1-intoLambda)+m_vertices[intoIndex2].x*intoLambda
	 * ,
	 * m_vertices[intoIndex].y*(1-intoLambda)+m_vertices[intoIndex2].y*intoLambda);
	 * outoVec.set(m_vertices[outoIndex].x*(1-outoLambda)+m_vertices[outoIndex2].x*outoLambda
	 * ,
	 * m_vertices[outoIndex].y*(1-outoLambda)+m_vertices[outoIndex2].y*outoLambda);
	 * // Initialize accumulator
	 * float area = 0;
	 * final Vec2 center = tlcenter.get();
	 * center.setZero();
	 * final Vec2 p2b = tlP2b.get().set(m_vertices[intoIndex2]);
	 * final Vec2 p3 = tlP3.get();
	 * p3.setZero();
	 * float k_inv3 = 1.0f / 3.0f;
	 * // An awkward loop from intoIndex2+1 to outIndex2
	 * i = intoIndex2;
	 * while (i != outoIndex2){
	 * i = (i+1) % m_vertexCount;
	 * if (i == outoIndex2){
	 * p3.set(outoVec);
	 * }
	 * else{
	 * p3.set(m_vertices[i]);
	 * }
	 * // Add the triangle formed by intoVec,p2,p3
	 * {
	 * e1.set(p2b).subLocal(intoVec);
	 * e2.set(p3).subLocal(intoVec);
	 * float D = Vec2.cross(e1, e2);
	 * float triangleArea = 0.5f * D;
	 * area += triangleArea;
	 * // Area weighted centroid
	 * center.x += triangleArea * k_inv3 * (intoVec.x + p2b.x + p3.x);
	 * center.y += triangleArea * k_inv3 * (intoVec.y + p2b.y + p3.y);
	 * }
	 * //
	 * p2b.set(p3);
	 * }
	 * // Normalize and transform centroid
	 * center.x *= 1.0f / area;
	 * center.y *= 1.0f / area;
	 * Transform.mulToOut(xf, center, c);
	 * return area;
	 * }
	 */

	/*
	 * Get the supporting vertex index in the given direction.
	 * @param d
	 * @return
	 * public final int getSupport( final Vec2 d){
	 * int bestIndex = 0;
	 * float bestValue = Vec2.dot(m_vertices[0], d);
	 * for (int i = 1; i < m_vertexCount; ++i){
	 * final float value = Vec2.dot(m_vertices[i], d);
	 * if (value > bestValue){
	 * bestIndex = i;
	 * bestValue = value;
	 * }
	 * }
	 * return bestIndex;
	 * }
	 * /**
	 * Get the supporting vertex in the given direction.
	 * @param d
	 * @return
	 * public final Vec2 getSupportVertex( final Vec2 d){
	 * int bestIndex = 0;
	 * float bestValue = Vec2.dot(m_vertices[0], d);
	 * for (int i = 1; i < m_vertexCount; ++i){
	 * final float value = Vec2.dot(m_vertices[i], d);
	 * if (value > bestValue){
	 * bestIndex = i;
	 * bestValue = value;
	 * }
	 * }
	 * return m_vertices[bestIndex];
	 * }
	 */

	/**
	 * Get the vertex count.
	 * 
	 * @return
	 */
	public final int getVertexCount() {
		return m_vertexCount;
	}
	
	/**
	 * Get a vertex by index.
	 * 
	 * @param index
	 * @return
	 */
	public final Vec2 getVertex(final int index) {
		assert (0 <= index && index < m_vertexCount);
		return m_vertices[index];
	}

	
	/**
	 * @see org.jbox2d.collision.shapes.Shape#raycast(org.jbox2d.collision.RayCastOutput,
	 *      org.jbox2d.collision.RayCastInput, org.jbox2d.common.Transform, int)
	 */
	@Override
	public final boolean raycast(RayCastOutput argOutput, RayCastInput argInput, Transform argXf) {
		final Vec2 p1 = pool1;
		final Vec2 p2 = pool2;
		final Vec2 d = pool3;
		final Vec2 temp = pool4;
		
		p1.set(argInput.p1).subLocal(argXf.position);
		Mat22.mulTransToOut(argXf.R, p1, p1);
		p2.set(argInput.p2).subLocal(argXf.position);
		Mat22.mulTransToOut(argXf.R, p2, p2);
		d.set(p2).subLocal(p1);
		
		if (m_vertexCount == 2) {
			Vec2 v1 = m_vertices[0];
			Vec2 v2 = m_vertices[1];
			Vec2 normal = m_normals[0];
			
			// q = p1 + t * d
			// dot(normal, q - v1) = 0
			// dot(normal, p1 - v1) + t * dot(normal, d) = 0
			temp.set(v1).subLocal(p1);
			float numerator = Vec2.dot(normal, temp);
			float denominator = Vec2.dot(normal, d);
			
			if (denominator == 0.0f) {
				return false;
			}
			
			float t = numerator / denominator;
			if (t < 0.0f || 1.0f < t) {
				return false;
			}
			
			final Vec2 q = pool5;
			final Vec2 r = pool6;
			
			// Vec2 q = p1 + t * d;
			temp.set(d).mulLocal(t);
			q.set(p1).addLocal(temp);
			
			// q = v1 + s * r
			// s = dot(q - v1, r) / dot(r, r)
			// Vec2 r = v2 - v1;
			r.set(v2).subLocal(v1);
			
			float rr = Vec2.dot(r, r);
			if (rr == 0.0f) {
				return false;
			}
			
			temp.set(q).subLocal(v1);
			float s = Vec2.dot(temp, r) / rr;
			if (s < 0.0f || 1.0f < s) {
				return false;
			}
			
			argOutput.fraction = t;
			if (numerator > 0.0f) {
				// argOutput.normal = -normal;
				argOutput.normal.set(normal).mulLocal(-1);
			}
			else {
				// output.normal = normal;
				argOutput.normal.set(normal);
			}
			return true;
		}
		else {
			
			float lower = 0, upper = argInput.maxFraction;
			
			int index = -1;
			
			for (int i = 0; i < m_vertexCount; ++i) {
				// p = p1 + a * d
				// dot(normal, p - v) = 0
				// dot(normal, p1 - v) + a * dot(normal, d) = 0
				temp.set(m_vertices[i]).subLocal(p1);
				final float numerator = Vec2.dot(m_normals[i], temp);
				final float denominator = Vec2.dot(m_normals[i], d);
				
				if (denominator == 0.0f) {
					if (numerator < 0.0f) {
						return false;
					}
				}
				else {
					// Note: we want this predicate without division:
					// lower < numerator / denominator, where denominator < 0
					// Since denominator < 0, we have to flip the inequality:
					// lower < numerator / denominator <==> denominator * lower >
					// numerator.
					if (denominator < 0.0f && numerator < lower * denominator) {
						// Increase lower.
						// The segment enters this half-space.
						lower = numerator / denominator;
						index = i;
					}
					else if (denominator > 0.0f && numerator < upper * denominator) {
						// Decrease upper.
						// The segment exits this half-space.
						upper = numerator / denominator;
					}
				}
				
				if (upper < lower) {
					return false;
				}
			}
			
			assert (0.0f <= lower && lower <= argInput.maxFraction);
			
			if (index >= 0) {
				argOutput.fraction = lower;
				Mat22.mulToOut(argXf.R, m_normals[index], argOutput.normal);
				// normal = Mul(xf.R, m_normals[index]);
				return true;
			}
		}
		return false;
	}
	
	
	public final void computeCentroidToOut(final Vec2[] vs, final int count, final Vec2 out) {
		assert (count >= 3);
		
		out.set(0.0f, 0.0f);
		float area = 0.0f;
		
		if (count == 2) {
			out.set(vs[0]).addLocal(vs[1]).mulLocal(.5f);
			return;
		}
		
		// pRef is the reference point for forming triangles.
		// It's location doesn't change the result (except for rounding error).
		final Vec2 pRef = pool1;
		pRef.setZero();
		
		final Vec2 e1 = pool2;
		final Vec2 e2 = pool3;
		
		final float inv3 = 1.0f / 3.0f;
		
		for (int i = 0; i < count; ++i) {
			// Triangle vertices.
			final Vec2 p1 = pRef;
			final Vec2 p2 = vs[i];
			final Vec2 p3 = i + 1 < count ? vs[i + 1] : vs[0];
			
			e1.set(p2).subLocal(p1);
			e2.set(p3).subLocal(p1);
			
			final float D = Vec2.cross(e1, e2);
			
			final float triangleArea = 0.5f * D;
			area += triangleArea;
			
			// Area weighted centroid
			out.addLocal(p1).addLocal(p2).addLocal(p3).mulLocal(triangleArea * inv3);
		}
		
		// Centroid
		assert (area > Settings.EPSILON);
		out.mulLocal(1.0f / area);
	}
	
	/**
	 * @see Shape#computeMass(MassData)
	 */
	public void computeMass(final MassData massData, float density) {
		// Polygon mass, centroid, and inertia.
		// Let rho be the polygon density in mass per unit area.
		// Then:
		// mass = rho * int(dA)
		// centroid.x = (1/mass) * rho * int(x * dA)
		// centroid.y = (1/mass) * rho * int(y * dA)
		// I = rho * int((x*x + y*y) * dA)
		//
		// We can compute these integrals by summing all the integrals
		// for each triangle of the polygon. To evaluate the integral
		// for a single triangle, we make a change of variables to
		// the (u,v) coordinates of the triangle:
		// x = x0 + e1x * u + e2x * v
		// y = y0 + e1y * u + e2y * v
		// where 0 <= u && 0 <= v && u + v <= 1.
		//
		// We integrate u from [0,1-v] and then v from [0,1].
		// We also need to use the Jacobian of the transformation:
		// D = cross(e1, e2)
		//
		// Simplification: triangle centroid = (1/3) * (p1 + p2 + p3)
		//
		// The rest of the derivation is handled by computer algebra.
		
		assert (m_vertexCount >= 2);
		
		// A line segment has zero mass.
		if (m_vertexCount == 2) {
			// massData.center = 0.5f * (m_vertices[0] + m_vertices[1]);
			massData.center.set(m_vertices[0]).addLocal(m_vertices[1]).mulLocal(0.5f);
			massData.mass = 0.0f;
			massData.I = 0.0f;
			return;
		}
		
		final Vec2 center = pool1;
		center.setZero();
		float area = 0.0f;
		float I = 0.0f;
		
		// pRef is the reference point for forming triangles.
		// It's location doesn't change the result (except for rounding error).
		final Vec2 pRef = pool2;
		pRef.setZero();
		
		final float k_inv3 = 1.0f / 3.0f;
		
		final Vec2 e1 = pool3;
		final Vec2 e2 = pool4;
		
		for (int i = 0; i < m_vertexCount; ++i) {
			// Triangle vertices.
			final Vec2 p1 = pRef;
			final Vec2 p2 = m_vertices[i];
			final Vec2 p3 = i + 1 < m_vertexCount ? m_vertices[i + 1] : m_vertices[0];
			
			e1.set(p2);
			e1.subLocal(p1);
			
			e2.set(p3);
			e2.subLocal(p1);
			
			final float D = Vec2.cross(e1, e2);
			
			final float triangleArea = 0.5f * D;
			area += triangleArea;
			
			// Area weighted centroid
			center.x += triangleArea * k_inv3 * (p1.x + p2.x + p3.x);
			center.y += triangleArea * k_inv3 * (p1.y + p2.y + p3.y);
			
			final float px = p1.x, py = p1.y;
			final float ex1 = e1.x, ey1 = e1.y;
			final float ex2 = e2.x, ey2 = e2.y;
			
			final float intx2 = k_inv3 * (0.25f * (ex1 * ex1 + ex2 * ex1 + ex2 * ex2) + (px * ex1 + px * ex2)) + 0.5f
					* px * px;
			final float inty2 = k_inv3 * (0.25f * (ey1 * ey1 + ey2 * ey1 + ey2 * ey2) + (py * ey1 + py * ey2)) + 0.5f
					* py * py;
			
			I += D * (intx2 + inty2);
		}
		
		// Total mass
		massData.mass = density * area;
		
		// Center of mass
		assert (area > Settings.EPSILON);
		center.mulLocal(1.0f / area);
		massData.center.set(center);
		
		// Inertia tensor relative to the local origin.
		massData.I = I * density;
	}
	
	/*
	 * Get the local centroid relative to the parent body. /
	 * public Vec2 getCentroid() {
	 * return m_centroid.clone();
	 * }
	 */

	/** Get the vertices in local coordinates. */
	public Vec2[] getVertices() {
		return m_vertices;
	}
	
	/** Get the edge normal vectors. There is one for each vertex. */
	public Vec2[] getNormals() {
		return m_normals;
	}
	
	/** Get the centroid and apply the supplied transform. */
	public Vec2 centroid(final Transform xf) {
		return Transform.mul(xf, m_centroid);
	}
	
	/** Get the centroid and apply the supplied transform. */
	public Vec2 centroidToOut(final Transform xf, final Vec2 out) {
		Transform.mulToOut(xf, m_centroid, out);
		return out;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy