Divide a 2D path created from points XY into equal chunks Java - java

I have an array of points A,B,C,D,E...N which when connected make a path.
How can i divide this path to equal chunks and get position of each chunk XY ?
EDIT : As user Hedja suggested i have created function to process this problem, but i cannot detect situation where chunk is splitted on two subpaths
public ArrayList<PointF> getPositions(ArrayList<PointF> mInput,float mChunkSize){
ArrayList<PointF> mResult = new ArrayList<PointF>();
float mModulo = 0f;
for (int i = 0;i<mInput.size()-1;i++){
//distance to next
float mDistanceAB = MyGameMath.distance(mInput.get(i).x, mInput.get(i).y,mInput.get(i+1).x,mInput.get(i+1).y);
//how many parts will fit
float mCountParts = (float) (mDistanceAB/mChunkSize); //how much parts will fit
//if distance is greater than chunk size
if (Math.abs(mDistanceAB)>=mChunkSize) {
Log.i("Chunk","Index "+(i)+" -> "+(i+1)+" = "+mCountParts+", rest="+mModulo);
float dx = mInput.get(i+1).x-mInput.get(i).x;
float dy = mInput.get(i+1).y-mInput.get(i).y;
float ux = dx/mDistanceAB;
float uy = dy/mDistanceAB;
for (int y=0;y<=mCountParts;y++){
//for every part
float nx = mInput.get(i).x+ux*mChunkSize*y;
float ny = mInput.get(i).y+uy*mChunkSize*y;
//Log.i("Chunk","at:"+nx+","+ny);
mResult.add(new PointF(nx, ny));
}
}
mModulo = mDistanceAB%mChunkSize; //how much left from previous subpath
}
return mResult;
}

So I assume you have something similar to this where Point is an object with attributes x and y.
Point[] points = new Points[]{ //Your Points }
List<Point> chunkedPoints = new ArrayList<Point>();
I also assume by "equal chunks" you mean the distance of each path.
First you'll iterate through the array, as you won't need to calculate the "next point" after the last point, you can add it at the end.:
for(int i = 0; i < points.length-1; i++) { //Skip the last element
//chunking here
}
chunkedPoints.add(points[points.length-1]); //Add the last element
You'll need to find the Unit Vector, that is, the direction you travel to get to the next point. So first you need to get the difference in x and y from one point and the next:
double dx = point[i].x - point[i+1].x;
double dy = point[i].y - point[i+1].y;
Then the distance from that point to the next (simple Pythagoras):
double distance = Math.sqrt(dx*dx+dy*dy);
The unit vector can now be calculated
double ux = dx/distance;
double uy = dy/distance;
So now you know where to travel, you need to specify how far you want to travel along it, I'll call this CHUNK_SIZE.
double nx = point[i].x + ux*CHUNK_SIZE;
double ny = point[i].y + uy*CHUNK_SIZE;
nx and ny is the co-ordinate of your new point. However, you need to check if you've passed the next point so that you can stop. Your problem doesn't specify what you do when you reach an end of a subpath without travelling the chunk size so I'll assume you simply stop at it, so the code becomes:
double nx = point[i].x;
double ny = point[i].y;
for(
//This part can be tidier
int count = 0;
count < CHUNK_SIZE && nx+ux != points[i+1].x && ny+uy != points[i+1].y;
count++
) {
nx += ux;
ny += uy;
}
Point newPoint = new Point(nx, ny);
Now you have your new Point, you can start from there, aim for the same point as before or if it's the same as the next point, start from the point after that. So your loop is now something like
chunkedPoints.add(points[0]);
for(int i = 0; i < points.length-1; i++) { //Skip the last element
Point newPoint;
do {
//chunking
newPoint = new Point(nx, ny);
chunkedPoints.add(newPoint);
} while(!newPoint.equals(points[i+1]));
}
chunkedPoints.add(points[points.length-1]); //Add the last element
Hope that helped.
I haven't tested this, but I've done something very similar in the past, so it should work.
EDIT: Okay, I've seen your edit and honestly have no idea what your question is asking. Sorry.

Related

Detect collision with lines and limit movement

I'm making a game with libGDX in Java. I'm trying to make a collision detection. As you can see in the image, I have a line which is a wall and a player with specified radius. The desired position is the next location which the player is trying to be in. But because there is a wall, he's placed in the Actual Position which is on the Velocity vector, but more closer to the prev location. I'm trying to figure out how can I detect that closer position?
My attempt:
private void move(float deltaTime) {
float step;
beginMovementAltitude();
if (playerComponent.isWalking())
step = handleAcceleration(playerComponent.getSpeed() + playerComponent.getAcceleration());
else step = handleDeacceleration(playerComponent.getSpeed(), playerComponent.getAcceleration());
playerComponent.setSpeed(step);
if (step == 0) return;
takeStep(deltaTime, step, 0);
}
private void takeStep(float deltaTime, float step, int rotate) {
Vector3 position = playerComponent.getCamera().position;
float x = position.x;
float y = position.y;
int radius = playerComponent.getRadius();
auxEnvelope.init(x, x + radius, y, y + radius);
List<Line> nearbyLines = lines.query(auxEnvelope);
float theta;
int numberOfIntersections = 0;
float angleToMove = 0;
Gdx.app.log(step + "", "");
for (Line line : nearbyLines) {
VertexElement src = line.getSrc();
VertexElement dst = line.getDst();
auxVector3.set(playerComponent.getCamera().direction);
auxVector3.rotate(Vector3.Z, rotate);
float nextX = x + (step * deltaTime) * (auxVector3.x);
float nextY = y + (step * deltaTime) * playerComponent.getCamera().direction.y;
float dis = Intersector.distanceLinePoint(src.getX(), src.getY(), dst.getX(), dst.getY(), nextX, nextY);
boolean bodyIntersection = dis <= 0.5f;
auxVector21.set(src.getX(), src.getY());
auxVector22.set(dst.getX(), dst.getY());
auxVector23.set(nextX, nextY);
if (bodyIntersection) {
numberOfIntersections++;
if (numberOfIntersections > 1) {
return;
}
theta = auxVector22.sub(auxVector21).nor().angle();
float angle = (float) (180.0 / MathUtils.PI * MathUtils.atan2(auxVector23.y - position.y, auxVector23.x - position.x));
if (angle < 0) angle += 360;
float diff = (theta > angle) ? theta - angle : angle - theta;
if (step < 0) step *=-1;
angleToMove = (diff > 90) ? theta + 180 : theta;
}
}
if (numberOfIntersections == 0) {
moveCameraByWalking(deltaTime, step, rotate);
} else {
moveCameraInDirection(deltaTime, step, angleToMove);
}
}
The idea is to find intersection of path of object center and the line moved by radius of the circle, see that picture.
At first, you need to find a normal to the line. How to do it, depends on how the line is defined, if it's defined by two points, the formula is
nx = ay - by
ny = bx - ax
If the line is defined by canonical equation, then coefficients at x and y define normal, if I remembered correctly.
When normal is found, we need to normalize it - set length to 1 by dividing coordinates by vector length. Let it be n.
Then, we will project starting point, desired point and randomly chosen point on line to n, treating them as radius vectors.
Projection of vector a to vector b is
project (a, b) = scalar_product (a, b) / length (b)**2 * b
but since b is n which length equals 1, we will not apply division, and also we want to only find length of the result, we do not multiply by b. So we only compute scalar product with n for each of three aforementioned points, getting three numbers, let s be the result for starting point, d for desired point, l for chosen point on the line.
Then we should modify l by radius of the circle:
if (s < d) l -= r;
else if (s > d) l += r;
If s = d, your object moves in parallel along the line, so line can't obstruct its movement. It's highly improbable case but should be dealt with.
Also, that's important, if l was initially between s and d but after modifying is no longer between then, it's a special case you may want to handle (restrict object movement for example)
Ather that, you should compute (d - s) / (l - s).
If the result is greater or equals 1, the object will not reach the line.
If the result is between 0 and 1, the line obstructs movement and the result indicates part of the path the object will complete. 0.5 means that object will stop halfway.
If the result is negative, it means the line is behind the object and will not obstruct movement.
Note that when using floating point numbers the result will not be perfectly precise, that's why we handle that special case. If you want to prevent this from happening at all, organize loop and try approximations until needed precision is reached.

How to determine if two points do not have any obstructions between them

I am currently trying to put together an algorithm where I can know if there is an obstruction between two defined points in a plane.
Here is an example image.
We can see with the image that point 1, 2, 3, & 6 are all accessible from the origin point. Points 4 and 5 are not. You pass through the polygon.
The code I am using is the following. pStartPoint and pEndPoint is the line from the origin to the point in question. The function checks all edges to see if the line passes through the edge.
public double GetSlopeOfLine(Point a, Point b){
double x = b.y - a.y;
double y = b.x - a.x;
return (x / y);
}
public double GetOffsetOfLine(double x, double y, double slope){
return (y - (slope * x));
}
public boolean IsPointAccessable(Point pStartPoint, Point pEndPoint){
//Define the equation of the line for these points. Once we have slope and offset the equation is
//y = slope * x + offset;
double slopeOfLine = GetSlopeOfLine(pStartPoint, pEndPoint);
double offSet = GetOffsetOfLine(pStartPoint.x, pStartPoint.y, slopeOfLine);
//Collision detection for each side of each obstacle. Once we get the point of collision, does it lie on the
//line in between the two points? If so, collision, and I can't reach that point yet.
for (Iterator<Obstacles> ObstacleIt = AdjustedObstaclesList.iterator(); ObstacleIt.hasNext();) {
Obstacles pObstacle = ObstacleIt.next();
int NumberOfEdges = pObstacle.getPoints().size();
for(int i=0; i<NumberOfEdges; i++){
//Get Edge[i];
int index = i;
Point pFirstPoint = (Point)pObstacle.getPoints().get(index);
if(i >= NumberOfEdges - 1)
index = 0;
else
index = i+1;
Point pNextPoint = (Point)pObstacle.getPoints().get(index);
double slopeOfEdge = GetSlopeOfLine(pFirstPoint, pNextPoint);
double offsetEdge = GetOffsetOfLine(pNextPoint.x, pNextPoint.y, slopeOfEdge);
int x = Math.round((float) ((-offSet + offsetEdge) / (slopeOfLine - slopeOfEdge)));
int y = Math.round((float) ((slopeOfLine * x) + offSet));
//If it lies on either point I could be looking at two adjacent points. I can still reach that point.
if(x > pStartPoint.x && x < pEndPoint.x && y > pStartPoint.y && y < pEndPoint.y &&
x > pFirstPoint.x && x < pNextPoint.x && y > pFirstPoint.y && y < pNextPoint.y){
return false;
}
}
}
return true;
}
If the line passes through and the point where the lines cross is found between pStartPoint and pEndPoint I am assuming that pEndPoint cannot be reached.
This function is not working and I am wondering if it has something to do with the fact that the origin is not at the bottom left but at the top left and that (width, height) of my window is located in the bottom right. Therefore the coordinate plane is messed up.
My mind must be mush because I cannot think how to adjust for this and if that is truly my mistake as I cannot seem to fix the error. I thought adjusting the slope and offset by multiplying each by -1 might have been the solution but that doesn't seem to work.
Is my solution the right one? Does my code seem correct in checking for an intersect point? Is there a better solution to see if a point is accessible.
There is also going to be the next step after this where once I determine what points are accessible if I am now on one of the points of the polygon. For example, from point 1 what points are accessible without crossing into the polygon?
First, I would like to say that using slopes for this kind of task is do-able, but also difficult due to the fact that they are very volatile in the sense that they can go from negative infinity to infinity with a very small change in the point. Here's a slightly different algorithm, which relies on angles rather than slopes. Another advantage of using this is that the coordinate systems don't really matter here. It goes like this (I reused as much of your existing code as I could):
public boolean IsPointAccessable(Point pStartPoint, Point pEndPoint) {
//Collision detection for each side of each obstacle. Once we get the point of collision, does it lie on the
//line in between the two points? If so, collision, and I can't reach that point yet.
for (Iterator<Obstacles> ObstacleIt = AdjustedObstaclesList.iterator(); ObstacleIt.hasNext();) {
Obstacles pObstacle = ObstacleIt.next();
int NumberOfEdges = pObstacle.getPoints().size();
for(int i=0; i<NumberOfEdges; i++){
//Get Edge[i];
int index = i;
Point pFirstPoint = (Point)pObstacle.getPoints().get(index);
if(i >= NumberOfEdges - 1)
index = 0;
else
index = i+1;
Point pNextPoint = (Point)pObstacle.getPoints().get(index);
// Here is where we get a bunch of angles that encode in them important info on
// the problem we are trying to solve.
double angleWithStart = getAngle(pNextPoint, pFirstPoint, pStartPoint);
double angleWithEnd = getAngle(pNextPoint, pFirstPoint, pEndPoint);
double angleWithFirst = getAngle(pStartPoint, pEndPoint, pFirstPoint);
double angleWithNext = getAngle(pStartPoint, pEndPoint, pNextPoint);
// We have accumulated all the necessary angles, now we must decide what they mean.
// If the 'start' and 'end' angles are different signs, then the first and next points
// between them. However, for a point to be inaccessible, it also must be the case that
// the 'first' and 'next' angles are opposite sides, as then the start and end points
// Are between them so a blocking occurs. We check for that here using a creative approach
// This is a creative way of checking if two numbers are different signs.
if (angleWithStart * angleWithEnd <= 0 && angleWithFirst * angleWithNext <= 0) {
return false;
}
}
}
return true;
}
Now, all that is left to do is find a method that calculates the signed angle formed by three points. A quick google search yielded this method (from this SO question):
private double getAngle(Point previous, Point center, Point next) {
return Math.toDegrees(Math.atan2(center.x - next.x, center.y - next.y)-
Math.atan2(previous.x- center.x,previous.y- center.y));
}
Now, this method should work in theory (I am testing to be sure and will edit my answer if I find any issues with signs of angles or something like that). I hope you get the idea and that my comments explain the code well enough, but please leave a comment/question if you want me to elaborate further. If you don't understand the algorithm itself, I recommend getting a piece of paper out and following the algorithm to see what exactly is going on. Hope this helps!
EDIT: To hopefully aid in better understanding the solution using angles, I drew a picture with the four base cases of how the start, end, first, and next could be oriented, and have attached it to this question. Sorry for the sloppiness, I drew it rather quickly, but this should in theory make the idea clearer.
If you have a low segment count (for instance, your example only shows 12 segments for three shapes, two shapes of which we know we can ignore (because of bounding box checks), then I would recommend simply performing line/line intersection checking.
Point s = your selected point;
ArrayList<Point> points = polygon.getPoints();
ArrayList<Edge> edges = polygon.getEdges();
for(Point p: points) {
Line l = new Line(s, p);
for(Edge e: edges) {
Point i = e.intersects(l);
if (i != null) {
System.out.println("collision", i.toString());
}
}
}
With an intersects method that is pretty straight forward:
Point intersects(Line l) {
// boring variable aliassing:
double x1 = this.p1.x,
y1 = this.p1.y,
x2 = this.p2.x,
y2 = this.p2.y,
x3 = l.p1.x,
y2 = l.p1.y,
x3 = l.p2.x,
y2 = l.p2.y,
// actual line intersection algebra:
nx = (x1 * y2 - y1 * x2) * (x3 - x4) -
(x1 - x2) * (x3 * y4 - y3 * x4),
ny = (x1 * y2 - y1 * x2) * (y3 - y4) -
(y1 - y2) * (x3 * y4 - y3 * x4),
d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d == 0) return null;
return new Point(nx/d, ny/d);
}

how to divide a rectangle

I have four latitude and longitude values which creates a rectangle(area).Now I want to divide the rectangle into grids.
So I define the latlong
(11.20804, 122.22839),(11.20804, 122.52914),(11.06926, 122.52914),(11.06926, 122.22839)
After dividing the grids I want to give each grid an id and want the latitude and longitude values of each grid_id.I am struggling how to divide the rectangle into grids and assign them grid_id.
Any help is appreciated
Something like this. Will need to play around with it and decide if your ok if the bottom won't have a perfect square. This is meant as a starting point, not a complete solution.
I just read that you want grid ids, so guessing like 1,1, so your going to have to adjust this to suite your needs.
double width = 122.52914 - 122.22839;
double height = 11.20804 - 11.06926;
int numbSquares = 10;
int lengthOfOneSquare = width/numbSquares;
HashMap<Double[], String> hmap = new HashMap<Double[], String>();
for (double x = 11.20804; x <= 11.06926 - lengthOfOneSquare; x += lengthOfOneSquare) {
for (double y = 122.22839; y <= 122.52914 - lengthOfOneSquare; y+= lengthOfOneSquare) {
Double[] location = new Double[]{x, y};
String id = x+""+y; <--- converts x and y to a string
hmap.put(location, id);
}
}

Detecting closest element in array

I'm having issues detecting the closest element in an array of blocks to the player (using circles).
What I have so far is:
public static int closestBarrier(GameObject object, GameObject[] barriers) {
int closest = -1;
float minDistSq = Float.MAX_VALUE;// ridiculously large value to start
for (int i = 0; i < barriers.length; i++) {
float barrierRadius = barriers[i].getWidth() / 2;
float objectRadius = object.getWidth() / 2;
GameObject curr = barriers[i];// current
float dx = (object.getX()) - ((curr.getX()));
float dy = (object.getY()) - ((curr.getY()));
float distSq = (((dx * dx + dy * dy) - objectRadius) - barrierRadius) ;// use the squared distance
if (distSq < minDistSq) {// find the smallest and remember the id
minDistSq = distSq;
closest = i;
}
}
return closest;
}
It passes most of the tests but on the final one the returned index of the closest one is 2 instead of the expected 3. Here are the tests (it is failing 'closest to fourth' :
public final void testClosestBarrier() {
// Closest to first
GameObject player = new GameObject(0,1);
player.setWidth(2);
GameObject[] barriers = new GameObject[4];
barriers[0] = new GameObject(8,9);
barriers[0].setWidth(3);
barriers[1] = new GameObject(10,15);
barriers[1].setWidth(2);
barriers[2] = new GameObject(15,20);
barriers[2].setWidth(5);
barriers[3] = new GameObject(100,210);
barriers[3].setWidth(10);
assertEquals("Player closest to first barrier",0,
Submission.closestBarrier(player,barriers));
// Closest to second
player.setX(12);
player.setY(12);
assertEquals("Player closest to second barrier",1,
Submission.closestBarrier(player,barriers));
// Closest to third
player.setX(12);
player.setY(20);
assertEquals("Player closest to third barrier",2,
Submission.closestBarrier(player,barriers));
// Closest to fourth
player.setX(90);
player.setY(100);
assertEquals("Player closest to fourth barrier",3,
Submission.closestBarrier(player,barriers));
}
Your code is correct and the test you have written is wrong - barrier 2 is closer to 90,100 than barrier 3.
barrier 2:
(90-15)^2 + (100-20)^2
12025
barrier 3:
(100-90)^2 + (210-100)^2
12200
12025 < 12200 -> barrier 2 is closer
EDIT: In your edited answer, you forgot to square objectRadius and barrierRadius. e.g.
float distSq = (((dx * dx + dy * dy) - objectRadius) - barrierRadius)
In this line, you have dx^2, dy^2 but only non-squared objectRadius and barrierRadius.

How to find points of intersection between ellipse and line?

I'm completely stuck.
I have an ellipse, and a line. Line is set by two points, ellipse - by bottom-left and top-right corners. I have to find their points of intersection, using java.
I tried to solve an equation system:
(1) y = kx + m;
x^2/a^2 + y^2/b^2 = 1;
but I could't make things work properly. I assume it's because of java's coordinate system, but it also may be my own mistake somewherem beacuse I'm confused with it.
Is there any better way to find points of intersection and, if not, how can I get them properly?
Thank you in advance.
Code:
double r1 = in_y2-in_y;
double r2 = in_x2-in_x;
double k = r1/r2;
double m = in_y2 - k*in_x2;
double a = Math.abs((double)x2 - (double)x)/2;
double b = Math.abs((double)y2 - (double)y)/2;
double A1 = 1/(a*a) + (k*k)/(b*b);
double B1 = (2*k*m)/b*b;
double C1 = (m*m)/(b*b);
double D = Math.sqrt(B1*B1 - 4*A1*C1);
double ex1 = (-B1 + D/(2*A1));
double ey1 = k*ex1 + m;
double ex2 = (-B1 - D/(2*A1));
double ey2 = k*ex2 + m;
This is probably no longer relevant to the original problem owner, but since I encountered the same question, let me present my answer.
There are three mistakes in the original computation that I can see: (i) the one pointed out by #mprivat, (ii) the bracketing in the assignment to B1 which should instead be
double B1 = (2*k*m)/(b*b);
and (iii) a more fundamental one: the presented computation does not correct for the origin of the ellipse. Since the ellipse is defined by itse circumscribing bounds, there is no guarantee that it is centered on (0,0).
Let's call the center (cx,cy), then the equation for the ellipse becomes
(x-cx)^2/a^2 + (y-cy)^2/b^2 = 1
rather than the original
x^2/a^2 + y^2/b^2 = 1
The simple repair, I think, is to translate the line wrt (cx,cy) and translate the results back, like so:
...
double m = (in_y2-cy) - k*(in_x2-cx);
...
double ex1 = (-B1 + D/(2*A1)) + cx;
double ey1 = k*(ex1-cx) + m + cy;
double ex2 = (-B1 - D/(2*A1)) + cx;
double ey2 = k*(ex2-cx) + m + cy;
The more elegant repair is to solve the correct equation for the ellipse instead, but this results in even more impenetrable formulas for B1 and C1:
double B1 = (2*k*(m-cy))/(b*b) - (2*cx)/(a*a);
double C1 = (m-cy)*(m-cy)/(b*b) - 1 + (cx*cx)/(a*a);
As a final remark, note that this breaks down for vertical lines, as then r2 = 0 so k isn't defined.
Java can't solve the algebra problem, but it can compute the solution once you tell it what to compute.
Sounds like you just need to replace your y in the ellipse's equation with kx+m then solve for x. Looks like it's a simply binomial equation. Write a program that computes x=... based on k, m, a and b. Java can help you compute the roots if you tell it what to compute and what the values of k, m, a and b are.
In your particular case, you want to use Java as a simple calculator...
can you please put your code so that we could see if it's correct?
anyway , here's an algorithm:
http://mathworld.wolfram.com/Ellipse-LineIntersection.html
note that since it has a square root , you might get a solution that is not precise.
Line2D.Double line = new Line2D.Double(x1,y1,x2,y2);
Ellipse2D.Double ellipse = new Ellipse2D.Double(x,y,width,height);
int resolution = 1000;
int x_distance = ellipse.getWidth()/2;
int y_distance = ellipse.getHeight()/2;
double angle = 360.0/(double)resolution;
Point center = new Point(width/2,height/2);
Point point = new Point();
for (int index = 0; index < resolution; index++)
{
int x = (center.x+x_distance)*Math.sin(Math.toRadians(angle*index)));
int y = (center.y+y_distance)*Math.cos(Math.toRadians(angle*index)));
Ellipse2D.Double dot = new Ellipse2D.Double(x,y,1,1);
if (line.intersects(dot.getBounds()))
{
point.setLocation(x,y);
index = resolution;
}
}

Categories

Resources