org.jbox2d.collision.shapes.PolygonShape Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jbox2d-library Show documentation
Show all versions of jbox2d-library Show documentation
A 2D java physics engine, a port of the C++ Box2d engine. This is the core physics engine.
/*******************************************************************************
* 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.
*
* 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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
e1.set(p1).addLocal(p2).addLocal(p3).mulLocal(triangleArea * inv3);
out.addLocal(e1);
}
// 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;
}
}