So I need to know the Distance from a point to a line (in 2D space), given two coordinates of the line (AB).
Here is what I have so far:
public double pointToLineDistance(Point A, Point B, Point P)
{
double normalLength = Math.sqrt((B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y));
return Math.abs((P.x - A.x) * (B.y - A.y) - (P.y - A.y) * (B.x - A.x)) / normalLength;
}
But I also need to get the coordinates of the point where the perpendicular line intersects with the AB line (it's ok if it's outside this segment).
Any ideas?
Observe that AB can just be expressed as
ab = A + (B - A) * s
So, the direction of AB is B - A, or (B.x - A.x, B.y - A.y). A line whose direction is (A.y - B.y, B.x - A.x) will be perpendicular. (We just swap the x and y and negate one of them.)
We specifically want a line which is perpendicular to AB and also passes through P, so we do
perp = P + (A.y - B.y, B.x - A.x) * t;
perp = (P.x + A.y - B.y, P.y + B.x - A.x) * t;
Now just find the intersection between this perpendicular line and AB. You have two equations (for the x and y components of the intersection point) and two unknowns (s and t). Once you find s and t, plug them in to either of the lines' equations to get the intersection point.
Here is some working code:
static Vect2 getIntersection(Vect2 A, Vect2 B, Vect2 P) {
Vect2 abDir = B.minus(A);
Vect2 perpDir = new Vect2(-abDir.y, abDir.x);
Vect2 apDir = P.minus(A);
double s = (perpDir.y * apDir.x - perpDir.x * apDir.y)
/ (abDir.x * perpDir.y - abDir.y * perpDir.x);
return A.plus(abDir.scale(s));
}
class Vect2 {
final double x, y;
Vect2(double x, double y) {
this.x = x;
this.y = y;
}
Vect2 scale(double k) {
return new Vect2(x * k, y * k);
}
Vect2 plus(Vect2 that) {
return new Vect2(x + that.x, y + that.y);
}
Vect2 minus(Vect2 that) {
return this.plus(that.scale(-1));
}
}
The idea is to construct an equation of the line going through the points A and B. When you have constructed that equation, you construct an equation of a line that goes through P and is perpendicular to AB. The equation for the perpendicular has coefficients that are easily derived from the equation for the AB-line. Once you have two equations, solving them will give you the coordinate of the intersection.
Is this for homework?
This would help
http://paulbourke.net/geometry/pointline/
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.
The following code got 2 functions . (credit to #AlexRudenko who helped me)
They both get a segment, a line(not necessary an infinitive one). In addition to the one they already have.
The first function checks whether there is an intersection point(only one), and returns a boolean value.
The second one should check the same and return the point itself.
// Returns true if the lines intersect, false otherwise
public boolean isIntersecting(Line other) {
if (equals(other)){
return false;
}
double x11 = this.start.getX();
double y11 = this.start.getY();
double x12 = this.end.getX();
double y12 = this.end.getY();
double x21 = other.start.getX();
double y21 = other.start.getY();
double x22 = other.end.getX();
double y22 = other.end.getY();
if (x11 == x12 && x21 == x22) { // both lines are constant x
// no intersection point
return false;
} else if (x11 == x12 || x21 == x22) { // either line is constant x
double x;
double m;
double b;
if (x11 == x12) { // first line is constant x, second is sloped
x = x11;
m = (y22 - y21) / (x22 - x21);
b = (x22 * y21 - x21 * y22) / (x22 - x21);
} else { // second line is constant x, first is sloped
x = x21;
m = (y12 - y11) / (x12 - x11);
b = (x12 * y11 - x11 * y12) / (x12 - x11);
}
double y = m * x + b;
Point.intersection = new Point (x,y);
return true;
} else { // both lines are sloped
double m1 = (y12 - y11) / (x12 - x11);
double b1 = (x12 * y11 - x11 * y12) / (x12 - x11);
double m2 = (y22 - y21) / (x22 - x21);
double b2 = (x22 * y21 - x21 * y22) / (x22 - x21);
if ((long) m1 == (long) m2) {
// no intersection point
return false;
}
// calculating intersection coordinates
double x = (b2 - b1)/(m1 - m2);
double y = m1 * x + b1; // or m2 * x + b2
Point.intersection = new Point (x,y);
return true;
}
}
// Returns the intersection point if the lines intersect,
// and null otherwise.
public Point intersectionWith(Line other) {
if (isIntersecting(other)) {
return Point.intersection;
}
return null;
}
I thought about "saving" the point in a static variable in the first function before returning a "true" value, and then using this static variable to "pull" the point in the second function and return the values of it.
Also, do you think that regarding segments and not infinitive lines, I should add more conditions to the first function?
I mean yes, lets say the both have the same slope, but it does not mean they can't have only one intersection point. Because if we are talking about
Line1: (1,0) (1,2)
and Line2: (1,2) (1,3)
They both have the same slope, but they still have an intersection point at (1,2).
So what do you think I should add to the code to eliminate all these cases?
I thought about something like:
if(!this.start().equals(line.start())
||!this.start().equals(line.end())
||!this.end().equals(line.start())
||!this.end().equals(line.end()))
return false;
I think I should also add a small check that checks whether the intersection point is on both of the segments. I should take the X & Y values of the intersection point and check whether the X value is between the X value of the starting point and the X value of the ending point (on both segments) and do the same check for the Y value.
What do you think?
Thanks.
wouldn't it be easier to de it the other way around tho ?
public Point isIntersecting(Line other) {
//your calculation here
return Point
}
public Boolean intersectionWith(Line other) {
Point p=isIntersecting(other)
if(p==null)
return false
return true
}
I have a affine equation y = ax + b where a is the coefficient (coeff).
Let D be a line that goes through axis and described by the previous equation.
I'm trying with this piece of code to find the coordinates of the closest point on D to position (ignoring the y coordinate, because 2D in 3D)
double a = coeff;
double b = position.getZ();
double c = axis.getZ() - axis.getX() * coeff;
double x0 = position.getX();
double y0 = position.getZ();
return new Vector((b * (b * x0 - a * y0) - a * c) / (a * a + b * b), position.getY(),
(a * (-b * x0 + a * y0) - b * c) / (a * a + b * b));
Using this as a refernece
However, this does not work and return weird results
If you stay in vector representation it may be easier.
I have an example code but only in C++ (and Direct3D):
D3DXVECTOR3 ProjectOnLine (const D3DXVECTOR3 &point,
const D3DXVECTOR3 &linePoint,
const D3DXVECTOR3 &lineUnityDir)
{
float t = D3DXVec3Dot(&(point-linePoint), &lineUnityDir);
return linePoint + lineUnityDir*t;
}
If I understand you then your parameters can be used like this:
D3DXVECTOR3 point = position;
D3DXVECTOR3 linePoint = axis;
D3DXVECTOR3 lineUnityDir = D3DXVECTOR3(1, a, 0)/sqrt(1+a*a);
Does anyone have a function in java for finding the shortest distance between a point and a line segment/edge? Every example I find is in another language and uses a bunch of sub functions. It can't be based on the assumption that they are perpendicular.
Update
I ported over a python function to java. If anyone is good at math and can verify I would appreciate it. x and y is the point, and other params are for the line segment.
public float pDistance(float x, float y, float x1, float y1, float x2, float y2) {
float A = x - x1;
float B = y - y1;
float C = x2 - x1;
float D = y2 - y1;
float dot = A * C + B * D;
float len_sq = C * C + D * D;
float param = -1;
if (len_sq != 0) //in case of 0 length line
param = dot / len_sq;
float xx, yy;
if (param < 0) {
xx = x1;
yy = y1;
}
else if (param > 1) {
xx = x2;
yy = y2;
}
else {
xx = x1 + param * C;
yy = y1 + param * D;
}
float dx = x - xx;
float dy = y - yy;
return (float) Math.sqrt(dx * dx + dy * dy);
}
We can simplify things a bit. You don't need to calculate param. What you can do is find a vector v at right angles to the line. The take the dot product of that with the vector (A,B). In 2D its easy enough to find the vector orthogonal to (C,D), its just (-D,C).
public float pDistance(float x, float y, float x1, float y1, float x2, float y2) {
float A = x - x1; // position of point rel one end of line
float B = y - y1;
float C = x2 - x1; // vector along line
float D = y2 - y1;
float E = -D; // orthogonal vector
float F = C;
float dot = A * E + B * F;
float len_sq = E * E + F * F;
return (float) Math.abs(dot) / Math.sqrt(len_sq);
}
If you are worried about performance is can be easier to work with the squared distances then the last line would be
return (float) dot * dot / len_sq;
This saves having to calculate a square root. So if you want to calculate the closest edge, find the squared distances to each edge and select the smallest.
This function find the distance to the infinite line rather than the line segment. This may not be what you want. The solution in the question differs in what happens if the point is beyond the two ends of the line segment. There it find the distance to the closest end point.
From http://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
The distance (or perpendicular distance) from a point to a line is the
shortest distance from a point to a line in Euclidean geometry. It is
the length of the line segment which joins the point to the line and
is perpendicular to the line.
You say that "It can't be based on the assumption that they are perpendicular.", but the shortest distance between a point and a line segment represents another line which is perpendicular to the original line. Hence it is the height of the triangle formed by A B and C, where A - the point, B and C are the end points of the line segment.
We know the coordinates of all three points, therefore we can obtain lengths of sides of the triangle. Using Heron's formula: https://www.mathsisfun.com/geometry/herons-formula.html we can obtain the area which is also equal to 0.5 * b * h from: https://www.mathsisfun.com/algebra/trig-area-triangle-without-right-angle.html
private static float distBetweenPointAndLine(float x, float y, float x1, float y1, float x2, float y2) {
// A - the standalone point (x, y)
// B - start point of the line segment (x1, y1)
// C - end point of the line segment (x2, y2)
// D - the crossing point between line from A to BC
float AB = distBetween(x, y, x1, y1);
float BC = distBetween(x1, y1, x2, y2);
float AC = distBetween(x, y, x2, y2);
// Heron's formula
float s = (AB + BC + AC) / 2;
float area = (float) Math.sqrt(s * (s - AB) * (s - BC) * (s - AC));
// but also area == (BC * AD) / 2
// BC * AD == 2 * area
// AD == (2 * area) / BC
// TODO: check if BC == 0
float AD = (2 * area) / BC;
return AD;
}
private static float distBetween(float x, float y, float x1, float y1) {
float xx = x1 - x;
float yy = y1 - y;
return (float) Math.sqrt(xx * xx + yy * yy);
}
I do not know how correct it is, hopefully a real mathematician can correct or back up this solution
If your line passes through two points, you can determine the equation of the line exactly.
If your line is ax + by + c = 0 and your point is (x0, y0), then the distance is given by :
This gives the shortest distance between any line and a point. (a, b, c are real constants)
Edit : In order to find the exact equation from two points on the line, the steps are :
`y − y1 = m(x − x1)` where m is the slope of the line.
Simplifying from this, a = -m, b = 1 and c = m*x1 - y1
If you do not want to implement all this by yourself I can recommend to use JTS. Use the distance method from LineSegment (for lines) or Coordinate (for points) accordingly. Given Points p1 and p2 for your Line and a Point p3 (that you want to calculate the distance) the code would look like that:
// create Line
LineSegment ls = new LineSegment(p1.getX(), p1.getY(), p2.getX(), p2.getY());
//calculate distance between Line and Point
double distanceLinePoint = ls.distance(new Coordinate(p3.getX(), p3.getY()));
// calculate distance between Points (p1 - p3)
double distanceBetweenPoints = new Coordinate(p1.getX(), p1.getY()).distance(new Coordinate(p3.getX(), p3.getY()));
I want to verify if a Point is part of a quadratic Bezier curve defined by points p0, p1 and p2..
This is my function to obtain a Point in the curve with a certain t:
public static final Point quadratic (Point p0, Point p1, Point p2, double t) {
double x = Math.pow(1-t, 2) * p0.x + 2 * (1-t) * t * p1.x + Math.pow(t, 2) * p2.x;
double y = Math.pow(1-t, 2) * p0.y + 2 * (1-t) * t * p1.y + Math.pow(t, 2) * p2.y;
return new Point((int)x, (int)y);
}
Considering that the point B(t) in a quadratic curve is obtained as follows:
B(t) = (1 - t)^2 * p0 + 2 * t * (1 - t) * p1 + t^2 * p2
I should verify if a point P belongs to a curve by getting the t value for that point and comparing it to the Point obtained using that t param, but in Java I'm having problems with the precision of the variables.
My function to verify a point is the following:
public static final boolean belongsQuadratic (Point p, Point p0, Point p1, Point p2) {
double[] tx = obtainTs(p.x, p0, p1, p2);
double[] ty = obtainTs(p.y, p0, p1, p2);
if (tx[0] >= 0) {
if ((tx[0] >= ty[0] - ERROR && tx[0] <= ty[0] + ERROR) || (tx[0] >= ty[1] - ERROR && tx[0] <= ty[1] + ERROR)) {
return true;
}
}
if (tx[1] >= 0) {
if ((tx[1] >= ty[0] - ERROR && tx[1] <= ty[0] + ERROR) || (tx[1] >= ty[1] - ERROR && tx[1] <= ty[1] + ERROR)) {
return true;
}
}
return false;
}
public static double[] obtainTs (int comp, Point p0, Point p1, Point p2) {
double a = p0.x - 2*p1.x + p2.x;
double b = 2*p1.x - 2*p0.x ;
double c = p0.x - comp;
double t1 = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
double t2 = (-b - Math.sqrt(b*b - 4*a*c)) / (2*a);
return new double[] {t1, t2};
}
So if I run the code with this values:
Point p0 = new Point(320, 480);
Point p1 = new Point(320, 240);
Point p2 = new Point(0, 240);
double t = 0.10f;
Point p = Bezier.quadratic(p0, p1, p2, t);
double[] ts = Bezier.obtainTs(p.x, p0, p1, p2);
I obtain the following output:
For t=0.10000000149011612, java.awt.Point[x=316,y=434]
For t1: -0.1118033988749895, java.awt.Point[x=316,y=536]
For t2: 0.1118033988749895, java.awt.Point[x=316,y=429]
java.awt.Point[x=316,y=434] belongs?: false
Should I use BigDecimal to perform the operations? Is there another way to verify this? Thanks
As an alternative answer, to circumvent the problem noted by Martin R, you can simply build a lookup table and resolve coordinates to on-curve-ness that way. When you build the curve, generate an N-point array of coordinates for incremental t values, and then when you need to test whether a coordinate lies on the curve, find the nearest t value to that coordinate by checking whether that coordinate is in the lookup table, or "near enough to" a coordinate in the lookup table. In code:
point[] points = new point[100];
for(i=0; i<100; i++) {
t = i/100;
points[i] = new point(computeX(t), computeY(t));
}
and then when you need on-curve-testing:
for(i=0; i<points.length; i++) {
point = points[i];
if(abs(point-coordinate)<3) {
// close enough to the curve to count,
// so we can use t value map(i,0,100,0,1)
}
}
building the LUT costs virtually nothing since we're generating those coordinate-for-t values already in order to draw the curve, and you're not going to run an on-curve test without first making sure the coordinate is even in the curve's bounding box.
There is an error here:
double[] ty = obtainTs(p.y, p0, p1, p2);
because obtainTs() uses the x-coordinates of p0, p1, p2 to find the t-parameter for
the y-coordinate of p.
If you change the method parameters to int (which can be the x- or y-coordinates of the point):
public static double[] obtainTs (int comp, int p0, int p1, int p2) {
double a = p0 - 2*p1 + p2;
double b = 2*p1 - 2*p0 ;
double c = p0 - comp;
double t1 = (-b + Math.sqrt(b*b - 4*a*c)) / (2*a);
double t2 = (-b - Math.sqrt(b*b - 4*a*c)) / (2*a);
return new double[] {t1, t2};
}
and call it
double[] tx = obtainTs(p.x, p0.x, p1.x, p2.x);
double[] ty = obtainTs(p.y, p0.y, p1.y, p2.y);
then your test code will return "true" (tested with ERROR = 0.02).
Note that if you write down the equation
B(t) = (1 - t)^2 * p0 + 2 * t * (1 - t) * p1 + t^2 * p2
for both x- and y-coordinate, then you can eliminate the t^2-Term and get a single
linear equation for t. This gives the following method, which might be slightly simpler
and does not use square roots:
public static final boolean belongsQuadratic2 (Point p, Point p0, Point p1, Point p2) {
double ax = p0.x - 2*p1.x + p2.x;
double bx = 2*p1.x - 2*p0.x ;
double cx = p0.x - p.x;
double ay = p0.y - 2*p1.y + p2.y;
double by = 2*p1.y - 2*p0.y ;
double cy = p0.y - p.y;
// "Candidate" for t:
double t = -(cx*ay - cy*ax)/(bx*ay - by*ax);
if (t < 0 || t > 1)
return false;
// Compute the point corresponding to this candidate value ...
Point q = Bezier.quadratic(p0, p1, p2, t);
// ... and check if it is near the given point p:
return Math.abs(q.x - p.x) <= 1 && Math.abs(q.y - p.y) <= 1;
}
Of course, one would have to check for special cases, such as bx*ay - by*ax == 0.
Note also that it is difficult do decide exactly if a point lies on the curve because
the point coordinates are rounded to integers.