How can I make a algorithm that detects if a point (x, y) intercepts with a line. (x1, y1, x2, y2)?
I have already tried :
boolean onLine(float a, float b, float c, float d, float x, float y){
boolean answer = false;
float[] p1 = new float[] {a, b};
float[] p2 = new float[] {c, d};
float x_spacing = (p2[0] - p1[0]) / ((a+c)/2 + (b+d));
float y_spacing = (p2[1] - p1[1]) / ((a+c)/2 + (b+d));
List<float[]> line = new ArrayList();
float currentX = 0;
float currentY = 0;
while(currentX+a<c&¤tY+b<d){
currentX += x_spacing;
currentY += y_spacing;
line.add(new float[]{a+currentX, b+currentY});
}
for(int j = 0; j < line.size(); j++){
if(x > line.get(j)[0]-x_spacing && x < line.get(j)[0]+x_spacing && y > line.get(j)[1]-
y_spacing && y < line.get(j)[1]+y_spacing){
answer = true;
println("Hit line!");
break;
}
}
return answer;
}
This works sometimes, but is not always consistent.
I am putting this with a physics game, and I need this so the ball can roll down a line.
What are some ways I can improve it so that it works?.
EDIT: Thanks to Felix Castor I got it working. Here is the final Code:
boolean onLine(float x1, float y1, float x2, float y2, float xt, float yt,
float wid, float hit){
float Y = (y2 - y1)/(x2 - x1)* xt + y1 -(y2 - y1)/(x2 - x1) * x1;
boolean answer = false;
if(abs(Y - yt) < 5) answer = true;
if(abs(Y - yt-hit) < 5) answer = true;
if(abs(Y - yt-(hit/2)) < 5) answer = true;
if(abs(Y - yt+hit) < 5) answer = true;
if(abs(Y - yt+(hit/2)) < 5) answer = true;
return answer;
}
Using slope intercept form you can plug in your x and see if the y's are equal.
y = m*x + b
m = (y2 - y1)/(x2 - x1)
b = y1 - (y2 - y1)/(x2 - x1) * x1
So the equation becomes
Y = (y2 - y1)/(x2 - x1)* X + y1 -(y2 - y1)/(x2 - x1) * x1
given a point (xt, yt) you can plug in the xt into X and evaluate then compare the result to yt. If they are equal then the point is on the line.
if Y == yt given xt then the point is on the line.
You will need to handle the case where you have strictly horizontal lines as edge cases. Those will blow up the equation.
Edit: Conditions Changed
Since you are wanting to determine how far from the line a point is I would say the formula for the distance between a point and a line in cartesian space would be the way to go. See Distance from a point to a line section Line Defined By Two Points. The formula looks ugly but it's straight forward.
double numerator = Math.abs((y2 - y1) * xt - (x2 - x1) * yt + x2 * y1 - y2 * x1);
double denominator = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2));
double distance = numerator / denominator;
As before your test point is (xt, yt) and your line is defined by two points (x1, y1) and (x2, y2). Because distance is always >= 0 your test would be:
if( distance <= tolerance) return true
I think this is a better approach if you are interested in a tolerance.
Related
I'm trying to calculate the slope given a set of points (queenx, queeny with the index number) to find out if two points are horizontal, vertical, or diagonal to each other. Here is the following code:
Note that the method checkSlope simply calculates the slope. I don't understand what's wrong with this method, given the following points:
[0, 0] [6, 1] [4, 2] [7, 3] [1, 4] [3, 5] [5, 6] [2, 7], it still shows that there is match, when there really isn't.
for(int i = 0; i < 8; i++) {
int x1 = queenx.get(i);
int y1 = queeny.get(i);
for(int j = 0; j < 8; j++) {
int x2 = queenx.get(j);
int y2 = queeny.get(j);
if(i != j) {
double slope = Math.abs(checkSlope(x1, y1, x2, y2));
try {
if ((slope == 1) || (slope == 0)) {
correct = false;
System.out.println("x1 = " + x1 + " y1 = " + y1 + " x2 = " + x2 + " y2 = " + y2);
break;
}
} catch(Exception e) {
correct = false;
break;
}
}
}
}
public static double checkSlope(int x1, int y1, int x2, int y2) {
return (double)((y2 - y1) / (x2 - x1));
}
The problem is you're converting to double too late. The division is using integers. Try removing the outer parens: (double)(y2 - y1) / (x2 - x1);
But that's a hard way to do it. Also, floating point computations are tricky, as you you've learned. E.g. the slope computation will fail if the denominator is zero.
A simpler way: There are 4 cases:
On the same horizontal line: y1 == y2
On the same vertical line: x1 == x2
On the same positive slope diagonal: y2 - y1 == x2 - x1
On the same negative slope diagonal: y2 - y1 == x1 - x2
You just need to test these four cases. You can make things neat by factoring differences:
int dy = y2 - y1;
int dx = x2 - x1;
if (dx == 0 || dy == 0 || dx == dy || dx == -dy) {
// match!
}
If you like, you can replace the last two "or" cases with one: abs(dx) == abs(dy).
point 1 = (x,y) point2 = (a,b)
checking for diagonals:
x+y == a+b
a-b == x-y (take absolute values)
along same horizontal line : y ==b
along same vertical line : x==a
I have a ellipse with a mid-point 'mid' an a horizontal radius 'h' and a vertical radius 'v' and a Line2D.
Now I need some code to calculate the two intersection points of the two.
I already tried some code and tried it on my own but there always is a mistake.
Does somebody have some working code?
You will need to use algebra to solve the two equations, and it will get a little bit messy. First you have to write out the ellipse
(1) (x/h)^2 + (y/v)^2 = 1
and the line in the format
(2) y = ax + b
First, shift over your coordinate axis so the ellipse is centered on the origin. You can do that by subtracting mid from the line. Once you have calculated the points of intersection, shift them back by adding mid.
You can calculate the linear slope from delta-y/delta-x from the starting and ending points of the line. You will have to check if the slope is vertical. If the slope is vertical, you just have to check whether or not the x-value of the line points falls in the location of the ellipse, and then easily calculate the values. Draw it out on paper and see how to calculate it.
Assume now that the slope is not vertical. Since you know y in terms of x from the line, square that and substitute into (1). Simplifying gives a quadratic equation.
(3) ((ah)^2+v^2)x^2 + (2abh^2)x + ((hb)^2-(hv)^2) = 0
Using the quadratic formula gives the two values for the x coordinates of the intersection. If there are two real values for x, there are two intersections. If there is only one real solution for x, there is one intersection. If there are no real solutions for x, there is no intersection.
Given ax^2
+ bx + c = 0, x is given by
x = (1/2a)(-b +- Sqrt(b^2 - 4ac))
Let D = b^2 - 4ac
If D < 0, there are no intersections
If D = 0, there is one intersection
If D > 0, there are two intersections
Once you have calculated the values of the x intersection, substitute the values of x into (2) to get the y values.
Now, you need to make sure that these points fall within the line. To do this, just check that the x and y components of the calculated points satisfy x1 <= x <= x2 and y1 <= y <= y2 where x1 is the smallest and x2 the largest x-endpoint of the line, and y1 is the smallest and y2 the largest y-endpoint of the line.
Here is an example method that I made
public static ArrayList<Point2D> getIntersection(double x1, double x2, double y1, double y2, double midX, double midY, double h, double v) {
ArrayList<Point2D> points = new ArrayList();
x1 -= midX;
y1 -= midY;
x2 -= midX;
y2 -= midY;
if (x1 == x2) {
double y = (v/h)*Math.sqrt(h*h-x1*x1);
if (Math.min(y1, y2) <= y && y <= Math.max(y1, y2)) {
points.add(new Point2D(x1+midX, y+midY);
}
if (Math.min(y1, y2) <= -y && -y <= Math.max(y1, y2)) {
points.add(newPoint2D(x1+midX, -y+midY);
}
}
else {
double a = (y2 - y1) / (x2 - x1);
double b = (y1 - a*x1);
double r = a*a*h*h + v*v;
double s = 2*a*b*h*h;
double t = h*h*b*b - h*h*v*v;
double d = s*s - 4*r*t;
if (d > 0) {
double xi1 = (-s+Math.sqrt(d))/(2*r);
double xi2 = (-s-Math.sqrt(d))/(2*r);
double yi1 = a*xi1+b;
double yi2 = a*xi2+b;
if (isPointInLine(x1, x2, y1, y2, xi1, yi1)) {
points.add(new Point2D.Double(xi1+midX, yi1+midY);
}
if (isPointInLine(x1, x2, y1, y2, xi2, yi2)) {
points.add(new Point2D.Double(xi2+midX, yi2+midY);
}
}
else if (d == 0) {
double xi = -s/(2*r);
double yi = a*xi+b;
if (isPointInLine(x1, x2, y1, y2, xi, yi)) {
points.add(new Point2D.Double(xi+midX, yi+midY));
}
}
}
return points;
}
public static boolean isPointInLine(double x1, double x2, double y1, double y2, double px, double py) {
double xMin = Math.min(x1, x2);
double xMax = Math.max(x1, x2);
double yMin = Math.min(y1, y2);
double yMax = Math.max(y1, y2);
return (xMin <= px && px <= xMax) && (yMin <= py && py <= yMax);
}
Feel free to check my algebra and my code, but you should solve this problem by carefully going through each algebraic step.
I would use the built in ellipse and line/polygon classes there they both have methods to determine collisions and intersection
When the line is given by two points P0 and P1, any point along the line is (X, Y) = (X0, Y0) + t (X1 - X0, Y1 - Y0) = (X0, Y0) + t (DX, DY).
The ellipse is (X - Xm)²/h² + (Y - Ym)²/v² = 1.
We will use a trick to simplify computation: take all X, subtract Xm and divide by h, giving x; take all Y, subtract Ym and divide by h, giving y. This will turn the ellipse into a circle centered at the origin. (You can do all computations without this reduction of the coordinates if you prefer.)
Now, (x, y) = (x0, y0) + t (dx, dy) and x² + y² = 1.
Or (t dx + x0)² + (t dy + y0)² = 1.
Or (dx² + dy²) t² + 2 (dx x0 + dy y0) t + (x0² + y0² - 1) = 0.
Solve this second degree equation for t. If there are real roots, you can check if they belong to the line segment by the condition 0 <= t <= 1. The intersections themselves are given by the first equation.
I'm writing a method to return a List of Points between 2 Points. Somehow the slope (y2-y1)/(x2-x1) keeps giving me 0.0 all the time regardless startPoint and endPoint positions.
Here is the method:
public ArrayList<Point> calculatePath(Point startPoint, Point endPoint) {
ArrayList<Point> calculatedPath = new ArrayList<>();
int x1 = startPoint.x;
int y1 = startPoint.y;
int x2 = endPoint.x;
int y2 = endPoint.y;
System.out.println("Run");
if ((x2 - x1) != 0) {
float ratio = ((y2 - y1) / (x2 - x1));
System.out.println(ratio);
int width = x2 - x1;
for (int i = 0; i < width; i++) {
int x = Math.round(x1 + i);
int y = Math.round(y1 + (ratio * i));
calculatedPath.add(new Point(x, y));
}
} else {
if (y1 < y2) {
while (y1 == y2) {
calculatedPath.add(new Point(x1, y1));
y1++;
}
} else {
while (y1 == y2) {
calculatedPath.add(new Point(x1, y1));
y1--;
}
}
}
return calculatedPath;
}
Can anyone point out what i'm doing wrong? Thanks
Try casting your ints into floats as well
During your caluclation you need to cast at least one element to float:
float ratio = ((float)(y2 - y1) / (float)(x2 - x1));
That is because:
float a = integer / integer
^^^^^^^^^^^^^^^^^ - The result will be an integer.
Therefore u need to cast at least one of the
to float
This examples shows it easly:
public static void main(String[] args)
{
float resultWithoutCast = 5 / 3;
float resultWithCast = (float)5 /3 ;
System.out.println(resultWithoutCast);
System.out.println(resultWithCast);
}
It will print
1.0
1.6666666
You forgot to cast your int during division. Try something like this:-
float ratio = ((float)(y2 - y1) / (x2 - x1));
When performing arithmetic you need to make sure you use types which allow for the expected result. For example, the issue with your code is you are looking for a floating point result but using int - the problem here is int will simply truncate any floating point.
There are a couple of ways to solving this problem - as already suggested you could use a cast
float ratio = ((float)(y2 - y1) / (x2 - x1));
Or you could use float variables, it makes for more readable code e.g.
float x1 = (float)startPoint.X;
float y1 = (float)startPoint.Y;
...
float ratio = (y2 - y1) / (x2 - x1);
However, this results in more casting.
Alternatively, you could swap out Point for PointF and eliminate casting completely.
I have a path stored as points in an arraylist and I want to check if the line segments are intersecting. For some unknown reason it's not working! I don't get any message in the LogCat despite that I'm drawing a shape that intersects. Preciate if someone could see what I have done wrong or have suggestions how to improve code.
// Intersection control
if(touchActionUp) {
// Loop throw all points except the last 3
for (int i = 0; i < points.size()-3; i++) {
Line line1 = new Line(points.get(i), points.get(i+1));
// Begin after the line above and check all points after that
for (int j = i + 2; j < points.size()-1; j++) {
Line line2 = new Line(points.get(j), points.get(j+1));
// Call method to check intersection
if(checkIntersection(line1, line2)) {
Log.i("Intersection", "Yes!");
}
}
}
}
And the method:
// Method to check for intersection between lines
private boolean interceptionControl(Line line1, Line line2) {
int x1 = line1.pointX1;
int x2 = line1.pointX2;
int x3 = line2.pointX1;
int x4 = line2.pointX2;
int y1 = line1.pointY1;
int y2 = line1.pointY2;
int y3 = line2.pointY1;
int y4 = line2.pointY2;
// Check if lines are parallel
int denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if(denom == 0) { // Lines are parallel
// ??
}
double a = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
double b = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
// Check for intersection
if( a >= 0.0f && a <= 1.0f && b >= 0.0f && b <= 1.0f) {
return true;
}
return false;
}
You are using int for coordinates and so it does integer division (i.e., 3/2 = 1). This might be the reason when you are dividing by denom. You can fix it by dividing by ((double)denom) instead of simply denom.
I have looked around for the past hour or so but I can not find any help on this problem. I am trying to convert this pseudocode to java and can not figure out what I am doing wrong(it ever prints anything).
function line(x0, x1, y0, y1)
boolean steep := abs(y1 - y0) > abs(x1 - x0)
if steep then
swap(x0, y0)
swap(x1, y1)
if x0 > x1 then
swap(x0, x1)
swap(y0, y1)
int deltax := x1 - x0
int deltay := abs(y1 - y0)
real error := 0
real deltaerr := deltay / deltax
int ystep
int y := y0
if y0 < y1 then ystep := 1 else ystep := -1
for x from x0 to x1
if steep then plot(y,x) else plot(x,y)
error := error + deltaerr
if error ≥ 0.5 then
y := y + ystep
error := error - 1.0
My conversion is:
public static void line(int x0,int x1,int y0,int y1) {
boolean steep = Math.abs(y1 - y0) > Math.abs(x1 - x0);
if(steep) {
swap(x0, y0);
swap(x1, y1);
}
if (x0 > x1) {
swap(x0, x1);
swap(y0, y1);
}
int deltax = x1 - x0;
int deltay = Math.abs(y1 - y0);
float error = 0;
float deltaerr = deltay / (float)deltax;
int ystep;
int y = y0;
if(y0 < y1) ystep = 1;
else ystep = -1;
//for x from x0 to x1
for(int x = x0; x < x1;x++)
if (steep) plot(y,x);
else plot(x,y);
error = error + deltaerr;
if (error >= 0.5f) {
y = y + ystep;
error = error - 1.0f;
}
}
//method plot
private static void plot(int x, int y) {
System.out.println(x+":"+y);
}
//method swap
private static void swap(int x0, int x1) {
int copy = x0;
x0 = x1;
x1 = copy;
}
Can someone help?
You cannot use a swap() method like that with ints. Since they are primitives, they are passed by value, and changing the local variables inside the method will have no effect on the variables you use as arguments.
Do the swapping directly in the line() method instead.
A second thing is that your for loop looks wrong. Based on your indentation, you probably want to use curly braces around that entire block.