Question

I am having some trouble determining if two line segments are collinear because of floating point precision. How can I determine if the line segments are collinear with some tolerance?

Was it helpful?

Solution

EDITED:

Line segments are colinear if they contain two of the same points. They are near-colinear if they share one point and are near-parallel.

Vectors are effectively parallel if the angle between them is less than a threshold you state. Maybe less than .000027 degrees, which is the decimal equivalent of one tenth of a degree-second (which is in latitudinal distance, and equivalently of longitudinal distance at the equator, a difference of approximately ten feet; that's about the accuracy of civilian GPS).

You didn't tell us what language or library you're using; in .NET's System.Windows.Media.3D library there is a Vector3D struct which has an AngleBetween() method, making this check a one-liner.

The "basic" math (it's actually vector trig, not a "basic" concept by most definitions) is that θ=cos-1( A*B / |A||B| ); that is, the arc-cosine of the quantity of the scalar product of the two vectors divided by the product of their magnitudes.

The dot product of vector A and vector B, both containing components X, Y, and Z, is XAXB + YAYB + ZAZB. The magnitude of a vector A is sqrt(XA2 + YA2 + ZA2).

So, in pseudo-C-ish:

//Vector is a simple immutable class or struct containing integer X, Y and Z components
public bool CloseEnough(Vector a, Vector b, decimal threshold = 0.000027m)
{
   int dotProduct = a.X*b.X + a.Y*b.Y + a.Z*b.Z;
   decimal magA = sqrt(a.X*a.X + a.Y*a.Y + a.Z*a.Z); //sub your own sqrt
   decimal magB = sqrt(b.X*b.X + b.Y*b.Y + b.Z*b.Z); //sub your own sqrt

   decimal angle = acos(dotProduct/(magA*magB)); //sub your own arc-cosine

   if(angle <= threshold
}

OTHER TIPS

I need to know for my application if two segments are near-collinear. It's about extracting lines from a laser scan. I will explain the solution I am using. It works pretty well. (Excuse my English!)

I think the conditions that KeithS proposes for near-collinearity are wrong.

They are near-colinear if they share one point and are near-parallel.

If two segments (or lines) are parallel but they are near, we can consider them near-collinear.

My solution consists in using a polar representation of the lines.

y * sin(theta) = rho - x * cos(theta)

With this representation, a line can be represented like a point using theta and rho. The trick is that, if those "points" are near, the lines are near-collinear. You just need to calculate the euclidean distance and use a threshold for determine if they are near-collinear.

Several solutions seem plausible depending on your definition of "almost collinear".

Fit a line through 4 points

Two line segments are defined by 4 points, and you could fit a line through the starts and ends of the 2 line segments.

You can use SVD to get the least square fit of a line through 4 points, similar to this answer: https://stackoverflow.com/a/2333251/5069869

With this approach, you take the direction of the line segments as well as the offset between the end of the first and the start of the second line segment into account.

However, if the two line-segments are parallel but not collinear, and if they are short relative to the distance between them, the fitted line will be perpendicular to the two line-segments. This is no problem for unit tests, where you like "almost collinear up to several digits behind zero", but for other applications this might be a problem.

Use average direction and average location independently

Alternatively, you can construct a target line using the average direction of the two line segments as direction of the target line. Thuis can be calculated by (v1/|v1|+v2/|v2|)/2, if the vectors v1 and v2 point in the same direction. Then use the mean of the 4 points as an anchor point for the target line.

Finally, you can calculate the individual distances of your 4 points to the target line and use them as measure of collinearity.

The effect of lengths of your segments & the space between segments

When you find a suitable definition of "almost collinear", you need to consider, how different lengths of line segments effect the result, as pointed out by Vahid in a comment. Furthermore, you should think about the effect of the space between the two line segments.

By calculating the distance of 4 points to a target line, I try to reduce the effect of different segment lengths (every segment only contributes 2 points), but I cannot completely eliminate it

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top