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));
}
}
}
}
}
}
}
}
}
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'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 want to create a Java method which returns the lowest root of a quadratic equation in the interval (0, 1). If there are no solutions in the interval, return 1. I need some help making this an efficient algorithm.
This is my current method:
public static float getLowestRoot(float A, float B, float C) {
float D = B*B - 4*A*C;
if(D < 0) return 1;
float sD = (float) Math.sqrt(D);
float x1 = (-B + sD) / (2*A);
float x2 = (-B - sD) / (2*A);
if(x2 < x1) {
float tmp = x2;
x2 = x1;
x1 = tmp;
}
if(x1 > 0 && x1 < 1) return x1;
if(x2 > 0 && x2 < 1) return x2;
return 1;
}
This method does the job but I was wondering if there is a way to compress the algorithm, because right now it feels bloated.
1) note that "less lines of code" isn't the same as "better performance".
2) you can consider Math.abs(sD) < Epsilon - if yes, then you don't have to calculate both roots. I'm guessing that this can improve speed in such cases.
3) I think you can improve checking which root is smaller:
x1 <= x2 <===> -B+sD/(2A) <= -B-sD/(2A) <==(adding sD/(2A) to both sides)==>
<===> -B+2sD/(2A) <= -B/(2A) <==(adding B/(2A) to both sides)==>
<===> 2sD/(2A) <= 0
<===> A <= 0 (because sD >= 0)
So you can avoid swapping the roots:
int signA = Math.signum(A);
float x1 = (-B + -signA * sD) / (2*A);
float x2 = (-B + signA * sD) / (2*A);
// always x1 <= x2
Again, I'm guessing that this improves performance, but I didn't measure it.
So, the final answer would look something like this:
public static float getLowestRoot(float A, float B, float C) {
float D = B*B - 4*A*C;
if (D < 0) return 1;
if (Math.abs(D) < 0.0001) // not sure how many 0s for float
{
float x = -B / (2*A);
if (x > 0 && x < 1) return x;
return 1;
}
float sD = (float) Math.sqrt(D);
int signA = Math.signum(A);
float x1 = (-B + -signA * sD) / (2*A);
float x2 = (-B + signA * sD) / (2*A);
if (x1 > 0 && x1 < 1) return x1;
if (x2 > 0 && x2 < 1) return x2;
return 1;
}
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.