I've got a problem: I have two coordinates (start and end point positions) and a polygon. I want to check whether this route is (1) from outside to inside of the polygon, (2) from inside to outside, (3) only inside, or (4) only outside.
I would really appreciate any help.
You are lucky! Java contains a Polygon class which has a method contains(double x, double y). Use it!
See this page:
http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
This function is taken from there.
It is based on the observation that a test point is within a polygon if when projected on the y-axis it's x value is below odd number of polygon edges. Works for both convex and concave polygons.
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
Related
I found this question that deals with the same issue. The provided answers work, but I need to change it slightly for my case. Below is the answer I went with:
double theta = Math.atan2(pointerY - height / 2, pointerX - width / 2);
if(theta<0)
theta = Math.PI - theta;
int whichSlice = 0;
double sliceSize = Math.PI*2 / 4;
double sliceStart;
for(int i=1; i<=4; i++) {
sliceStart = i*sliceSize;
if(theta < sliceStart) {
whichSlice = i;
break;
}
}
In my case, I need to rotate the quadrants by 45 degrees. Below is an example; red is what this code does, while green is what I want:
I've tried various code alterations, but still can't figure it out.
EDIT:
First off, create your circle in it's own desperate JComponent, and add it's own listeners - basically create a class for this circle, make the circle itself receive mouse events, and MAKE SURE THAT THE CIRCLE OCCUPIES THE ENTIRE RECTANGLE OF THE JCOMPONENT - it must be touching all edges (I will be using this.getHeight() and this must return the height of the bounding box of the circle)!!!
Fixed code below to support such a case, in addition to support y axis which increases downwards:
Step 1:
Check if we are inside the circle.
Step 2:
Check if we are above/below the diagonal lines (note: equations for diagonal lines are y = x, and y = -x)
Point pointWeAreChecking;
Point centerOfCircle;
double radius;
if(Math.pow(Math.pow(pointWeAreChecking.x-centerOfCircle.x , 2) + Math.pow(pointWeAreChecking.y-centerOfCircle.y , 2), 0.5) <= radius)
{
//Means we are in circle.
if(pointWeAreChecking.y>pointWeAreChecking.x)
{
//Means it is either in 2 or 3 (it is below y = -x line)
if(pointWeAreChecking.y>-pointWeAreChecking.x + this.getHeight()){
//We are in 2.
}else
{
//We are in 3.
}
}else
{
if(pointWeAreChecking.y>-pointWeAreChecking.x + this.getHeight())
{
//We are in 4.
}else
{
//We are in 2.
}
}
}
I'm using the method of dividing the x and y coordinates by the z value, then rendering it just like you would a 2D game/thing to create 3D looking thing. The problem is when the z value is less then 0 it flips the quadrants for the coordinate and looks very weird. At the moment there is only movement if someone could help me fix the negative z value thing and show me how to rotate. I'm not using and matrices only vectors that take in x,y and z for the maths if that helps. I'm making this using Java with no extra libraries.
Thanks for the help.
I used the perspective matrix and multiplied it by my vector but it didn't work here is my code there might be something wrong with it. I just turned the vector into a 1 by 3 matrix and then did this.
public Matrix multiply(Matrix matrix)
{
Matrix result = new Matrix(getWidth(),getHeight());
for(int y = 0; y < getHeight()-1; y++)
{
for(int x = 0; x < getWidth()-1; x++)
{
float sum = 0.0f;
for(int e = 0; e < this.getWidth()-1; e++)
{
sum += this.matrix[e + y * getWidth()] * matrix.matrix[x + e * matrix.getWidth()];
}
result.matrix[x + y * getWidth()] = sum;
}
}
return result;
}
Just guessing here, but it sounds like you are trying to do a projection transform: You are modeling 3D objects (things with X, Y, and Z coordinates) and you want to project them onto a 2D window (i.e., the screen).
The meaning of Z in the naive projection transform is the distance between the point and a plane parallel to the screen, that passes through your eyeball. If you have points with -Z, those represent points that are behind your head.
Sounds like you need to translate the Z coordinates so that Z=0 is the plane of the screen, or a plane parallel to and behind the screen. (In other words, Add a constant to all of your Zs, so that none of them is negative.)
http://en.wikipedia.org/wiki/3D_projection
I'm trying to get my head around this- and I've literally been looking for a whole day!
I think I understand the main concepts behind it, but I'm struggling to figure out the math I need to create the axis on which to project my shapes on to?
So if I have a rectange I find out each of the points and then use these to find the side of the shape edge = v(n) - v(n-1) and go through all sides.
But I don't know how to then create the separating axis.
The theorem is not difficult to understand: If you can find a line for which all points of shape A are on the one side, and all points of shape B are on the other (dot product positive or negative), that line is separating the shapes.
What do you want to do? Find separating lines for arbitrary shapes?
I would recommend to have a look at projective geometry, as the edge for two vertices of a polygon extended to infinity can be represented by the cross product of the two vertices (x, y, 1). For convex polygons you can simply create lines for all edges and then take the dot product of all vertices of your other polygon to check on which side they are. If for one edge all points are outside, that edge is a separating line.
Also keep in mind that by keeping the line normalized you get the distance of a point to the line using dot product. The sign identifies the side on which the point lies.
If your problem is more difficult, please explain it in more detail. Maybe you can use some sort of clipping to solve it fast.
Example: projective geometry
public double[] e2p(double x, double y) {
return new double[] { x, y, 1 };
}
// standard vector maths
public double[] getCrossProduct(double[] u, double[] v) {
return new double[] { u[1] * v[2] - u[2] * v[1],
u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0] };
}
public double getDotProduct(double[] u, double[] v) {
return u[0] * v[0] + u[1] * v[1] + u[2] * v[2];
}
// collision check
public boolean isCollision(List<Point2D> coordsA, List<Point2D> coordsB) {
return !(isSeparate(pointsA, pointsB) || isSeparate(pointsB, pointsA));
}
// the following implementation expects the convex polygon's vertices to be in counter clockwise order
private boolean isSeparate(List<Point2D> coordsA, List<Point2D> coordsB) {
edges: for (int i = 0; i < coordsA.size(); i++) {
double[] u = e2p(coordsA.get(i).getX(), coordsA.get(i).getY());
int ni = i + 1 < coordsA.size() ? i + 1 : 0;
double[] v = e2p(coordsA.get(ni).getX(), coordsA.get(ni).getY());
double[] pedge = getCrossProduct(u, v);
for (Point2D p : coordsB) {
double d = getDotProduct(pedge, e2p(p.getX(), p.getY()));
if (d > -0.001) {
continue edges;
}
}
return true;
}
return false;
}
The separating axis is one of the sides. You can find the signs of the vertexes of the shape itself when plugged in the equation of this side:
(X - Xn).(Y - Yn-1) - (X - Xn-1).(Y - Yn) = 0
Check that the vertices of the other shape yield opposite signs.
I want a java function where if we pass the lat,long of a object, whether that object lies inside a area. Again this Area is defined by lat,long
for example I define a area with 3 lat/long positions.( a rectangle ) and if I pass a lat/long position it should return me whether its within the rectangle.
If the area is always a rectangle, the easiest way is to compare the coordinates.
Let's assume that your rectangle is defined by its upper left (r1x: lon and r1y: lat) and lower right (r2x and r2y) corners. Your object (a point) is defined by px: lon and py: lat.
So, your object is inside the area if
px > r1x and px < r2x
and
py < r1y and py > r2y
Programatically it would be something like:
boolean isPInR(double px, double py, double r1x, double r1y, double r2x, double r2y){
if(px > r1x && px < r2x && py < r1y && py > r2y){
//It is inside
return true;
}
return false;
}
EDIT
In the case where your polygon is not a rectangle, you can use the Java.awt.Polygon Class. In this class you will find the method contains(x,y) which return true if the point with x and y coordinates is inside the Polygon.
This method uses the Ray-casting algorithm. To simplify, this algorithm draw a segment in a random direction from your point. If the segment cross your polygon's boarder an odd number of times, then it is inside your polygon. If it crosses it an even number of times, then it is outside.
To use the polygon Class, you can do something like:
//This defines your polygon
int xCoord[] = {1,2,3,5,9,-5};
int yCoord[] = {18,-32,1,100,-100,0};
myPolygon = new Polygon(xCoord, yCoord, xCoord.length);
//This finds if the points defined by x and y coordinates is inside the polygon
Boolean isInside = myPolygon.contains(x,y);
And don't forget to
import java.awt.Polygon;
EDIT
Right coordinates are in Double !
So you need to use Path2D.Double !
Begin by import java.awt.geom.Path2D;
Let's say you start with similar arrays as before:
//This defines your polygon
Double xCoord[] = {1.00121,2,3.5464,5,9,-5};
Double yCoord[] = {18.147,-32,1,100,-100.32,0};
Path2D myPolygon = new Path2D.Double();
//Here you append all of your points to the polygon
for(int i = 0; i < xCoord.length; i++) {
myPolygon.moveTo(xCoord[i], yCoord[i]);
}
myPolygon.closePath();
//Now we want to know if the point x, y is inside the Polygon:
Double x; //The x coord
Double y; //The y coord
Boolean isInside = myPolygon.contains(x,y);
And here you go with Double !
I'm making an ellipsoid out of voxels, and I have an implementation, but it's not working correctly, for the past few hours I've been trying things with no success.
Here's how it works:
It draws 2D ellipses down the Z axis using the Bresenham circle algorithm
It calculates the coordinates of a circle going forward and one going side to side
It uses those coordinates as radii for X and Y
Here's what works:
Spheres are perfect
Ellipsoids are perfect when
z > x and z > y
x == y
Here's what doesn't:
Ellipsoids when x != y
Example when y > x http://postimage.org/image/gslvykrgd/
The same thing happens when x > y only flipped
I'm guessing its because I'm drawing the slices from center to front every time, but I don't know. Below is my implementation, it's a mess. I quickly cleaned it up a bit so it's understandable to some degree. Also I'm saving optimisations for later, I just want it working now.
I won't show my implementation of the circle algorithm itself, because I KNOW that it works and it would just make this question longer. So I'll explain the two functions for it instead.
private List<Integer> getSlice(int rx, int ry) gets the raw result from a run of the Bresenham circle algorithm, no symmetry needed. It returns the result as a list of the x, y results in that order.
public void generateEllipse(int z, int cx, int cy, int cz, int rx, int ry) generates an ellipse with the given information and plots down the coordinates using symmetry, these will be rendered on the screen.
ArrayList<Integer> s = getSlice(rz, rx);
ArrayList<Integer> s2 = getSlice(rz, ry);
int cap = Math.max(s2.size(), s.size());
while (s.size() > 1)
{
int x = 0;
int z = 0;
int z2 = 0;
int y2 = 0;
int i = cap - 2;
if (s.size() > i)
{
z = s.get(i);
x = s.get(i + 1);
s.remove(i + 1);
s.remove(i);
}
if (s2.size() > i)
{
z2 = s2.get(i);
y2 = s2.get(i + 1);
s2.remove(i + 1);
s2.remove(i);
}
if (x != 0 && y2 != 0)
generateEllipse(z, cx, cy, cz, x, y2);
cap = Math.max(s2.size(), s.size());
I asked a similar question a week or two back (yeah I've been having problems for that long :() and got an answer, I implemented it and it worked, but I wasn't satisfied, I want to avoid floating point numbers all together. See 3D Ellipsoid out of discrete units.
With that I was having problems with rounding off the numbers and getting uneven slices, so spheres were impossible. Ironically, now spheres are the only thing possible (other than front to back ellipses).
EDIT:
I got x > y working by adding
else if (y2 < x && ry - y2 != 0)
generateEllipse(z, cx, cy, cz, x, ry - y2);
and || r - y2 == 0 to the first test at the bottom.
I'm not too sure why that worked, I'm figuring that out now. But I'm still having problems with y > x. Anyone?
EDIT2:
Now that I look at it, it's different than the y = x ellipsoid, back to the drawing board.
EDIT3:
Last night I was thinking about this and I think I've figured it out, this isn't an answer to my own question, but I guess pointing out what I'm seeing to be wrong. I'm testing for the first list and drawing coordinates decrementally from the size of the largest list.
That's badness because the two lists aren't guaranteed equal lengths, that's a failure on my part for trying to rush an algorithm.
In the picture, you can't see, but the little ellipse is actually a few blocks away from the big ellipse. This is caused when from ellipses not being drawn, which is caused by lists not having data. The actual little ellipse big ellipse is caused because the algorithm draws two octants, both of which are stored into the list from getSlice(...).
So how can I fix this problem? I haven't implemented anything yet and probably won't for a while (it's early) but this is what my brain cranked out. Again, this is not an answer to the question, just thoughts.
I iterate for while(!s.isEmpty()) and define two new values outside the loop: incX = s.size and incY = s1.size I test to see if their z values match, if not, then I correct it. I'm thinking I'll get the values, test the largest list and if they don't match then decrement the inc value of the smallest list by two and get the old values.
I test for !s.isEmpty() because the two lists will be empty at the same time. Also drawing ellipses, I'll use whichever one z value, because, again, they both should be equal.
If that's wrong, then I guess I have this document I found to go through: http://www.cs.sunysb.edu/vislab/wordpress/pdf/398.pdf.
Thank you for anyone who viewed this (even though I didn't get any replies :(), I'm setting this answer because the problem is solved, the code is as follows:
ArrayList<Integer> s = getSlice(rz, rx);
ArrayList<Integer> s2 = getSlice(rz, ry);
boolean yMax = Math.max(s2.size(), s.size()) == s2.size();
int decX = s.size() - 1;
int decY = s2.size() - 1;
boolean done = false;
while (!done)
{
int x = 0;
int z = 0;
int z2 = 0;
int y = 0;
y = s2.get(decY--);
z2 = s2.get(decY--);
x = s.get(decX--);
z = s.get(decX--);
if (z != z2)
{
if (yMax)
{
decX += 2;
x = s.get(decX);
s2.remove(decY + 2);
s2.remove(decY + 1);
}
else
{
decY += 2;
y = s2.get(decY);
s.remove(decX + 2);
s.remove(decX + 1);
}
z = z < z2 ? z : z2;
}
else
{
s.remove(decX + 2);
s.remove(decX + 1);
s2.remove(decY + 2);
s2.remove(decY + 1);
}
if (y != 0 && x != 0)
generateEllipse(z, cx, cy, cz, x, y);
done = yMax ? s2.isEmpty() : s.isEmpty();
}
All that needs to be done now is optimisation. I'm still going to read that paper, it covers other topics that are interesting and useful for my program :).
The issue was that I was not accounting for the different sizes of each list, if a curve is less steep than the other it will have more coordinates. Z will always be the same when rx == ry. This allowed me to draw spheres and forward to back ellipsoids.
When they are not the same the z will change because the curve will be faster / slower. When this happened while I was testing for the first list it would ignore those because iteration would stop before it got to them.
The big ellipse, little ellipse was caused because they were drawn backwards, so the outer octant was drawn first which happens to have less total values.
In the near future I will put up a much more detailed answer and a much more elegant implementation. I'm just putting up this answer to let any passersby know that the problem is resolved. I want no one to ever go through the frustration I did trying to figure this whole thing out! It's amazing how the most difficult problems are caused by the silliest things.