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
Related
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.
I stumbled upon this problem while solving an algorithm question. There is a rectangular matrix.
I get a pair of positions as input, then how can I calculate if they fall in a straight line, for eg. in this case I get input as (d,B)(c,D)(b,F)(a,H) which infact is straight line.
If we see it carefully, the counter on the short side is jumping by 1 and counter on the longer side is jumping by 2. If I write my code based on this logic then, will it be a safe assumption for a bigger rectangles or I can face issue with that logic ?
I'm assuming here that inclined straight line can only be of two types
1) Where both the counters jump by 1, like the diagonal of a square matrix.
2) The case discussed above where counter is jumping by 1 on shorter side and by 2 on longer side.
Can there be a group of points that may fall in straight line but does not fit either of the above conditions and neither they are in a single row or a single column ?
Well, let's start from two degenerated cases:
if you have 0 points, answer as you wish (either there's a line, or there's no such line)
if you have 1 or 2 points, the answer is always yes
Suppose, you have 3+ points and you want to check if they are all on the same line. Take two arbitrary points (x1, y1) and (x2, y2). Once again you have two cases:
x1 == x2; check that all the points are such that xi == x1
x1 != x2; check that all points are such that two conditions are met:
(y1 - y2) * (xi - x2) == (yi - y2) * (x1 - x2)
(y2*x1 - y1*x2) * (xi - x2) == (y2*xi - yi*x2) * (x1 - x2)
I.e. all points are on the same y = kx + b line where k and b are derived from (x1, y1) and (x2, y2)
In you case, when A = 1, B = 2, ..., H = 8; a = 1, b = 2, ..., d = 4 we have
(2, 4)
(4, 3)
(6, 2)
(8, 1)
points which are on the same line. Possible (C#) implementation:
private static bool SameLine(IEnumerable<Tuple<int, int>> points) {
if (null == points)
return true;
Tuple<int, int>[] data = points.ToArray();
// i = 2 - first two points are always on the same line
for (int i = 2; i < data.Length; ++i) {
int x1 = data[0].Item1;
int y1 = data[0].Item2;
int x2 = data[1].Item1;
int y2 = data[1].Item2;
int xi = data[i].Item1;
int yi = data[i].Item2;
// y = k * x + b where k = infinity
if (x1 == x2) {
if (xi != x1)
return false;
continue;
}
// Same k in y = k * x + b
if (!((y1 - y2) * (xi - x2) == (yi - y2) * (x1 - x2)))
return false;
// Same b in y = k * x + b
if (!((y2 * x1 - y1 * x2) * (xi - x2) == (y2 * xi - yi * x2) * (x1 - x2)))
return false;
}
return true;
}
Test
Tuple<int, int>[] test = new Tuple<int, int>[] {
new Tuple<int, int>(2, 4),
new Tuple<int, int>(4, 3),
new Tuple<int, int>(6, 2),
new Tuple<int, int>(8, 1),
};
// Same line
Console.Write(SameLine(test) ? "Same line" : "different lines");
I have created a function to calculate the intersection point of two line segment .
Unfortunantly the code below dosen't work if one of the segment is verticale
public static Point intersection(Segment s1, Segment s2) {
double x1 = s1.getP1().getX();
double y1 = s1.getP1().getY() ;
double x2 = s1.getP2().getX();
double y2 = s1.getP2().getY() ;
double x3 = s2.getP1().getX();
double y3 = s2.getP1().getY();
double x4 = s2.getP2().getX();
double y4 = s2.getP2().getY();
double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d == 0) {
return null;
}
double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
Point p = new Point(xi, yi);
if (xi < Math.min(x1, x2) || xi > Math.max(x1, x2)) {
return null;
}
if (xi < Math.min(x3, x4) || xi > Math.max(x3, x4)) {
return null;
}
return p;
}
the problem when i have a vertical line segment , this formula
double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
is equal to 0 and the method return null.
How can I handle this exception.
Thank you
Line intersection without special cases
Coming from a background of projective geometry, I'd write the points in homogeneous coordinates:
v1 = [x1, y1, 1]
v2 = [x2, y2, 1]
v3 = [x3, y3, 1]
v4 = [x4, y4, 1]
Then both the line joining two points and the intersection of two lines can be expressed using the cross product:
[x5, y5, z5] = (v1 × v2) × (v3 × v4)
which you can dehomogenize to find the resulting point as
[x5/z5, y5/z5]
without having to deal with any special cases. If your lines are parallel, the last point would lead to a division by zero, though, so you might want to catch that case.
Restriction to segments
The above is for infinite lines, though. You might want to keep the code which returns null if the point of intersection falls outside the bounding box. But if you want real segments, that code is incorrect: you could have a point of intersection which lies outside one of the segments but still inside the bounding box.
A proper check can be implemented using an orientation-checking predicate. The determinant of three of the vectors vi given above will have positive sign if the triangle they form has one orientation, and negative sign for the opposite orientation. So the points v3 and v4 lie on different sides of s1 if
det(v1, v2, v3) * det(v1, v2, v4) < 0
and in a similar way v1 and v2 lie on different sides of s2 if
det(v3, v4, v1) * det(v3, v4, v2) < 0
so if both of these are satisfied, you have an intersection between the segments. If you want to include the segment endpoints, change the < to a ≤ in these inequalities.
I have tried code it without testing... I hope it works! ^^
public static Point intersection(Segment s1, Segment s2) {
// Components of the first segment's rect.
Point v1 = new Point(s1.p2.x - s1.p1.x, s1.p2.y - s1.p1.y); // Directional vector
double a1 = v.y;
double b1 = -v.x;
double c1 = v1.x * s1.p1.y - s1.v.y * s1.p1.x;
// Components of the second segment's rect.
Point v2 = new Point(s2.p2.x - s2.p1.x, s2.p2.y - s2.p1.y);
double a2 = v2.y;
double b2 = -v2.x;
double c2 = v2.x * s2.p1.y - s2.v.y * s2.p1.x;
// Calc intersection between RECTS.
Point intersection = null;
double det = a1 * b2 - b1 * a2;
if (det != 0) {
intersection = new Point(
(b2 * (-c1) - b1 * (-c2)) / det;
(a1 * (-c2) - a2 * (-c1)) / det;
);
}
// Checks ff segments collides.
if (
intersection != null &&
(
(s1.p1.x <= intersection.x && intersection.x <= s1.p2.x) ||
(s1.p2.x <= intersection.x && intersection.x <= s1.p1.x)
) &&
(
(s1.p1.y <= intersection.y && intersection.y <= s1.p2.y) ||
(s1.p2.y <= intersection.y && intersection.y <= s1.p1.y)
) &&
(
(s2.p1.x <= intersection.x && intersection.x <= s2.p2.x) ||
(s2.p2.x <= intersection.x && intersection.x <= s2.p1.x)
) &&
(
(s2.p1.y <= intersection.y && intersection.y <= s2.p2.y) ||
(s2.p2.y <= intersection.y && intersection.y <= s2.p1.y)
)
)
return intersection;
return null;
};
Here's my answer. I have tested it to be accurate by making a loop that checks that the answer it gives is the same as the one given by the Boost geometry library and they agree on each test, though the one that I have written below is much much faster than the one in Boost. The test makes every possible line segment where x is and integer in [-3,2] and y is an integer in [-3,2], for all possible pairs of line segments.
The code below considers line segments that touch at endpoints to be intersecting.
T-shaped intersections are also considered intersecting. The code is in c++ but would be easily adaptable to any language. It's based on a different stackoverflow answer but that answer did not handle endpoints correctly.
It uses the cross product method, which can report if a point is to the left or to the right of a given ray.
There are some optimizations to be made in the math but doing them showed no performance improvement over compilation with g++ -O2 and sometime the performance even decreased! The compiler is able to do those optimizations so I prefered to leave the code readable.
// is_left(): tests if a point is Left|On|Right of an infinite line.
// Input: three points p0, p1, and p2
// Return: >0 for p2 left of the line through p0 and p1
// =0 for p2 on the line
// <0 for p2 right of the line
// See: Algorithm 1 "Area of Triangles and Polygons"
// This is p0p1 cross p0p2.
extern inline coordinate_type_fp is_left(point_type_fp p0, point_type_fp p1, point_type_fp p2) {
return ((p1.x() - p0.x()) * (p2.y() - p0.y()) -
(p2.x() - p0.x()) * (p1.y() - p0.y()));
}
// Is x between a and b, where a can be lesser or greater than b. If
// x == a or x == b, also returns true. */
extern inline coordinate_type_fp is_between(coordinate_type_fp a,
coordinate_type_fp x,
coordinate_type_fp b) {
return x == a || x == b || (a-x>0) == (x-b>0);
}
// https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
extern inline bool is_intersecting(const point_type_fp& p0, const point_type_fp& p1,
const point_type_fp& p2, const point_type_fp& p3) {
const coordinate_type_fp left012 = is_left(p0, p1, p2);
const coordinate_type_fp left013 = is_left(p0, p1, p3);
const coordinate_type_fp left230 = is_left(p2, p3, p0);
const coordinate_type_fp left231 = is_left(p2, p3, p1);
if (p0 != p1) {
if (left012 == 0) {
if (is_between(p0.x(), p2.x(), p1.x()) &&
is_between(p0.y(), p2.y(), p1.y())) {
return true; // p2 is on the line p0 to p1
}
}
if (left013 == 0) {
if (is_between(p0.x(), p3.x(), p1.x()) &&
is_between(p0.y(), p3.y(), p1.y())) {
return true; // p3 is on the line p0 to p1
}
}
}
if (p2 != p3) {
if (left230 == 0) {
if (is_between(p2.x(), p0.x(), p3.x()) &&
is_between(p2.y(), p0.y(), p3.y())) {
return true; // p0 is on the line p2 to p3
}
}
if (left231 == 0) {
if (is_between(p2.x(), p1.x(), p3.x()) &&
is_between(p2.y(), p1.y(), p3.y())) {
return true; // p1 is on the line p2 to p3
}
}
}
if ((left012 > 0) == (left013 > 0) ||
(left230 > 0) == (left231 > 0)) {
if (p1 == p2) {
return true;
}
return false;
} else {
return true;
}
}
The test code:
BOOST_AUTO_TEST_CASE(small, *boost::unit_test::disabled()) {
for (double x0 = -3; x0 < 3; x0++) {
for (double y0 = -3; y0 < 3; y0++) {
for (double x1 = -3; x1 < 3; x1++) {
for (double y1 = -3; y1 < 3; y1++) {
for (double x2 = -3; x2 < 3; x2++) {
for (double y2 = -3; y2 < 3; y2++) {
for (double x3 = -3; x3 < 3; x3++) {
for (double y3 = -3; y3 < 3; y3++) {
point_type_fp p0{x0, y0};
point_type_fp p1{x1, y1};
point_type_fp p2{x2, y2};
point_type_fp p3{x3, y3};
linestring_type_fp ls0{p0,p1};
linestring_type_fp ls1{p2,p3};
BOOST_TEST_INFO("intersection: " << bg::wkt(ls0) << " " << bg::wkt(ls1));
BOOST_CHECK_EQUAL(
path_finding::is_intersecting(p0, p1, p2, p3),
bg::intersects(ls0, ls1));
}
}
}
}
}
}
}
}
}
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 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.