So i have a point (x,y,z) and a vector (x1,y1,z1) and i want to rotate the point around the vector in a 3D space. From what i read I should be able to do so using quaternions like this:
(0,new_x,new_y,new_z) = K^-1 * I * K
where K = (cos(fi/2), sin(fi/2)*(x1,y1,z1)) (where (x1,y1,z1) is a normalized vector)
I = (0,(x,y,z))
K^-1 = (cos(fi/2), -sin(fi/2)*(x1,y1,z1))
I implemented it like this:
Point3D n = new Point3D(x1,y1,z1);
n=n.normalize();
double a=Math.cos(Math.toRadians(45)); //fi is 90
double b= Math.sin(Math.toRadians(45));
double k_a = a;
double k_b = b*n.getX();
double k_c=b*n.getY();
double k_d = b*n.getZ(); //K points
double k_a2=k_a; //K^-1 points
double k_b2=-k_b;
double k_c2 = -k_c;
double k_d2= -k_d;
//I*K
double a_m = -((x*k_b)+(y*k_c)+(z*k_d));
double b_m= k_a*x+y*k_d+0*k_b-z*k_c;
double c_m = k_a*y+0*k_c+k_b*z-x*k_d;
double d_m = k_a*z+0*k_d+x*k_c-y*k_b;
//K^-1 * what we got above aka the final coordinates
double a_f = k_a2*a_m -b_m*k_b2-c_m*k_c2-d_m*k_d2; //should and is 0
double x_f= k_a2*b_m+a_m*k_b2+k_c2*d_m-k_d2*c_m;
double y_f = k_a2*c_m+a_m*k_c2+k_b2*d_m-k_d2*b_m;
double z_f = k_a2*d_m+a_m*k_d2+k_b2*c_m-k_c2*b_m;
The problem is, that when i use the above code for a animation (rotating a sphere around a vector), instead of a circle i get a spiral, where the sphere quickly ends up in the same place as the vector:
The move it self is done with a button click for now like this:
btn2.setOnAction(new EventHandler() {
#Override
public void handle(ActionEvent e) {
Point3D n = calc(x,y,z,x1,y1,z1); //a call to the method calculating K^-1*I*K shown above
Sphere sphere= new Sphere(10); //I know, drawing a new one everytime is a waste, but i wanted to be sure the translate wasnt at fault since im new at javaFX
sphere.setMaterial(new PhongMaterial(Color.CORAL));
sphere.setTranslateX(n.getX());
sphere.setTranslateY(n.getY());
sphere.setTranslateZ(n.getZ());
x=n.getX();
y=n.getY();
z=n.getZ();
content.group.getChildren().remove(0);
content.group.getChildren().add(0, sphere);
}
});
I think the problem is in the calculation of the new coordinates, after a bit they end up somewhere on the vector, but after rechecking the math more times than i can count, im officially lost. Can anyone tell me what i am missing or where i went wrong?
Oh nevermind, it was an calculation mistake afterall (though i swear i checked it over a thousand times..)
instead of:
double y_f = k_a2*c_m+a_m*k_c2+k_b2*d_m-k_d2*b_m;
its supposed to be:
double y_f = k_a2*c_m+a_m*k_c2-k_b2*d_m+k_d2*b_m;
Related
I have a list of latitude/longitude pairs in degrees (think turning points in directions) and I want to draw them as lines in a 2D JPanel within limits of the path (not the whole world). Latitude values are +/-90.0 and longitude values are +/-180.0. I don't know how to convert the lat,lon coordinates to an x,y pixel coordinates based on the width and height of a JPanel and scale to the max/min lat/lon.
I found this answer, but it is expecting to map all lat/lon into a given width/height panel. So, if I have a list of lat/lon coordinates that cover around 100 miles, the conversion results in all points being equal pixel coordinates because of the scale. Following the referenced answer, I'd like to know if it is possible to scale the very small values to something that would draw a visible path, and if so, how?
I found other approaches, but they all break when a path transitions over +/-180 or +/-90 (i.e. equator). I really feel like the first answer will work taking care of the edge cases, but only if I can figure out how to scale the results (maybe something like use Path2D.createTransformedShape()?). I'm happy to provide some code of what I've tried, but I think what I'm asking is a higher level yes/no if the approach would work.
Some sample code, after a suggestion from #Mike.
Code from comment.
abstract class Mercator {
final static double RADIUS_MAJOR = 6378137.0;
final static double RADIUS_MINOR = 6356752.3142;
abstract double yAxisProjection(double input);
abstract double xAxisProjection(double input);
}
public class SphericalMercator extends Mercator {
#Override
double xAxisProjection(double input) {
return Math.toRadians(input) * RADIUS_MAJOR;
}
#Override
double yAxisProjection(double input) {
return Math.log(Math.tan(Math.PI / 4 + Math.toRadians(input) / 2)) * RADIUS_MAJOR;
}
}
Driver creating a Path2D with points in CCS with bounds (-20037508.34, -34619289.37, 20037508.34, 34619289.37).
public class ConvertLatLonToXY {
public ConvertLatLonToXY() {
}
public static void main(String[] args) {
List<Double> tLatitudes = new ArrayList<>();
List<Double> tLongitudes = new ArrayList<>();
// zigzag over equator - Indonesia
tLatitudes.add(-0.531011); tLongitudes.add(110.811661);
tLatitudes.add(0.838810); tLongitudes.add(112.176126);
tLatitudes.add(-0.535924); tLongitudes.add(113.541832);
tLatitudes.add(0.824974); tLongitudes.add(115.319110);
Path2D tPath = new Path2D.Double();
Iterator<Double> tLatIter = tLatitudes.iterator();
Iterator<Double> tLonIter = tLongitudes.iterator();
while (tLatIter.hasNext() && tLonIter.hasNext()) {
SphericalMercator tMercator = new SphericalMercator();
double tX = tMercator.xAxisProjection(aLon);
double tY = tMercator.yAxisProjection(aLat);
System.out.println("(x,y) "+tX+", "+tY);
if (null == aPath.getCurrentPoint()) {
tPath.moveTo(tX, tY);
} else {
tPath.lineTo(tX, tY);
}
}
}
}
So the results are:
(x,y) 1.2335497676476853E7, -58717.0086085379
(x,y) 1.2487389225482095E7, 92754.16604443597
(x,y) 1.2639418921975415E7, -59260.28388925838
(x,y) 1.2837264603933502E7, 91224.09692837084
How does one go about changing these values to something that can be visualized in say a 320x240 JPanel?
I'm using polygon on shape renderer and the problem is that the user should be able to add vertex whenever they want, basically the vertices are not set by me but by the user. What I did is whenever the user add a point, I add them to an arrayList.
ArrayList<Float> v = new ArrayList<Float>();
public void ontouch(screenX, screenY){
v.add(screenX);
v.add(screenY)
}
And then I have this problem when I try to render a polygon on a shapeRenderer
for(int i = 0; i < v.size; i++){
float[] vertices = new float[v.size()]
vertices[i - 1] = v.get(i - 1);
vertices[i] = v.get(i);
}
sr.polygon(v);
But I just get errors.
I am trying to achieve something like this, if you know a different way of doing this then that would be really helpful. By the way I'm also using box2d and this does not need to have collision it's just for the user visual.
The way I would personally do it is by having an LinkedList with Vector2 objects. Vector2 objects store two floats, so for every click, you get the x and y coordinates and make a new Vector2 object. By storing them in the LinkedList, you can retrieve the points at any time in the correct order so you can connect a line.
LinkedList<Vector2> v = new LinkedList<Vector2>();
public void ontouch(screenX, screenY){
v.add(new Vector2(screenX, screenY)); // add Vector2 into LinkedList
}
How you want to draw the lines or connect the points is up to you.
Another was is to just only keep the two most recent points that were clicked, and throw the others away. This would mean storing the lines instead of the points. If the lines are objects, then you can do this:
Vector2 previousPoint;
Vector2 currentPoint;
ArrayList<MyLineClass> lines = new ArrayList<MyLineClass>();
public void ontouch(screenX, screenY){
if(previousPoint == null){
previousPoint = new Vector2(screenX, screenY);
}else{
previousPoint = currentPoint;
currentPoint = new Vector2(screenX, screenY);
lines.add(new MyLineClass(currentPoint, previousPoint)
}
}
I wrote this off the cuff but I believe this example should work.
EDIT:
Good thing LibGDX is open source. If you want to use an array of float numbers, the method simply gets an x and y coordinate in alternating order. So for each index:
0 = x1
1 = y1
2 = x2
3 = y2
4 = x3
5 = y3
etc.
It's an odd method, but I suppose it works.
I am making a java rigid body physics engine, and it has gone great so far, until I tried to implement rotation. I don't know where the problem is coming from. I have methods calculating the moment of inertia of convex polygons and circles using formulas from these websites:
http://lab.polygonal.de/?p=57
http://en.wikipedia.org/wiki/List_of_moments_of_inertia
This is the code for the polygon moment of inertia:
public float momentOfInertia() {
Vector C = centerOfMass().subtract(position); //center of mass
Line[] sides = sides(); //sides of the polygon
float moi = 0; //moment of inertia
for(int i = 0; i < sides.length; i++) {
Line l = sides[i]; //current side of polygon being looped through
Vector p1 = C; //points 1, 2, and 3 are the points of the triangle
Vector p2 = l.point1;
Vector p3 = l.point2;
Vector Cp = p1.add(p2).add(p3).divide(3); //center of mass of the triangle, or C'
float d = new Line(C, Cp).length(); //distance between center of mass
Vector bv = p2.subtract(p1); //vector for side b of triangle
float b = bv.magnitude(); //scalar for length of side b
Vector u = bv.divide(b); //unit vector for side b
Vector cv = p3.subtract(p1); //vector for side c of triangle, only used to calculate variables a and h
float a = cv.dot(u); //length of a in triangle
Vector av = u.multiply(a); //vector for a in triangle
Vector hv = cv.subtract(av); //vector for height of triangle, or h in diagram
float h = hv.magnitude(); //length of height of triangle, or h in diagram
float I = ((b*b*b*h)-(b*b*h*a)+(b*h*a*a)+(b*h*h*h))/36; //calculate moment of inertia of individual triangle
float M = (b*h)/2; //mass or area of triangle
moi += I+M*d*d; //equation in sigma series of website
}
return moi;
}
And this is for the circle:
public float momentOfInertia() {
return (float) Math.pow(radius, 2)*area()/2;
}
I know for a fact that the area functions work fine, I have checked them. I just don't know how to check if the moment of inertia equations are wrong.
For collision detection, I used the separating axis theorem to check for any combination of two polygons and circles, where it can find out whether they are colliding, the normal velocity of the collision, and the contact point of the collision. These methods all work beautifully.
I might also like to say how positions are organized. Every body has a position and a shape, either a polygon or a circle. Each shape has a position, and polygons have individual vertices. So if I want to find the absolute position of a vertex of a polygon-shaped body, I need to add the positions of the body, the polygon, and the vertex itself. The center of mass equation is in absolute position according to the shape, with no account for the body. The center of mass and moment of inertia methods are in the Shape class.
For every body, the constants are being updated according to the force and torque in the body's update method where dt is delta time. I also rotate the polygon based on the difference in rotation, because the vertices are ever changing.
public void update(float dt) {
if(mass != 0) {
momentum = momentum.add(force.multiply(dt));
velocity = momentum.divide(mass);
position = position.add(velocity.multiply(dt));
angularMomentum += torque*dt;
angularVelocity = angularMomentum/momentOfInertia;
angle += angularVelocity*dt;
shape.rotate(angularVelocity*dt);
}
}
Finally, I also have a CollisionResolver class which fixes the collision of two colliding bodies, involving applying the normal force and friction. Here is the class's only method which does all of this:
public static void resolveCollision(Body a, Body b, float dt) {
//calculate normal vector
Vector norm = CollisionDetector.normal(a, b);
Vector normb = norm.multiply(-1);
//undo overlap between bodies
float ratio1 = a.mass/(a.mass+b.mass);
float ratio2 = b.mass/(b.mass+a.mass);
a.position = a.position.add(norm.multiply(ratio1));
b.position = b.position.add(normb.multiply(ratio2));
//calculate contact point of collision and other values needed for rotation
Vector cp = CollisionDetector.contactPoint(a, b, norm);
Vector c = a.shape.centerOfMass().add(a.position);
Vector cb = b.shape.centerOfMass().add(b.position);
Vector d = cp.subtract(c);
Vector db = cp.subtract(cb);
//create the normal force vector from the velocity
Vector u = norm.unit();
Vector ub = u.multiply(-1);
Vector F = new Vector(0, 0);
boolean doA = a.mass != 0;
if(doA) {
F = a.force;
}else {
F = b.force;
}
Vector n = new Vector(0, 0);
Vector nb = new Vector(0, 0);
if(doA) {
Vector Fyp = u.multiply(F.dot(u));
n = Fyp.multiply(-1);
nb = Fyp;
}else{
Vector Fypb = ub.multiply(F.dot(ub));
n = Fypb;
nb = Fypb.multiply(-1);
}
//calculate normal force for body A
float r = a.restitution;
Vector v1 = a.velocity;
Vector vy1p = u.multiply(u.dot(v1));
Vector vx1p = v1.subtract(vy1p);
Vector vy2p = vy1p.multiply(-r);
Vector v2 = vy2p.add(vx1p);
//calculate normal force for body B
float rb = b.restitution;
Vector v1b = b.velocity;
Vector vy1pb = ub.multiply(ub.dot(v1b));
Vector vx1pb = v1b.subtract(vy1pb);
Vector vy2pb = vy1pb.multiply(-rb);
Vector v2b = vy2pb.add(vx1pb);
//calculate friction for body A
float mk = (a.friction+b.friction)/2;
Vector v = a.velocity;
Vector vyp = u.multiply(v.dot(u));
Vector vxp = v.subtract(vyp);
float fk = -n.multiply(mk).magnitude();
Vector fkv = vxp.unit().multiply(fk); //friction force
Vector vr = vxp.subtract(d.multiply(a.angularVelocity));
Vector fkvr = vr.unit().multiply(fk); //friction torque - indicated by r for rotation
//calculate friction for body B
Vector vb = b.velocity;
Vector vypb = ub.multiply(vb.dot(ub));
Vector vxpb = vb.subtract(vypb);
float fkb = -nb.multiply(mk).magnitude();
Vector fkvb = vxpb.unit().multiply(fkb); //friction force
Vector vrb = vxpb.subtract(db.multiply(b.angularVelocity));
Vector fkvrb = vrb.unit().multiply(fkb); //friction torque - indicated by r for rotation
//move bodies based on calculations
a.momentum = v2.multiply(a.mass).add(fkv.multiply(dt));
if(a.mass != 0) {
a.velocity = a.momentum.divide(a.mass);
a.position = a.position.add(a.velocity.multiply(dt));
}
b.momentum = v2b.multiply(b.mass).add(fkvb.multiply(dt));
if(b.mass != 0) {
b.velocity = b.momentum.divide(b.mass);
b.position = b.position.add(b.velocity.multiply(dt));
}
//apply torque to bodies
float t = (d.cross(fkvr)+d.cross(n));
float tb = (db.cross(fkvrb)+db.cross(nb));
if(a.mass != 0) {
a.angularMomentum = t*dt;
a.angularVelocity = a.angularMomentum/a.momentOfInertia;
a.angle += a.angularVelocity*dt;
a.shape.rotate(a.angularVelocity*dt);
}
if(b.mass != 0) {
b.angularMomentum = tb*dt;
b.angularVelocity = b.angularMomentum/b.momentOfInertia;
b.angle += b.angularVelocity*dt;
b.shape.rotate(b.angularVelocity*dt);
}
}
As for the actual problem, both the circles and polygons rotate very slowly and often in wrong directions. I know I am throwing a lot out there, but this problem has been bugging me for a while, and I would appreciate any help I can get.
Thanks.
This answer addresses the "I just don't know how to check if the moment of inertia equations are wrong." part of the question.
There are several possible approaches, some of which you may have already tried, and they can be used in combination:
Unit testing
Take your moment of inertia code and apply it to problems with known solutions from a tutorial or textbook.
Dimensional analysis
I would recommend this anyway for any scientific or engineering program. You may have deleted comments for compactness of posted code, but they are important. Annotate each variable that represents a physical quantity with its units. Check that every expression you evaluate has the right units, based on its inputs, for its result variable. For example, in the classic equation F=ma in SI units: F is in Newtons, equivalent to kg.m/(s^2), m is in kg, a is in m/(s^2), so it all balances. Be careful with transitions between physics world coordinates and screen coordinates.
Program simplification
Try working first with only one instance of one very simple shape for which you can do all the calculations by hand. Since some of your problems do not relate to rotation, a circle may be a good first choice because of its symmetry. Debug that, comparing intermediate results to equivalent results from paper-and-pencil (and calculator). Gradually add more instances of the same shape, then debug a single instance of the next shape...
Deliberate error
Given that you suspect your inertia calculations, try setting arbitrary values slightly different from your calculations, and see what differences they make in the display. Are the effects similar to the problems you are seeing? If so, keep it as a hypothesis.
As a more general note, programs that do iterative simulation can be very vulnerable to accumulated floating point error. Unless you have a real need to save space, and have done enough analysis of the numerical stability of your code to be sure float is OK, I strongly recommend using double instead. This is probably not your current problem, but is something that could become an issue later.
I've been trying for the past few days to make a working implementation of a virtual trackball for the user interface for a 3D graphing-like program. But I'm having trouble.
Looking at the numbers and many tests the problems seems to be the actual concatenation of my quaternions but I don't know or think so. I've never worked with quaternions or virtual trackballs before, this is all new to me. I'm using the Quaternion class supplied by JOGL. I tried making my own and it worked (or at least as far a I know) but it was a complete mess so I just went with JOGL's.
When I do not concatenate the quaternions the slight rotations I see seem to be what I want, but of course It's hard when it's only moving a little bit in any direction. This code is based off of the Trackball Tutorial on the OpenGL wiki.
When I use the Quaternion class's mult (Quaternion q) method the graph hardly moves (even less than not trying to concatenate the quaternions).
When I tried Quaternionclass'sadd (Quaternion q)` method for the fun of it I get something that at the very least rotates the graph but not in any coherent way. It spazzes out and rotates randomly as I move the mouse. Occasionally I'll get quaternions entirely filled with NaN.
In my code I will not show either of these, I'm lost with what to do with my quaternions. I know I want to multiply them because as far as I'm aware that's how they are concatenated. But like I said I've had no success, I'm assuming the screw up is somewhere else in my code.
Anyway, my setup has a Trackball class with a public Point3f projectMouse (int x, int y) method and a public void rotateFor (Point3f p1, Point3f p2), Where Point3f is a class I made. Another class called Camera has a public void transform (GLAutoDrawable g) method which will call OpenGL methods to rotate based on the trackball's quaternion.
Here's the code:
public Point3f projectMouse (int x, int y)
{
int off = Screen.WIDTH / 2; // Half the width of the GLCanvas
x = x - objx_ - off; // obj being the 2D center of the graph
y = off - objy_ - y;
float t = Util.sq(x) + Util.sq(y); // Util is a class I made with
float rsq = Util.sq(off); // simple some math stuff
// off is also the radius of the sphere
float z;
if (t >= rsq)
z = (rsq / 2.0F) / Util.sqrt(t);
else
z = Util.sqrt(rsq - t);
Point3f result = new Point3f (x, y, z);
return result;
}
Here's the rotation method:
public void rotateFor (Point3f p1, Point3f p2)
{
// Vector3f is a class I made, I already know it works
// all methods in Vector3f modify the object's numbers
// and return the new modify instance of itself
Vector3f v1 = new Vector3f(p1.x, p1.y, p1.z).normalize();
Vector3f v2 = new Vector3f(p2.x, p2.y, p2.z).normalize();
Vector3f n = v1.copy().cross(v2);
float theta = (float) Math.acos(v1.dot(v2));
float real = (float) Math.cos(theta / 2.0F);
n.multiply((float) Math.sin(theta / 2.0F));
Quaternion q = new Quaternion(real, n.x, n.y, n.z);
rotation = q; // A member that can be accessed by a getter
// Do magic on the quaternion
}
EDIT:
I'm getting closer, I found out a few simple mistakes.
1: The JOGL implementation treats W as the real number, not X, I was using X for real
2: I was not starting with the quaternion 1 + 0i + 0j + 0k
3: I was not converting the quaternion into an axis/angle for opengl
4: I was not converting the angle into degrees for opengl
Also as Markus pointed out I was not normalizing the normal, when I did I couldn't see much change, thought it's hard to tell, he's right though.
The problem now is when I do the whole thing the graph shakes with a fierceness like you would never believe. It (kinda) moves in the direction you want it to, but the seizures are too fierce to make anything out of it.
Here's my new code with a few name changes:
public void rotate (Vector3f v1, Vector3f v2)
{
Vector3f v1p = v1.copy().normalize();
Vector3f v2p = v2.copy().normalize();
Vector3f n = v1p.copy().cross(v2p);
if (n.length() == 0) return; // Sometimes v1p equals v2p
float w = (float) Math.acos(v1p.dot(v2p));
n.normalize().multiply((float) Math.sin(w / 2.0F));
w = (float) Math.cos(w / 2.0F);
Quaternion q = new Quaternion(n.x, n.y, n.z, w);
q.mult(rot);
rot_ = q;
}
Here's the OpenGL code:
Vector3f p1 = tb_.project(x1, y1); // projectMouse [changed name]
Vector3f p2 = tb_.project(x2, y2);
tb_.rotate (p1, p2);
float[] q = tb_.getRotation().toAxis(); // Converts to angle/axis
gl.glRotatef((float)Math.toDegrees(q[0]), q[1], q[2], q[3]);
The reason for the name changes is because I deleted everything in the Trackball class and started over. Probably not the greatest idea, but oh well.
EDIT2:
I can say with pretty good certainty that there is nothing wrong with projecting onto the sphere.
I can also say that as far as the whole thing goes it seems to be the VECTOR that is the problem. The angle looks just fine, but the vector seems to jump around.
EDIT3:
The problem is the multiplication of the two quaternions, I can confirm that everything else works as expected. Something goes whacky with the axis during multiplication!
The problem is the multiplication of the two quaternions, I can confirm that everything else works as expected. Something goes whacky with the axis during multiplication!
You are absolutely correct!! I just recently submitted a correct multiplication and Jogamp has accepted my change. They had incorrect multiplication on mult(quaternion).
I am sure if you get the latest jogl release, it'll have the correct mult(Quaternion)
I did it!
Thanks to this C++ implementation I was able to develop a working trackball/arcball interface. My goodness me, I'm still not certain what the problem was, but I rewrote everything and even wrote my own Quaternions class and suddenly the whole thing works. I also made a Vectors class for vectors. I had a Vector3f class before but the Quaternions and Vectors classes are full of static methods and take in arrays. To make it easy to do vector computations on quaternions and vice versa. I will link the code for those two classes below, but only the Trackball class will be show here.
I made those two classes pretty quickly this morning so if there are any mathematical errors, well, uh, oops. I only used what I needed to use and made sure they were correct. These classes are below:
Quaternions: http://pastebin.com/raxS4Ma9
Vectors: http://pastebin.com/fU3PKZB9
Here is my Trackball class:
public class Trackball
{
private static final float RADIUS_ = Screen.DFLT_WIDTH / 2.0F;
private static final int REFRESH_ = 50;
private static final float SQRT2_ = (float) Math.sqrt(2);
private static final float SQRT2_INVERSE_ = 1.0F / SQRT2_;
private int count_;
private int objx_, objy_;
private float[] v1_, v2_;
private float[] rot_;
public Trackball ()
{
v1_ = new float[4];
v2_ = new float[4];
rot_ = new float[] {0, 0, 0, 1};
}
public void click (int x, int y)
{
v1_ = project(x, y);
}
public void drag (int x, int y)
{
v2_ = project(x, y);
if (Arrays.equals(v1_, v2_)) return;
float[] n = Vectors.cross(v2_, v1_, null);
float[] o = Vectors.sub(v1_, v2_, null);
float dt = Vectors.len(o) / (2.0F * RADIUS_);
dt = dt > 1.0F ? 1.0F : dt < -1.0F ? -1.0F : dt;
float a = 2.0F * (float) Math.asin(dt);
Vectors.norm_r(n);
Vectors.mul_r(n, (float) Math.sin(a / 2.0F));
if (count_++ == REFRESH_) { count_ = 0; Quaternions.norm_r(rot_); }
float[] q = Arrays.copyOf(n, 4);
q[3] = (float) Math.cos(a / 2.0F);
rot_ = Quaternions.mul(q, rot_, rot_);
}
public float[] getAxis ()
{
return Quaternions.axis(rot_, null);
}
public float[] project (float x, float y)
{
x = RADIUS_ - objx_ - x;
y = y - objy_ - RADIUS_;
float[] v = new float[] {x, y, 0, 0};
float len = Vectors.len(v);
float tr = RADIUS_ * SQRT2_INVERSE_;
if (len < tr)
v[2] = (float) Math.sqrt(RADIUS_ * RADIUS_ - len * len);
else
v[2] = tr * tr / len;
return v;
}
}
You can see there's a lot of similarities from the C++ example. Also I'd like to note there is no method for setting the objx_ and objy_ values yet. Those are for setting the center of the graph which can be moved around. Just saying, so you don't scratch your head about those fields.
The cross-product of two normalized vectors is not normalized itself. It's length is sin(theta). Try this instead:
n = n.normalize().multiply((float) Math.sin(theta / 2.0F));
I have a list of lat/long coordinates that I would like to use to calculate an area of a polygon. I can get exact in many cases, but the larger the polygon gets, the higher chance for error.
I am first converting the coordinates to UTM using http://www.ibm.com/developerworks/java/library/j-coordconvert/
From there, I am using http://www.mathopenref.com/coordpolygonarea2.html to calculate the area of the UTM coordinates.
private Double polygonArea(int[] x, int[] y) {
Double area = 0.0;
int j = x.length-1;
for(int i = 0; i < x.length; i++) {
area = area + (x[j]+x[i]) * (y[j]-y[i]);
j = i;
}
area = area/2;
if (area < 0)
area = area * -1;
return area;
}
I compare these areas to the same coordinates I put into Microsoft SQL server and ArcGIS, but I cannot seem to match them exactly all the time. Does anyone know of a more exact method than this?
Thanks in advance.
EDIT 1
Thank you for the comments.
Here is my code for getting the area (CoordinateConversion code is listed above on the IBM link):
private Map<Integer, GeoPoint> vertices;
private Double getArea() {
List<Integer> xpoints = new ArrayList<Integer>();
List<Integer> ypoints = new ArrayList<Integer>();
CoordinateConversion cc = new CoordinateConversion();
for(Entry<Integer, GeoPoint> itm : vertices.entrySet()) {
GeoPoint pnt = itm.getValue();
String temp = cc.latLon2MGRUTM(pnt.getLatitudeE6()/1E6, pnt.getLongitudeE6()/1E6);
// Example return from CC: 02CNR0634657742
String easting = temp.substring(5, 10);
String northing = temp.substring(10, 15);
xpoints.add(Integer.parseInt(easting));
ypoints.add(Integer.parseInt(northing));
}
int[] x = toIntArray(xpoints);
int[] y = toIntArray(ypoints);
return polygonArea(x,y);
}
Here is an example list of points:
44.80016800 -106.40808100
44.80016800 -106.72123800
44.75016800 -106.72123800
44.75016800 -106.80123800
44.56699100 -106.80123800
In ArcGIS and MS SQL server I get 90847.0 Acres.
Using the code above I get 90817.4 Acres.
Another example list of points:
45.78412600 -108.51506700
45.78402600 -108.67972100
45.75512200 -108.67949400
45.75512200 -108.69962300
45.69795400 -108.69929400
In ArcGIS and MS SQL server I get 15732.9 Acres.
Using the code above I get 15731.9 Acres.
The area formula you are using is valid only on a flat plane. As the polygon gets larger, the Earth's curvature starts to have an effect, making the area larger than what you calculate with this formula. You need to find a formula that works on a the surface of a sphere.
A simple Google search for "area of polygon on spherical surface" turns up a bunch of hits, of which the most interesting is Wolfram MathWorld Spherical Polygon
It turns out that UTM just isn't able to get the extreme accuracy I was looking for. Switching projection systems to something more accurate like Albers or State Plane provided a much more accurate calculation.