|
2 | 2 | """
|
3 | 3 | intersects(a::Line, b::Line) -> Bool, Point
|
4 | 4 |
|
5 |
| -Intersection of 2 line segmens `a` and `b`. |
6 |
| -Returns intersection_found::Bool, intersection_point::Point |
| 5 | +Intersection of 2 line segments `a` and `b`. |
| 6 | +Returns `(intersection_found::Bool, intersection_point::Point)` |
7 | 7 | """
|
| 8 | +# 2D Line-segment intersection algorithm by Paul Bourke and many others. |
| 9 | +# http://paulbourke.net/geometry/pointlineplane/ |
8 | 10 | function intersects(a::Line{2,T1}, b::Line{2,T2}) where {T1,T2}
|
9 | 11 | T = promote_type(T1, T2)
|
10 |
| - v1, v2 = a |
11 |
| - v3, v4 = b |
12 |
| - MT = Mat{2,2,T,4} |
13 | 12 | p0 = zero(Point2{T})
|
14 | 13 |
|
15 |
| - verticalA = v1[1] == v2[1] |
16 |
| - verticalB = v3[1] == v4[1] |
17 |
| - |
18 |
| - # if a segment is vertical the linear algebra might have trouble |
19 |
| - # so we will rotate the segments such that neither is vertical |
20 |
| - dorotation = verticalA || verticalB |
21 |
| - |
22 |
| - if dorotation |
23 |
| - θ = T(0.0) |
24 |
| - if verticalA && verticalB |
25 |
| - θ = T(π / 4) |
26 |
| - elseif verticalA || verticalB # obviously true, but make it clear |
27 |
| - θ34 = -atan(v4[2] - v3[2], v4[1] - v3[1]) |
28 |
| - θ12 = -atan(v2[2] - v1[2], v2[1] - v1[1]) |
29 |
| - θ = verticalA ? θ34 : θ12 |
30 |
| - θ = abs(θ) == T(0) ? (θ12 + θ34) / 2 : θ |
31 |
| - θ = abs(θ) == T(pi) ? (θ12 + θ34) / 2 : θ |
32 |
| - end |
33 |
| - rotation = MT(cos(θ), sin(θ), -sin(θ), cos(θ)) |
34 |
| - v1 = rotation * v1 |
35 |
| - v2 = rotation * v2 |
36 |
| - v3 = rotation * v3 |
37 |
| - v4 = rotation * v4 |
38 |
| - end |
| 14 | + x1, y1 = a[1] |
| 15 | + x2, y2 = a[2] |
| 16 | + x3, y3 = b[1] |
| 17 | + x4, y4 = b[2] |
39 | 18 |
|
40 |
| - a = det(MT(v1[1] - v2[1], v1[2] - v2[2], v3[1] - v4[1], v3[2] - v4[2])) |
| 19 | + denominator = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1)) |
| 20 | + numerator_a = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3)) |
| 21 | + numerator_b = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3)) |
41 | 22 |
|
42 |
| - (abs(a) < eps(T)) && return false, p0 # Lines are parallel |
| 23 | + if denominator == 0 |
| 24 | + # no intersection: lines are parallel |
| 25 | + return false, p0 |
| 26 | + end |
43 | 27 |
|
44 |
| - d1 = det(MT(v1[1], v1[2], v2[1], v2[2])) |
45 |
| - d2 = det(MT(v3[1], v3[2], v4[1], v4[2])) |
46 |
| - x = det(MT(d1, v1[1] - v2[1], d2, v3[1] - v4[1])) / a |
47 |
| - y = det(MT(d1, v1[2] - v2[2], d2, v3[2] - v4[2])) / a |
| 28 | + # If we ever need to know if the lines are coincident, we can get that too: |
| 29 | + # denominator == numerator_a == numerator_b == 0 && return :coincident_lines |
48 | 30 |
|
49 |
| - (x < prevfloat(min(v1[1], v2[1])) || x > nextfloat(max(v1[1], v2[1]))) && |
50 |
| - return false, p0 |
51 |
| - (y < prevfloat(min(v1[2], v2[2])) || y > nextfloat(max(v1[2], v2[2]))) && |
52 |
| - return false, p0 |
53 |
| - (x < prevfloat(min(v3[1], v4[1])) || x > nextfloat(max(v3[1], v4[1]))) && |
54 |
| - return false, p0 |
55 |
| - (y < prevfloat(min(v3[2], v4[2])) || y > nextfloat(max(v3[2], v4[2]))) && |
56 |
| - return false, p0 |
| 31 | + # unknown_a and b tell us how far along the line segment the intersection is. |
| 32 | + unknown_a = numerator_a / denominator |
| 33 | + unknown_b = numerator_b / denominator |
57 | 34 |
|
58 |
| - point = Point2{T}(x, y) |
59 |
| - # don't forget to rotate the answer back |
60 |
| - if dorotation |
61 |
| - point = transpose(rotation) * point |
| 35 | + # Values between [0, 1] mean the intersection point of the lines rests on |
| 36 | + # both of the line segments. |
| 37 | + if 0 <= unknown_a <= 1 && 0 <= unknown_b <= 1 |
| 38 | + # Substituting an unknown back lets us find the intersection point. |
| 39 | + x = x1 + (unknown_a * (x2 - x1)) |
| 40 | + y = y1 + (unknown_a * (y2 - y1)) |
| 41 | + return true, Point2{T}(x, y) |
62 | 42 | end
|
63 | 43 |
|
64 |
| - return true, point |
| 44 | + # lines intersect, but outside of at least one of these line segments. |
| 45 | + return false, p0 |
65 | 46 | end
|
66 | 47 |
|
67 | 48 | function simple_concat(vec::AbstractVector, range, endpoint::P) where {P}
|
|
0 commit comments