Line Segment Intersection for Partial or Lapsed Mathematicians
24th February 2013 by Ian Martin
Line Segment Intersection for Partial or Lapsed Mathematicians
24th February 2013 by Ian Martin
Introduction
A common problem in 2D geometry is the determination of whether two line segments intersect, and following a positive answer, optionally deriving the intersection point. The most common solution is the use of cross product to derive the intersection point (if any), and whilst to a mathematician (or student thereof) this is a relatively simple solution, research for this article quickly revealed at least two published incorrect code implementations, flawed by their author's failure to understand that maths.
The purpose of this article is therefore to present a less elegant but much simpler solution, easier to illustrate and comprehend.
The article is therefore written with the assumption of an average knowledge of school mathematics.
Lines and Line Segments
In common parlance a 2D line is the shortest distance between two points, that description actually applies to line segments. A line is of infinite length; a line segment is a portion of the line, between two given points. Unless the two lines are parallel they will by definition intersect somewhere, given their inifinte lengths; the question of intersection of line segments is therefore the concern of this article.
The image below shows two line segments AB and CD
A Simpler Solution
To solve the line segment intersection problem we will use the fact the the intersection calculation is very simple if one of the lines is vertical or horizontal.
We will translate and rotate the "world" so that this is the case; we imagine ourselves at point A, looking along the line towards B, and considering the "world" relative to our own position and orentation.
This solution, described below in stages, also allows for some trivial (i.e. fast) checks that can determine that the line segments do not cross, before the full calculations are executed.
Stage 1 - Initial Checks
Considering again two line segments AB and CD. If the lines are identical (i.e. A and C, and B and D are at the same position (or A and D and B and C) the line segments should be designated as crossing or not crossing, subject to the ultimate objective of the test. No further checks are needed.
If both line segments are actually points (i.e. A=B and C=D), execute a simple test to determine if the the points coincide. No further checks are needed.
Finally if one of the line segment ends is the same as one of the other ends (but only one, not both), the line segments would normally be designated as not crossing. This is shown below, where B and C are coincidental.
Stage 2 - Trivial Rejection
Consider the line segments below. We can clearly see that they do not cross.
If we check the bounding boxes of each line segment, we can quickly in this case deduce that the line segments cannot cross, and no further checks are needed. The follow pseudo-code is used:-
if (box1.xmax < box2.xmin OR box1.ymax < box2.ymin OR box1.xmin > box2.xmax OR box1.ymin > box2.ymax) then reject
In the case shown below, line1's box.xmax is less than line2's box xmin, so they can never cross.
Stage 3 - Translate and Rotate Relative to A
Considering again the line segments AB and CD. Clearly they cross;
we will now show how to determine whether they do, and optionally calculate the point at which they do.
In order to determine whether they cross, we will rotate and shift the "world" of AB and CD so that AB is vertical, with A being at the centre (0,0). We will view the "world" from the perspective of point A, looking at point B.
First, translate A to (0,0) and move B,C and D by the same amount:-
Subtract A's X and Y coordinates from points B,C and D:-
Now we will rotate the "world" around 0,0 so that AB lies at X=0 (on the Y axis). We will rotate both AB and CD around (0,0) by the angle between AB and the Y axis (X=0); we will designate this angle a.
Here's where the school maths comes in. The Pythagoras formula shows that for a right angled triangle, the square of the longest edge = the sum of the square of the other two edges. Considering the line segment AB shown below:-
If we complete the right angled triangle using point E which is (A.x,B.y), the angle a is that between AB and AE. Using Pythagoras, the length or magnitude of line segment AB is derived thus:-
sqrt( (B.x-A.x)^2 + (B.y-A.y>^2) )
In order to rotate points by a given angle, we need the sine and cosine of that angle. For angle a these are:-
sin a = length(EB) / length(AB) or sin = B.x / length(AB)cos a = length(EA) / length(AB) or cos = B.y / length(AB)
Remember that we calculated length(AB) using Pythagoras, above.
To rotate points around (0,0) by angle a, use these formulae, which you may have encountered in school maths:-
x' = x cos a - y sin ay' = y cos a + x sin a
When the above formula is applied to AB and CD we see the result below.
(A shrewd observer will note that A (0,0) does not need to be rotated, and the new position B' of B is (0,length(AB)).)
Remember that this is the same "world", merely viewed from the perspetive of A, looking towards B. We will calculate the intersection point (if any) relative to point A - the fact of intersection is not changed by the co-ordinates being relative to A.
Allowing Tolerance
Even when using high precision floating point code, slight errors must be allowed for. Furthermore, although lines and line segments are normally regarded as having zero with, your application may consider them to be "thick". For these reasons we need to add a small tolerance to some checks applied after the rotation has been applied.
We will define a small value which will be used for the tolerance. For example:-
const double small_value = 0.00001;
Stage 4 - Further Trivial Checks
The new position of C'D' relative to AB' allows us to easily ascertain if C'D' crosses A'B'. We can see from the above diagram that the following pseudo-code checks can be applied:-
if C'.y < 0 AND D'.y < 0 they do not crossif C'.y > B'.y AND D'.y > B'.y they do not crossif C'.x < 0 AND D'.x < 0 they do not crossif C'.x > 0 AND D'.x > 0 they do not cross
Stage 5 - Check for Co-linear
If line segment C'D' is now on the Y axis (abs(C'.x) < small_value) AND abs(D'.x) < small_value) then the line segments lie on the same infinite line. The only questions that now concerns is whether they share the same space on that line. The stage 4 trivial checks have determined that A'B' and C'D' are not above or below each other, we know that if C'D' lies at or very close to X=0 it must cross A'B'.
Stage 6 - Check for Parallel
If line segment C'D' is now vertical ((abs(C'.x - D'.x) < small_value) we know that they cannot cross, and we also know that it is not co-linear, having already performed the stage 5 check.
Stage 7 - Do They Cross?
Having completed the above trivial checks, if the question is still not settled, we must calculate where C'D' crosses the Y axis (X=0).
Here is some more maths, but really simple, similar triangles. Look at C'D' crossing the Y axis below at point F:-
The smaller shaded triangle is "similar" to the larger triangle formed by C'D' and (D'.x,C'.y). To compute the Y position of point F:-
F.y = C'.y - (((D'.y - C'.y) * C'.x) / (D'.x - C'.x) )
Note that we already know that C'D' is not vertical, so D'.x-C'x cannot be zero - there is no need to apply a division by zero check before executing the above calculation.
We now have the position of F. We simply need to check it against line A'B'.
Thus, if F.y < 0 or F.y> B'.y, the line segments do not cross.
Stage 8 - Calculate the Intersection Point (optional)
If we need the intersection position, we will need to reverse the rotation and translation of stage 3, upon point F.
First, rotate point F the other way, using the rotation through the angle -a, using again these formulae:-
x' = x cos a - y sin ay' = y cos a + x sin a
...but since F.x=0, sin(-a) = -sin(a), and cos(-a) = cos(a), the operation becomes much simpler:-
F'.x = F.y * sin aF'.y = F.y * cos a
Then, translate the rotated F' by the position of A:-
F'.x = F'x + A.xF'.y = F'y + A.y
... to derive the actual "world" position of the intersection point F, rather than relative to AB.
Disadvantages
Clearly this method is not as elegant as the cross product solution. The single square root required is relatively slow.
Advantages
This method much is easier to comprehend and illustrate. The trivial checks at different stages can save un-needed calculations.
Improvements
If a complex system, where many line segments must be checked against each other, a simple improvment to performance could be derived by caching the line segment lengths as they are calculated.
Adding Figures
Using the line segments AB and CD in the worked example:-AB is (-5,-2) to (1,2)
CD is (-8,-2) to (2,1)
The boundary boxes cross, so we proceed to translate so that A is at (0,0).
Subtract A from B,C,D i.e. subtract (-5,-2)
AB is now (0,0) to (6,4)
CD is now (-3,0) to (7,3)
Calculate length(AB) using Pythagoras.
Length(AB) = 7.2111025