I am doing something wrong when i try get type of object.
I am trying to sum two vectors, it can be in cartesian coordinate (x,y) or polar coordinate (azimuth, length).
How i can check what type of object i have?
Here is my code:
import java.lang.Math;
/**
* Write a description of class Velocity here.
*
* #author (your name)
* #version (a version number or a date)
*/
public class Velocity
{
// instance variables - replace the example below with your own
private double x;
private double y;
private double azimuth;
private double length;
private double prod;
private PolarCoordinate pt;
private CartesianCoordinate ct;
private boolean compare;
private double s;
/**
* Constructor for objects of class Velocity
*/
public Velocity(CartesianCoordinate c)
{
x = c.getX();
y = c.getY();
}
public Velocity(Velocity z)
{
}
public Velocity(PolarCoordinate p)
{
ct = new CartesianCoordinate(x, y);
pt = new PolarCoordinate(azimuth, length);
azimuth = p.getAzimuth();
length = p.getLength();
}
private Velocity sumOfPolar(double s)
{
this.s = s;
return null;
}
private Velocity PolarCoordinate(double azimuth, double length)
{
return null;
}
private Velocity CartesianCoordinate(double x, double y)
{
return null;
}
private Velocity dotProduct(double prod)
{
return null;
}
//private boolean compare(java.lang.Object velocity,java.lang.Object ct)
//{
// if (velocity.getClass().equals(ct.getClass())) {
// return true;
// } else {
// return false;
// }
//}
/**
* This method performs a vector addition
* and returns a new object representing the
* sum of two vectors.
*
*/
public Velocity add(final Velocity velocity)
{
if(velocity.getClass().equals(ct.getClass()))
{
double sumX = x + velocity.x;
double sumY = y + velocity.y;
Velocity v = new Velocity(CartesianCoordinate(x,y));
v.x = sumX;
v.y = sumY;
System.out.println("BLYABLYA");
return v;
}
if(compare == false)
{
System.out.println("YEAAAAA");
Velocity v = new Velocity(PolarCoordinate(azimuth, length));
double xFirst = length * Math.cos(azimuth);
System.out.println("xFirst: " + xFirst);
double xSecond = velocity.length * Math.cos(velocity.azimuth);
System.out.println("xSecond: " + xSecond);
double yFirst = length * Math.sin(azimuth);
System.out.println("yFirst: " + yFirst);
double ySecond = velocity.length * Math.sin(velocity.azimuth);
System.out.println("ySecond: " + ySecond);
double sumX = xFirst + xSecond;
System.out.println("sumX: " + sumX);
double sumY = yFirst + ySecond;
System.out.println("sumY: " + sumY);
double sumXY = sumX + sumY;
System.out.println("sumXY: " + sumXY);
Velocity sum = new Velocity(sumOfPolar(sumXY));
sum.s = sumXY;
return sum;
}
return null;
}
/**
* This method performs a vector subtraction
* and returns a new object representing the
* sub of two vectors.
*
*/
public Velocity subtarct(final Velocity velocity)
{
double subX = x - velocity.x;
double subY = y - velocity.y;
Velocity v = new Velocity(CartesianCoordinate(x,y));
v.x = subX;
v.y = subY;
return v;
}
/**
* This method performs a vector dot product
* and returns a new object representing the
* product of two vectors.
*
*/
public Velocity dotProduct(final Velocity velocity)
{
double prodX = x * velocity.x;
double prodY = y * velocity.y;
double sumProdXY = prodX + prodY;
Velocity v = new Velocity(dotProduct(prod));
v.prod = sumProdXY;
return v;
}
/**
* This method performs a scaling on a vector.
*
*/
public Velocity scale(final double scaleFactor)
{
double prodX = x * scaleFactor;
double prodY = y * scaleFactor;
Velocity v = new Velocity(CartesianCoordinate(x, y));
v.x = prodX;
v.y = prodY;
return v;
}
public double getAzimuth()
{
PolarCoordinate p = new PolarCoordinate(azimuth,length);
return p.getAzimuth();
}
public double getLength()
{
PolarCoordinate p = new PolarCoordinate(azimuth,length);
return p.getLength();
}
}
You can use instanceof to determine the object type.
if (velocity instanceof PolarCoordinate) {
return true;
} else {
return false;
}
use instanceof.
String s = "TGTGGQCC";
System.out.println(s instanceof String); // true
Related
I am attempting to simulate a sphere, and shade it realistically given an origin vector for the light, and the sphere being centered around the origin. Moreover, the light's vector is the normal vector on a larger invisible sphere at a chosen point. The sphere looks off.
https://imgur.com/a/IDIwQQF
The problem, is that it is very difficult to bug fix this kind of program. Especially considering that I know how I want it to look in my head, but when looking at the numbers in my program there is very little meaning attached to them.
Since I don't know where the issue is, I'm forced to paste all of it here.
public class SphereDrawing extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ADJ = 320;
private static final double LIGHT_SPHERE_RADIUS = 5;
private static final double LIGHT_X = 3;
private static final double LIGHT_Y = 4;
private static final double LIGHT_Z = 0;
private static final double DRAWN_SPHERE_RADIUS = 1;
private static final int POINT_COUNT = 1000000;
private static Coord[] points;
private static final double SCALE = 200;
public SphereDrawing() {
setPreferredSize(new Dimension(640, 640));
setBackground(Color.white);
points = new Coord[POINT_COUNT];
initializePoints();
for (int i = 0; i < points.length; i++) {
points[i].scale();
}
new Timer(17, (ActionEvent e) -> {
repaint();
}).start();
}
public void initializePoints() { //finding the points on the surface of the sphere (hopefully somewhat equidistant)
double random = Math.random() * (double)POINT_COUNT;
double offset = 2/(double)POINT_COUNT;
double increment = Math.PI * (3 - Math.sqrt(5));
for (int i = 0; i < POINT_COUNT; i++) {
double y = ((i * offset) - 1) + (offset / 2);
double r = Math.sqrt(1 - Math.pow(y, 2));
double phi = ((i + random) % (double)POINT_COUNT) * increment;
double x = Math.cos(phi) * r;
double z = Math.sin(phi) * r;
points[i] = new Coord(x, y, z);
}
}
public void drawSphere(Graphics2D g) {
g.translate(ADJ, ADJ); //shifting from origin for drawing purposes
Arrays.sort(points); //sorting points by their z coordinates
double iHat = -2 * LIGHT_X;
double jHat = -2 * LIGHT_Y; //Light vector
double kHat = -2 * LIGHT_Z;
double angL1 = 0;
if (Math.abs(iHat) != 0.0)
angL1 = Math.atan(jHat / iHat); //converting light vector to spherical coordinates
else
angL1 = Math.PI/2;
double angL2 = Math.atan(Math.sqrt(Math.pow(iHat, 2) + Math.pow(jHat, 2))/ kHat);
double maxArcLength = LIGHT_SPHERE_RADIUS * Math.PI; // maximum arc length
for (int i = 0; i < points.length; i++) {
if(points[i].checkValid()) {
double siHat = -2 * points[i].x;
double sjHat = -2 * points[i].y; //finding normal vector for the given point on the sphere
double skHat = -2 * points[i].z;
double angSF1 = -1 * Math.abs(Math.atan(sjHat / siHat)); // converting vector to spherical coordinates
double angSF2 = Math.atan(Math.sqrt(Math.pow(siHat, 2) + Math.pow(sjHat, 2))/ skHat);
double actArcLength = LIGHT_SPHERE_RADIUS * Math.acos(Math.cos(angL1) * Math.cos(angSF1) + Math.sin(angL1) * Math.sin(angSF1) * Math.cos(angL2 - angSF2)); //calculating arc length at this point
double comp = actArcLength / maxArcLength; // comparing the maximum arc length to the calculated arc length for this vector
int col = (int)(comp * 255);
col = Math.abs(col);
g.setColor(new Color(col, col, col));
double ovalDim = (4 * Math.PI * Math.pow(DRAWN_SPHERE_RADIUS, 2))/POINT_COUNT; //using surface area to determine how large size of each point should be drawn
if (ovalDim < 1) // if it too small, make less small
ovalDim = 2;
g.fillOval((int)points[i].x, (int)points[i].y, (int)ovalDim, (int)ovalDim); //draw this oval
}
}
}
#Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawSphere(g);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Sphere");
f.setResizable(false);
f.add(new SphereDrawing(), BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
#SuppressWarnings("rawtypes")
private class Coord implements Comparable {
public double x;
public double y;
public double z;
public Coord(double x2, double y2, double z2) {
x = x2;
y = y2;
z = z2;
}
public void scale() {
x *= SCALE;
y *= SCALE; //drawing purposes
z *= SCALE;
}
public String toString() {
return x + " " + y + " " + z;
}
public int compareTo(Object c) {
double diff = this.z - ((Coord)c).z;
if (diff < 0)
return -1;
else if (diff > 0) //for sorting the array of points
return 1;
else
return 0;
}
public boolean checkValid() {
return (z > 0); //checks if need to draw this point
}
}
}
I was hoping to at least draw a realistic looking sphere, even if not completely accurate, and I couldn't tell you what exactly is off with mine
In the physics (written following this tutorial) part of my game engine (on github), sometimes collisions between AABBs and circles or between circles and circles stick together, like so (apologies for the gif artifacts):
I've confirmed that this happens even when I don't call applyFriction, so it's not that. I've also made the positionalCorrection algorithm uses 1.05f for percent, but even that doesn't fix anything, so I'm stumped.
public final class Collisions {
private Collisions() {
// cant instantiate this class
}
/**
* Returns whether two GameObjects are colliding
*
* #param a
* must be a RectObject or CircleObject
* #param b
* must be a RectObject or CircleObject
* #return
*/
public static boolean isColliding(final GameEntity a, final GameEntity b) {
final CShape as = a.shape;
final CShape bs = b.shape;
if (as instanceof RectShape && bs instanceof RectShape) {
return isColliding((RectShape) as, (RectShape) bs);
}
if (as instanceof CircleShape && bs instanceof CircleShape) {
return isColliding((CircleShape) as, (CircleShape) bs);
}
if (as instanceof RectShape && bs instanceof CircleShape) {
return isColliding((RectShape) as, (CircleShape) bs);
}
if (as instanceof CircleShape && bs instanceof RectShape) {
return isColliding((RectShape) bs, (CircleShape) as);
}
throw new UnsupportedOperationException();
}
private static boolean isColliding(final RectShape a, final RectShape b) {
return collisionNormal(a, b) != null;
}
private static boolean isColliding(final CircleShape o1, final CircleShape o2) {
final float c = o1.radius + o2.radius;
final float b = o1.center.x - o2.center.x;
final float a = o1.center.y - o2.center.y;
return c * c > b * b + a * a;
}
private static boolean isColliding(final RectShape a, final CircleShape b) {
final float circleDistance_x = Math.abs(b.center().x - (a.min.x + a.width() / 2));
final float circleDistance_y = Math.abs(b.center().y - (a.min.y + a.height() / 2));
if (circleDistance_x > a.width() / 2 + b.radius) {
return false;
}
if (circleDistance_y > a.height() / 2 + b.radius) {
return false;
}
if (circleDistance_x <= a.width() / 2) {
return true;
}
if (circleDistance_y <= a.height() / 2) {
return true;
}
final int cornerDistance_sq = (int) Math.pow(circleDistance_x - a.width() / 2, 2) + (int) Math.pow(circleDistance_y - a.height() / 2, 2);
return cornerDistance_sq <= (int) Math.pow(b.radius, 2);
}
/**
* Returns the face normal of a collision between a and b
*
* #param a
* #param b
* #return null if no collision
*/
private static Vec2D collisionNormal(final RectShape a, final RectShape b) {
final float w = 0.5f * (a.width() + b.width());
final float h = 0.5f * (a.height() + b.height());
final float dx = a.center().x - b.center().x;
final float dy = a.center().y - b.center().y;
if (Math.abs(dx) <= w && Math.abs(dy) <= h) {
/* collision! */
final float wy = w * dy;
final float hx = h * dx;
if (wy > hx) {
if (wy > -hx) {
/* collision at the top */
return new Vec2D(0, -1);
} else {
/* on the left */
return new Vec2D(1, 0);
}
} else {
if (wy > -hx) {
/* on the right */
return new Vec2D(-1, 0);
} else {
/* at the bottom */
return new Vec2D(0, 1);
}
}
}
return null;
}
public static void fixCollision(final GameEntity a, final GameEntity b) {
fixCollision(generateManifold(a, b), true);
}
/**
* Fixes a collision between two objects by correcting their positions and applying impulses.
*
*/
public static void fixCollision(final CManifold m, final boolean applyFriction) {
final GameEntity a = m.a;
final GameEntity b = m.b;
// Calculate relative velocity
final Vec2D rv = b.velocity.minus(a.velocity);
// Calculate relative velocity in terms of the normal direction
final float velAlongNormal = rv.dotProduct(m.normal);
// Calculate restitution
final float e = Math.min(a.getRestitution(), b.getRestitution());
// Calculate impulse scalar
float j = -(1 + e) * velAlongNormal;
j /= a.getInvMass() + b.getInvMass();
// Apply impulse
final Vec2D impulse = m.normal.multiply(j);
a.velocity = a.velocity.minus(impulse.multiply(a.getInvMass()));
b.velocity = b.velocity.plus(impulse.multiply(b.getInvMass()));
if (applyFriction) {
applyFriction(m, j);
}
positionalCorrection(m);
}
private static void applyFriction(final CManifold m, final float normalMagnitude) {
final GameEntity a = m.a;
final GameEntity b = m.b;
// relative velocity
final Vec2D rv = b.velocity.minus(a.velocity);
// normalized tangent force
final Vec2D tangent = rv.minus(m.normal.multiply(m.normal.dotProduct(rv))).unitVector();
// friction magnitude
final float jt = -rv.dotProduct(tangent) / (a.getInvMass() + b.getInvMass());
// friction coefficient
final float mu = (a.getStaticFriction() + b.getStaticFriction()) / 2;
final float dynamicFriction = (a.getDynamicFriction() + b.getDynamicFriction()) / 2;
// Coulomb's law: force of friction <= force along normal * mu
final Vec2D frictionImpulse = Math.abs(jt) < normalMagnitude * mu ? tangent.multiply(jt) : tangent.multiply(-normalMagnitude
* dynamicFriction);
// apply friction
a.velocity = a.velocity.minus(frictionImpulse.multiply(a.getInvMass()));
b.velocity = b.velocity.plus(frictionImpulse.multiply(b.getInvMass()));
}
/**
* Generates a collision manifold from two colliding objects.
*
* #param a
* #param b
* #return
*/
private static CManifold generateManifold(final GameEntity a, final GameEntity b) {
final CManifold m = new CManifold();
m.a = a;
m.b = b;
final CShape as = a.shape;
final CShape bs = b.shape;
if (as instanceof RectShape && bs instanceof RectShape) {
return generateManifold((RectShape) as, (RectShape) bs, m);
} else if (as instanceof CircleShape && bs instanceof CircleShape) {
return generateManifold((CircleShape) as, (CircleShape) bs, m);
} else if (as instanceof RectShape && bs instanceof CircleShape) {
return generateManifold((RectShape) as, (CircleShape) bs, m);
} else if (as instanceof CircleShape && bs instanceof RectShape) {
m.b = a;
m.a = b;
return generateManifold((RectShape) bs, (CircleShape) as, m);
} else {
throw new UnsupportedOperationException();
}
}
private static CManifold generateManifold(final RectShape a, final RectShape b, final CManifold m) {
final Rectangle2D r = a.getRect().createIntersection(b.getRect());
m.normal = collisionNormal(a, b);
// penetration is the min resolving distance
m.penetration = (float) Math.min(r.getWidth(), r.getHeight());
return m;
}
private static CManifold generateManifold(final CircleShape a, final CircleShape b, final CManifold m) {
// A to B
final Vec2D n = b.center.minus(a.center);
final float dist = n.length();
if (dist == 0) {
// circles are on the same position, choose random but consistent values
m.normal = new Vec2D(0, 1);
m.penetration = Math.min(a.radius, b.radius);
return m;
}
// don't recalculate dist to normalize
m.normal = n.divide(dist);
m.penetration = b.radius + a.radius - dist;
return m;
}
private static CManifold generateManifold(final RectShape a, final CircleShape b, final CManifold m) {
// Vector from A to B
final Vec2D n = b.center.minus(a.center());
// Closest point on A to center of B
Vec2D closest = n;
// Calculate half extents along each axis
final float x_extent = a.width() / 2;
final float y_extent = a.height() / 2;
// Clamp point to edges of the AABB
closest = new Vec2D(clamp(closest.x, -x_extent, x_extent), clamp(closest.y, -y_extent, y_extent));
boolean inside = false;
// Circle is inside the AABB, so we need to clamp the circle's center
// to the closest edge
if (n.equals(closest)) {
inside = true;
// Find closest axis
if (Math.abs(closest.x) > Math.abs(closest.y)) {
// Clamp to closest extent
closest = new Vec2D(closest.x > 0 ? x_extent : -x_extent, closest.y);
}
// y axis is shorter
else {
// Clamp to closest extent
closest = new Vec2D(closest.x, closest.y > 0 ? y_extent : -y_extent);
}
}
// closest point to center of the circle
final Vec2D normal = n.minus(closest);
final float d = normal.length();
final float r = b.radius;
// Collision normal needs to be flipped to point outside if circle was
// inside the AABB
m.normal = inside ? normal.unitVector().multiply(-1) : normal.unitVector();
m.penetration = r - d;
return m;
}
private static float clamp(final float n, final float lower, final float upper) {
return Math.max(lower, Math.min(n, upper));
}
/**
* Corrects positions between two colliding objects to avoid "sinking."
*
* #param m
*/
private static void positionalCorrection(final CManifold m) {
final GameEntity a = m.a;
final GameEntity b = m.b;
// the amount to correct by
final float percent = 1f; // usually .2 to .8
// the amount in which we don't really care, this avoids vibrating objects.
final float slop = 0.05f; // usually 0.01 to 0.1
final float correctionMag = m.penetration + (m.penetration > 0 ? -slop : slop);
final Vec2D correction = m.normal.multiply(correctionMag / (a.getInvMass() + b.getInvMass()) * percent);
a.moveRelative(correction.multiply(-1 * a.getInvMass()));
b.moveRelative(correction.multiply(b.getInvMass()));
}
}
Update:
I've fixed the problem so that circles don't stick to other circles anymore by making it so that I only check and fix between two objects once per tick (and not possible twice), but circles still sometimes stick to rectangles and can still ride underneath them.
I fixed the problem. I just had to change my calculations a bit in my fixCollision method.
This is a link to the commit that fixed the problem, if you're curious.
I'm getting unexpected results when multiplying two quaternions and applying the resulting rotation to my local right-handed coordinate system. (X pointing forward, Y to the right and Z downward).
(See my Java SCCE below)
So I am trying to first apply a Z rotation by 90 degrees (yaw) and then a rotation of 90 degrees around the local X axis (roll).
I am trying to accomplish this by multiplying two quaternions representing these two rotations, creating a rotation Matrix from the result and applying it to the 3 unit vectors of my coordinate system but the results I am getting do not make sense. (i.e. they do not represent the coordinate system you should get from these two rotations.)
I have tried changing the quaternion multiplication order which did not help (see code lines that were commented out in the main method of the SCCE).
I have also tried creating the quaternion for the second rotation from global Y to simulate that it was created from the resulting local coordinate system after the first rotation.
For reference I am also calculating the result by applying the two individual rotation matrices (which works as expected).
What am I doing wrong?
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class Quaternion {
public static final double NORMALIZATION_LOWER_TOLERANCE = 1 - 1e-4;
public static final double NORMALIZATION_UPPER_TOLERANCE = 1 + 1e-4;
private double w = 1.0;
private double x = 0.0;
private double y = 0.0;
private double z = 0.0;
public static void main(String[] args) {
Vector3D xVect = new Vector3D(1,0,0);
Vector3D yVect = new Vector3D(0,1,0);
Vector3D zVect = new Vector3D(0,0,1);
System.out.println("Initial Local Coordinate System: X:"+xVect+" / Y:"+yVect+ " / Z:"+zVect);
Quaternion rotZ = new Quaternion(Math.PI/2, zVect); // Yaw +90 deg
Quaternion rotY = new Quaternion(Math.PI/2, yVect); // Yaw +90 deg
Quaternion rotX = new Quaternion(Math.PI/2, xVect); // Then roll +90 deg
Matrix rotationMatrixZ = new Matrix(rotZ);
Vector3D localX = xVect.rotate(rotationMatrixZ);
Vector3D localY = yVect.rotate(rotationMatrixZ);
Vector3D localZ = zVect.rotate(rotationMatrixZ);
System.out.println("New Local Coordinate System after Yaw: X:"+localX+" / Y:"+localY+ " / Z:"+localZ); // Gives expected result
Quaternion localRotX = new Quaternion(Math.PI/2, localX);
Matrix localRotXMatrix = new Matrix(localRotX);
Vector3D rotatedX = localX.rotate(localRotXMatrix);
Vector3D rotatedY = localY.rotate(localRotXMatrix);
Vector3D rotatedZ = localZ.rotate(localRotXMatrix);
System.out.println("New Local Coordinate System two local rotations: X:"+rotatedX+" / Y:"+rotatedY+ " / Z:"+rotatedZ); // Gives expected result
Quaternion rotZX = rotZ.multiply(rotX);
// Quaternion rotZX = rotX.multiply(rotZ); // Tried both orders
// Quaternion rotZX = rotZ.multiply(rotY); // rotY is in fact the local rotX
// Quaternion rotZX = rotZ.multiply(rotY); // rotY is in fact the local rotX, tried both orders
rotZX.normalizeIfNeeded();
Matrix rotationXMatrixZX = new Matrix(rotZX);
rotatedX = xVect.rotate(rotationXMatrixZX);
rotatedY = localY.rotate(rotationXMatrixZX);
rotatedZ = localZ.rotate(rotationXMatrixZX);
System.out.println("New Local Coordinate System Quaternion Multiplication: X:"+rotatedX+" / Y:"+rotatedY+ " / Z:"+rotatedZ); // Expect same as above
}
public Quaternion() {
}
public Quaternion(double w, double x, double y, double z) {
this.w = w;
this.x = x;
this.y = y;
this.z = z;
}
public Quaternion(double angle, Vector3D vector){
double halfAngle = angle / 2;
double sin = Math.sin(halfAngle);
this.w = Math.cos(halfAngle);
this.x = vector.getX()*sin;
this.y = vector.getY()*sin;
this.z = vector.getZ()*sin;
}
public boolean normalizeIfNeeded() {
double sum = w * w + x * x + y * y + z * z;
if (NORMALIZATION_LOWER_TOLERANCE < sum && sum < NORMALIZATION_UPPER_TOLERANCE) {
return false;
}
double magnitude = Math.sqrt(sum);
w /= magnitude;
x /= magnitude;
y /= magnitude;
z /= magnitude;
return true;
}
public Quaternion multiply(Quaternion q2) {
Quaternion result = new Quaternion();
result.w = w * q2.w - x * q2.x - y * q2.y - z * q2.z;
result.x = w * q2.x + x * q2.w + y * q2.z - z * q2.y;
result.y = w * q2.y - x * q2.z + y * q2.w + z * q2.x;
result.z = w * q2.z + x * q2.y - y * q2.x + z * q2.w;
return result;
}
public Quaternion conjugate() {
return new Quaternion(w, -x, -y, -z);
}
public double getW() {
return w;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getZ() {
return z;
}
#Override
public String toString() {
return "Quaternion [w=" + w + ", x=" + x + ", y=" + y + ", z=" + z + "]";
}
static class Vector3D {
double x=0;
double y=0;
double z=0;
public Vector3D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3D rotate(Matrix rotationMatrix){
return rotationMatrix.multiply(this);
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getZ() {
return z;
}
#Override
public String toString() {
NumberFormat df = DecimalFormat.getNumberInstance();
return "[x=" + df.format(x) + ", y=" + df.format(y) + ", z=" + df.format(z) + "]";
}
}
static class Matrix {
private double[][] values;
public Matrix(int rowCount, int colCount) {
values = new double[rowCount][colCount];
}
public Matrix(Quaternion quaternionForRotationMatrix) {
this(3,3);
double w = quaternionForRotationMatrix.getW();
double x = quaternionForRotationMatrix.getX();
double y = quaternionForRotationMatrix.getY();
double z = quaternionForRotationMatrix.getZ();
double ww = w*w;
double wx = w*x;
double xx = x*x;
double xy = x*y;
double xz = x*z;
double wy = w*y;
double yy = y*y;
double yz = y*z;
double wz = w*z;
double zz = z*z;
values[0][0] = ww + xx - yy - zz;
values[0][1] = 2 * xy - 2 * wz;
values[0][2] = 2 * xz + 2 * wy;
values[1][0] = 2 * xy + 2 * wz;
values[1][1] = ww - xx + yy - zz;
values[1][2] = 2 * yz + 2 * wx;
values[2][0] = 2 * xz - 2 * wy;
values[2][1] = 2 * yz - 2 * wx;
values[2][2] = ww - xx - yy + zz;
}
public Vector3D multiply(Vector3D vector){
double [][] vect = new double [3][1];
vect[0][0] = vector.getX();
vect[1][0] = vector.getY();
vect[2][0] = vector.getZ();
double [][] result = multiplyMatrices(values, vect);
return new Vector3D(result[0][0], result[1][0], result[2][0]);
}
private double[][] multiplyMatrices(double[][] m1, double[][] m2) {
double[][] result = null;
if (m1[0].length == m2.length) {
int rowCount1 = m1.length;
int colCount1 = m1[0].length;
int rowCount2 = m2[0].length;
result = new double[rowCount1][rowCount2];
for (int i = 0; i < rowCount1; i++) {
for (int j = 0; j < rowCount2; j++) {
result[i][j] = 0;
for (int k = 0; k < colCount1; k++) {
result[i][j] += m1[i][k] * m2[k][j];
}
}
}
} else {
int rowCount = m1.length;
int colCount = m1[0].length;
result = new double[rowCount][colCount];
for (int i = 0; i < m1.length; i++) {
for (int j = 0; j < m1[0].length; j++) {
result[i][j] = 0;
}
}
}
return result;
}
#Override
public String toString() {
StringBuffer sb = new StringBuffer("Matrix = ");
for(int row = 0 ; row<values.length; row++){
sb.append ("[ ");
for(int col = 0 ; col<values[0].length; col++){
sb.append(Double.toString(values[row][col]));
if(col<values.length-1){
sb.append(" | ");
}
}
sb.append("] ");
}
return sb.toString();
}
}
}
Nevermind. Found it. I had an error in the formulas to build the rotation matrix. It now works as expected.
I am making a mental note to use formulas from Wikipedia in the future and not some random other site.
The respective part should be
values[0][0] = ww + xx - yy - zz;
values[0][1] = 2 * xy - 2 * wz;
values[0][2] = 2 * xz + 2 * wy;
values[1][0] = 2 * xy + 2 * wz;
values[1][1] = ww - xx + yy - zz;
values[1][2] = 2 * yz - 2 * wx; //CORRECTED SIGN
values[2][0] = 2 * xz - 2 * wy;
values[2][1] = 2 * yz + 2 * wx; //CORRECTED SIGN
values[2][2] = ww - xx - yy + zz;
At the end of the main method I was also using the wrong vectors for y and z:
Matrix rotationXMatrixZX = new Matrix(rotZX);
rotatedX = xVect.rotate(rotationXMatrixZX);
rotatedY = yVect.rotate(rotationXMatrixZX); // Corrected used y-vector
rotatedZ = zVect.rotate(rotationXMatrixZX); // Corrected used z-vector
What I'm trying to do is come up with a way to generate n random points on a graph (displaying it isn't necessary). A point is randomly selected and connected to the point closest to it (or the next closest if it's already connected to the best option) in a way so that no two lines intersect. This repeats until no more connections are possible. The vertices are meant to represent regions on a map, and connections represent adjacency. The following code I have thus far is as follows, taken from http://javaingrab.blogspot.com/2012/12/m-way-graph-coloring-with-backtracking.html:
public class MWayGrColor{
/*G is graph's adjacency matrix and x is solution vector */
private int G[][],x[],n,m,soln;
public void mColoring(int k){ //backtracking function
for(int i=1;i<=n;i++){
next_color(k); //coloring kth vertex
if(x[k]==0)
return; //if unsuccessful then backtrack
if(k==n) //if all colored then show
write();
else
mColoring(k+1); /* successful but still left to color */
}
}
private void next_color(int k){
do{
int i=1;
x[k]=(x[k]+1)%(m+1);
if(x[k]==0)
return;
for(i=1;i<=n;i++)
if(G[i][k]!=0 && x[k]==x[i]) /* checking adjacency and not same color */
break;
if(i==n+1) return; //new color found
}while(true);
}
private void write(){
System.out.print("\nColoring(V C) # "+(++soln)+"-->");
for(int i=1;i<=n;i++)
System.out.print("\t("+i+" "+x[i]+")"); //solution vector
}
public void input(){
java.util.Scanner sc=new java.util.Scanner(System.in);
System.out.print("Enter no. of vertices : ");
n=sc.nextInt();
G=new int[n+1][n+1];
x=new int[n+1];
System.out.print("Enter no. of colors : ");
m=sc.nextInt();
System.out.println("Enter adjacency matrix-->");
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
G[i][j]=sc.nextInt();
}
public static void main (String[] args) {
MWayGrColor obj=new MWayGrColor();
obj.input();
obj.mColoring(1);
if(obj.soln==0)
System.out.println("\nNeed more than "+obj.m+" colors");
else
System.out.print("\nTOTAL SOLN : "+obj.soln);
}
}
As noted, the map doesn't need to be visually represented, as the current method of display is adequate. I'm aware of the Point2D.Double class and the Line2D class, and I was originally going to just start generating points and use the lines to create the adjacency matrix already shown in the code, but the methods for connecting points and avoiding repetition are extremely confusing to me in how they should be implemented. How can I accomplish this generation of an adjacency matrix?
It's still not clear what the actual question is. It sounds like "this is so complicated, I don't get it done". However, unless there are strict requirements about the approach and its running time etc., one can pragmatically write down what has to be done:
do
{
V v0 = randomVertex();
V v1 = findClosestUnconnected(v0);
if (line(v0,v1).intersectsNoOtherLine())
{
insert(line(v0,v1));
}
} while (insertedNewLine);
Of course, this implies some searching. For large graphs there may be some sophisticated data structures to accelerate this. Particularly the search for the nearest (unconnected) neighbor may be accelerated with the classical structures like KD-trees etc. But this seems to be unrelated to the original question.
The handing of the adjacency matrix can be made a bit more convenient with a wrapper that offers methods that allow a more "natural" description:
class Graph
{
private final boolean matrix[][];
void addEdge(V v0, V v1)
{
matrix[v0.index][v1.index] = true;
matrix[v1.index][v0.index] = true;
}
boolean hasEdge(V v0, V v1)
{
return matrix[v0.index][v1.index];
}
}
But in this case, this is only a minor, syntactical simplification.
An example, only as a VERY q&d sketch:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class NonIntersectingAdjacencies
{
public static void main(String[] args)
{
Random random = new Random(0);
int numVertices = 25;
List<AdjacencyVertex> vertices =
createRandomVertices(numVertices, random);
final AdjacencyGraph adjacencyGraph =
new AdjacencyGraph(vertices);
boolean createdNewLine = true;
while (createdNewLine)
{
createdNewLine = false;
List<Integer> indices =
createShuffledList(numVertices, random);
for (int i=0; i<numVertices; i++)
{
int randomIndex = indices.get(i);
AdjacencyVertex randomVertex = vertices.get(randomIndex);
AdjacencyVertex closest =
findClosestUnconnected(randomVertex, adjacencyGraph);
if (closest != null)
{
if (!intersectsOtherLine(
randomVertex, closest, adjacencyGraph))
{
adjacencyGraph.addEdge(randomVertex, closest);
createdNewLine = true;
}
}
}
}
AdjacencyGraphPanel.show(adjacencyGraph);
}
private static List<AdjacencyVertex> createRandomVertices(
int numVertices, Random random)
{
List<AdjacencyVertex> vertices = new ArrayList<AdjacencyVertex>();
for (int i=0; i<numVertices; i++)
{
AdjacencyVertex v = new AdjacencyVertex();
v.index = i;
v.x = random.nextDouble();
v.y = random.nextDouble();
vertices.add(v);
}
return vertices;
}
private static List<Integer> createShuffledList(
int maxValue, Random random)
{
List<Integer> list = new ArrayList<Integer>();
for (int i=0; i<maxValue; i++)
{
list.add(i);
}
Collections.shuffle(list, random);
return list;
}
private static boolean intersectsOtherLine(
AdjacencyVertex v0, AdjacencyVertex v1,
AdjacencyGraph adjacencyGraph)
{
Line2D newLine = new Line2D.Double(
v0.x, v0.y, v1.x, v1.y);
List<AdjacencyVertex> vertices = adjacencyGraph.getVertices();
for (int i=0; i<vertices.size(); i++)
{
for (int j=0; j<vertices.size(); j++)
{
if (i == j)
{
continue;
}
AdjacencyVertex oldV0 = vertices.get(i);
AdjacencyVertex oldV1 = vertices.get(j);
if (adjacencyGraph.hasEdge(oldV0, oldV1))
{
Line2D oldLine = new Line2D.Double(
oldV0.x, oldV0.y, oldV1.x, oldV1.y);
if (Intersection.intersect(oldLine, newLine))
{
return true;
}
}
}
}
return false;
}
private static AdjacencyVertex findClosestUnconnected(
AdjacencyVertex v,
AdjacencyGraph adjacencyGraph)
{
double minDistanceSquared = Double.MAX_VALUE;
AdjacencyVertex closest = null;
List<AdjacencyVertex> vertices = adjacencyGraph.getVertices();
for (int i=0; i<vertices.size(); i++)
{
AdjacencyVertex other = vertices.get(i);
if (other.index == v.index)
{
continue;
}
if (adjacencyGraph.hasEdge(v, other))
{
continue;
}
double dx = other.x - v.x;
double dy = other.y - v.y;
double distanceSquared = Math.hypot(dx, dy);
if (distanceSquared < minDistanceSquared)
{
minDistanceSquared = distanceSquared;
closest = other;
}
}
return closest;
}
}
class AdjacencyVertex
{
double x;
double y;
int index;
}
class AdjacencyGraph
{
private final boolean matrix[][];
private final List<AdjacencyVertex> vertices;
AdjacencyGraph(List<AdjacencyVertex> vertices)
{
this.vertices = vertices;
this.matrix = new boolean[vertices.size()][vertices.size()];
}
List<AdjacencyVertex> getVertices()
{
return vertices;
}
void addEdge(AdjacencyVertex v0, AdjacencyVertex v1)
{
matrix[v0.index][v1.index] = true;
matrix[v1.index][v0.index] = true;
}
boolean hasEdge(AdjacencyVertex v0, AdjacencyVertex v1)
{
return matrix[v0.index][v1.index];
}
}
//============================================================================
// Only helper stuff below this line...
class AdjacencyGraphPanel extends JPanel
{
public static void show(final AdjacencyGraph adjacencyGraph)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI(adjacencyGraph);
}
});
}
private static void createAndShowGUI(AdjacencyGraph adjacencyGraph)
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new AdjacencyGraphPanel(adjacencyGraph));
f.setSize(600,600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private final AdjacencyGraph adjacencyGraph;
public AdjacencyGraphPanel(AdjacencyGraph adjacencyGraph)
{
this.adjacencyGraph = adjacencyGraph;
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
int offsetX = 30;
int offsetY = 30;
int w = getWidth() - offsetX - offsetX;
int h = getHeight() - offsetY - offsetY;
g.setColor(Color.BLACK);
List<AdjacencyVertex> vertices = adjacencyGraph.getVertices();
for (int i=0; i<vertices.size(); i++)
{
for (int j=0; j<vertices.size(); j++)
{
if (i == j)
{
continue;
}
AdjacencyVertex v0 = vertices.get(i);
AdjacencyVertex v1 = vertices.get(j);
if (adjacencyGraph.hasEdge(v0, v1))
{
Line2D newLine = new Line2D.Double(
offsetX + v0.x*w,
offsetY + v0.y*h,
offsetX + v1.x*w,
offsetY + v1.y*h);
g.draw(newLine);
}
}
}
g.setColor(Color.BLUE);
for (int i=0; i<vertices.size(); i++)
{
AdjacencyVertex v = vertices.get(i);
int ix = (int)(offsetX + v.x * w);
int iy = (int)(offsetY + v.y * h);
g.fill(new Ellipse2D.Double(
ix - 5, iy - 5, 10, 10));
g.drawString(String.valueOf(i), ix, iy+16);
}
}
}
class Intersection
{
static boolean intersect(Line2D line0, Line2D line1)
{
Point2D location = new Point2D.Double();
Point2D intersection =
Intersection.computeIntersectionSegmentSegment(
line0, line1, location);
if (intersection == null)
{
return false;
}
return !isAtLineAnd(location);
}
private static boolean isAtLineAnd(Point2D location)
{
double EPSILON = 0.05;
if (Math.abs(location.getX()) < EPSILON)
{
return true;
}
if (Math.abs(location.getX()-1) < EPSILON)
{
return true;
}
if (Math.abs(location.getY()) < EPSILON)
{
return true;
}
if (Math.abs(location.getY()-1) < EPSILON)
{
return true;
}
return false;
}
/**
* Epsilon for floating point computations
*/
private static final double epsilon = 1e-6f;
/**
* Computes the intersection of the specified line segments and returns
* the intersection point, or <code>null</code> if the line segments do
* not intersect.
*
* #param line0 The first line segment
* #param line1 The second line segment
* #param location Optional location that stores the
* relative location of the intersection point on
* the given line segments
* #return The intersection point, or <code>null</code> if
* there is no intersection.
*/
public static Point2D computeIntersectionSegmentSegment(
Line2D line0, Line2D line1, Point2D location)
{
return computeIntersectionSegmentSegment(
line0.getX1(), line0.getY1(), line0.getX2(), line0.getY2(),
line1.getX1(), line1.getY1(), line1.getX2(), line1.getY2(),
location);
}
/**
* Computes the intersection of the specified line segments and returns
* the intersection point, or <code>null</code> if the line segments do
* not intersect.
*
* #param s0x0 x-coordinate of point 0 of line segment 0
* #param s0y0 y-coordinate of point 0 of line segment 0
* #param s0x1 x-coordinate of point 1 of line segment 0
* #param s0y1 y-coordinate of point 1 of line segment 0
* #param s1x0 x-coordinate of point 0 of line segment 1
* #param s1y0 y-coordinate of point 0 of line segment 1
* #param s1x1 x-coordinate of point 1 of line segment 1
* #param s1y1 y-coordinate of point 1 of line segment 1
* #param location Optional location that stores the
* relative location of the intersection point on
* the given line segments
* #return The intersection point, or <code>null</code> if
* there is no intersection.
*/
public static Point2D computeIntersectionSegmentSegment(
double s0x0, double s0y0,
double s0x1, double s0y1,
double s1x0, double s1y0,
double s1x1, double s1y1,
Point2D location)
{
if (location == null)
{
location = new Point2D.Double();
}
Point2D result = computeIntersectionLineLine(
s0x0, s0y0, s0x1, s0y1, s1x0, s1y0, s1x1, s1y1, location);
if (location.getX() >= 0 && location.getX() <= 1.0 &&
location.getY() >= 0 && location.getY() <= 1.0)
{
return result;
}
return null;
}
/**
* Computes the intersection of the specified lines and returns the
* intersection point, or <code>null</code> if the lines do not
* intersect.
*
* Ported from
* http://www.geometrictools.com/LibMathematics/Intersection/
* Wm5IntrSegment2Segment2.cpp
*
* #param s0x0 x-coordinate of point 0 of line segment 0
* #param s0y0 y-coordinate of point 0 of line segment 0
* #param s0x1 x-coordinate of point 1 of line segment 0
* #param s0y1 y-coordinate of point 1 of line segment 0
* #param s1x0 x-coordinate of point 0 of line segment 1
* #param s1y0 y-coordinate of point 0 of line segment 1
* #param s1x1 x-coordinate of point 1 of line segment 1
* #param s1y1 y-coordinate of point 1 of line segment 1
* #param location Optional location that stores the
* relative location of the intersection point on
* the given line segments
* #return The intersection point, or <code>null</code> if
* there is no intersection.
*/
public static Point2D computeIntersectionLineLine(
double s0x0, double s0y0,
double s0x1, double s0y1,
double s1x0, double s1y0,
double s1x1, double s1y1,
Point2D location)
{
double dx0 = s0x1 - s0x0;
double dy0 = s0y1 - s0y0;
double dx1 = s1x1 - s1x0;
double dy1 = s1y1 - s1y0;
double len0 = Math.sqrt(dx0*dx0+dy0*dy0);
double len1 = Math.sqrt(dx1*dx1+dy1*dy1);
double dir0x = dx0 / len0;
double dir0y = dy0 / len0;
double dir1x = dx1 / len1;
double dir1y = dy1 / len1;
double c0x = s0x0 + dx0 * 0.5;
double c0y = s0y0 + dy0 * 0.5;
double c1x = s1x0 + dx1 * 0.5;
double c1y = s1y0 + dy1 * 0.5;
double cdx = c1x - c0x;
double cdy = c1y - c0y;
double dot = dotPerp(dir0x, dir0y, dir1x, dir1y);
if (Math.abs(dot) > epsilon)
{
double dot0 = dotPerp(cdx, cdy, dir0x, dir0y);
double dot1 = dotPerp(cdx, cdy, dir1x, dir1y);
double invDot = 1.0/dot;
double s0 = dot1*invDot;
double s1 = dot0*invDot;
if (location != null)
{
double n0 = (s0 / len0) + 0.5;
double n1 = (s1 / len1) + 0.5;
location.setLocation(n0, n1);
}
double x = c0x + s0 * dir0x;
double y = c0y + s0 * dir0y;
return new Point2D.Double(x,y);
}
return null;
}
/**
* Returns the perpendicular dot product, i.e. the length
* of the vector (x0,y0,0)x(x1,y1,0).
*
* #param x0 Coordinate x0
* #param y0 Coordinate y0
* #param x1 Coordinate x1
* #param y1 Coordinate y1
* #return The length of the cross product vector
*/
private static double dotPerp(double x0, double y0, double x1, double y1)
{
return x0*y1 - y0*x1;
}
}
I want to compute the moment of inertia of a (2D) concave polygon. I found this on the internet. But I'm not very sure how to interpret the formula...
Formula http://img101.imageshack.us/img101/8141/92175941c14cadeeb956d8f.gif
1) Is this formula correct?
2) If so, is my convertion to C++ correct?
float sum (0);
for (int i = 0; i < N; i++) // N = number of vertices
{
int j = (i + 1) % N;
sum += (p[j].y - p[i].y) * (p[j].x + p[i].x) * (pow(p[j].x, 2) + pow(p[i].x, 2)) - (p[j].x - p[i].x) * (p[j].y + p[i].y) * (pow(p[j].y, 2) + pow(p[i].y, 2));
}
float inertia = (1.f / 12.f * sum) * density;
Martijn
#include <math.h> //for abs
float dot (vec a, vec b) {
return (a.x*b.x + a.y*b.y);
}
float lengthcross (vec a, vec b) {
return (abs(a.x*b.y - a.y*b.x));
}
...
do stuff
...
float sum1=0;
float sum2=0;
for (int n=0;n<N;++n) { //equivalent of the Σ
sum1 += lengthcross(P[n+1],P[n])*
(dot(P[n+1],P[n+1]) + dot(P[n+1],P[n]) + dot(P[n],P[n]));
sum2 += lengthcross(P[n+1],P[n]);
}
return (m/6*sum1/sum2);
Edit: Lots of small math changes
I think you have more work to do that merely translating formulas into code. You need to understand exactly what this formula means.
When you have a 2D polygon, you have three moments of inertia you can calculate relative to a given coordinate system: moment about x, moment about y, and polar moment of inertia. There's a parallel axis theorem that allows you to translate from one coordinate system to another.
Do you know precisely which moment and coordinate system this formula applies to?
Here's some code that might help you, along with a JUnit test to prove that it works:
import java.awt.geom.Point2D;
/**
* PolygonInertiaCalculator
* User: Michael
* Date: Jul 25, 2010
* Time: 9:51:47 AM
*/
public class PolygonInertiaCalculator
{
private static final int MIN_POINTS = 2;
public static double dot(Point2D u, Point2D v)
{
return u.getX()*v.getX() + u.getY()*v.getY();
}
public static double cross(Point2D u, Point2D v)
{
return u.getX()*v.getY() - u.getY()*v.getX();
}
/**
* Calculate moment of inertia about x-axis
* #param poly of 2D points defining a closed polygon
* #return moment of inertia about x-axis
*/
public static double ix(Point2D [] poly)
{
double ix = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double sum = 0.0;
for (int n = 0; n < (poly.length-1); ++n)
{
double twiceArea = poly[n].getX()*poly[n+1].getY() - poly[n+1].getX()*poly[n].getY();
sum += (poly[n].getY()*poly[n].getY() + poly[n].getY()*poly[n+1].getY() + poly[n+1].getY()*poly[n+1].getY())*twiceArea;
}
ix = sum/12.0;
}
return ix;
}
/**
* Calculate moment of inertia about y-axis
* #param poly of 2D points defining a closed polygon
* #return moment of inertia about y-axis
* #link http://en.wikipedia.org/wiki/Second_moment_of_area
*/
public static double iy(Point2D [] poly)
{
double iy = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double sum = 0.0;
for (int n = 0; n < (poly.length-1); ++n)
{
double twiceArea = poly[n].getX()*poly[n+1].getY() - poly[n+1].getX()*poly[n].getY();
sum += (poly[n].getX()*poly[n].getX() + poly[n].getX()*poly[n+1].getX() + poly[n+1].getX()*poly[n+1].getX())*twiceArea;
}
iy = sum/12.0;
}
return iy;
}
/**
* Calculate polar moment of inertia xy
* #param poly of 2D points defining a closed polygon
* #return polar moment of inertia xy
* #link http://en.wikipedia.org/wiki/Second_moment_of_area
*/
public static double ixy(Point2D [] poly)
{
double ixy = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double sum = 0.0;
for (int n = 0; n < (poly.length-1); ++n)
{
double twiceArea = poly[n].getX()*poly[n+1].getY() - poly[n+1].getX()*poly[n].getY();
sum += (poly[n].getX()*poly[n+1].getY() + 2.0*poly[n].getX()*poly[n].getY() + 2.0*poly[n+1].getX()*poly[n+1].getY() + poly[n+1].getX()*poly[n].getY())*twiceArea;
}
ixy = sum/24.0;
}
return ixy;
}
/**
* Calculate the moment of inertia of a 2D concave polygon
* #param poly array of 2D points defining the perimeter of the polygon
* #return moment of inertia
* #link http://www.physicsforums.com/showthread.php?t=43071
* #link http://www.physicsforums.com/showthread.php?t=25293
* #link http://stackoverflow.com/questions/3329383/help-me-with-converting-latex-formula-to-code
*/
public static double inertia(Point2D[] poly)
{
double inertia = 0.0;
if ((poly != null) && (poly.length > MIN_POINTS))
{
double numer = 0.0;
double denom = 0.0;
double scale;
double mag;
for (int n = 0; n < (poly.length-1); ++n)
{
scale = dot(poly[n + 1], poly[n + 1]) + dot(poly[n + 1], poly[n]) + dot(poly[n], poly[n]);
mag = Math.sqrt(cross(poly[n], poly[n+1]));
numer += mag * scale;
denom += mag;
}
inertia = numer / denom / 6.0;
}
return inertia;
}
}
Here's the JUnit test to accompany it:
import org.junit.Test;
import java.awt.geom.Point2D;
import static org.junit.Assert.assertEquals;
/**
* PolygonInertiaCalculatorTest
* User: Michael
* Date: Jul 25, 2010
* Time: 10:16:04 AM
*/
public class PolygonInertiaCalculatorTest
{
#Test
public void testTriangle()
{
Point2D[] poly =
{
new Point2D.Double(0.0, 0.0),
new Point2D.Double(1.0, 0.0),
new Point2D.Double(0.0, 1.0)
};
// Moment of inertia about the y1 axis
// http://www.efunda.com/math/areas/triangle.cfm
double expected = 1.0/3.0;
double actual = PolygonInertiaCalculator.inertia(poly);
assertEquals(expected, actual, 1.0e-6);
}
#Test
public void testSquare()
{
Point2D[] poly =
{
new Point2D.Double(0.0, 0.0),
new Point2D.Double(1.0, 0.0),
new Point2D.Double(1.0, 1.0),
new Point2D.Double(0.0, 1.0)
};
// Polar moment of inertia about z axis
// http://www.efunda.com/math/areas/Rectangle.cfm
double expected = 2.0/3.0;
double actual = PolygonInertiaCalculator.inertia(poly);
assertEquals(expected, actual, 1.0e-6);
}
#Test
public void testRectangle()
{
// This gives the moment of inertia about the y axis for a coordinate system
// through the centroid of the rectangle
Point2D[] poly =
{
new Point2D.Double(0.0, 0.0),
new Point2D.Double(4.0, 0.0),
new Point2D.Double(4.0, 1.0),
new Point2D.Double(0.0, 1.0)
};
double expected = 5.0 + 2.0/3.0;
double actual = PolygonInertiaCalculator.inertia(poly);
assertEquals(expected, actual, 1.0e-6);
double ix = PolygonInertiaCalculator.ix(poly);
double iy = PolygonInertiaCalculator.iy(poly);
double ixy = PolygonInertiaCalculator.ixy(poly);
assertEquals(ix, (1.0 + 1.0/3.0), 1.0e-6);
assertEquals(iy, (21.0 + 1.0/3.0), 1.0e-6);
assertEquals(ixy, 4.0, 1.0e-6);
}
}
For reference, here's a mutable 2D org.gcs.kinetic.Vector implementation and a more versatile, immutable org.jscience.mathematics.vector implementation. This article on Calculating a 2D Vector’s Cross Product is helpful, too.
I did it with Tesselation. And take the MOI's all together.