-
Notifications
You must be signed in to change notification settings - Fork 294
/
Copy pathLineIntersection.cs
207 lines (175 loc) · 7.26 KB
/
LineIntersection.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
using System;
namespace Advanced.Algorithms.Geometry;
/// <summary>
/// Line intersection computer.
/// </summary>
public class LineIntersection
{
/// <summary>
/// Returns Point of intersection if do intersect otherwise default Point (null).
/// </summary>
/// <param name="precision">precision tolerance.</param>
/// <returns>The point of intersection.</returns>
public static Point Find(Line lineA, Line lineB, int precision = 5)
{
var tolerance = Math.Round(Math.Pow(0.1, precision), precision);
return FindIntersection(lineA, lineB, tolerance);
}
internal static Point FindIntersection(Line lineA, Line lineB, double tolerance)
{
if (lineA == lineB) throw new Exception("Both lines are the same.");
//make lineA as left
if (lineA.Left.X.CompareTo(lineB.Left.X) > 0)
{
var tmp = lineA;
lineA = lineB;
lineB = tmp;
}
else if (lineA.Left.X.CompareTo(lineB.Left.X) == 0)
{
if (lineA.Left.Y.CompareTo(lineB.Left.Y) > 0)
{
var tmp = lineA;
lineA = lineB;
lineB = tmp;
}
}
double x1 = lineA.Left.X, y1 = lineA.Left.Y;
double x2 = lineA.Right.X, y2 = lineA.Right.Y;
double x3 = lineB.Left.X, y3 = lineB.Left.Y;
double x4 = lineB.Right.X, y4 = lineB.Right.Y;
//equations of the form x=c (two vertical overlapping lines)
if (x1 == x2 && x3 == x4 && x1 == x3)
{
//get the first intersection in vertical sorted order of lines
var firstIntersection = new Point(x3, y3);
//x,y can intersect outside the line segment since line is infinitely long
//so finally check if x, y is within both the line segments
if (IsInsideLine(lineA, firstIntersection, tolerance) &&
IsInsideLine(lineB, firstIntersection, tolerance))
return new Point(x3, y3);
}
//equations of the form y=c (two overlapping horizontal lines)
if (y1 == y2 && y3 == y4 && y1 == y3)
{
//get the first intersection in horizontal sorted order of lines
var firstIntersection = new Point(x3, y3);
//get the first intersection in sorted order
//x,y can intersect outside the line segment since line is infinitely long
//so finally check if x, y is within both the line segments
if (IsInsideLine(lineA, firstIntersection, tolerance) &&
IsInsideLine(lineB, firstIntersection, tolerance))
return new Point(x3, y3);
}
//equations of the form x=c (two vertical lines)
if (x1 == x2 && x3 == x4) return null;
//equations of the form y=c (two horizontal lines)
if (y1 == y2 && y3 == y4) return null;
//general equation of line is y = mx + c where m is the slope
//assume equation of line 1 as y1 = m1x1 + c1
//=> -m1x1 + y1 = c1 ----(1)
//assume equation of line 2 as y2 = m2x2 + c2
//=> -m2x2 + y2 = c2 -----(2)
//if line 1 and 2 intersect then x1=x2=x and y1=y2=y where (x,y) is the intersection point
//so we will get below two equations
//-m1x + y = c1 --------(3)
//-m2x + y = c2 --------(4)
double x, y;
//lineA is vertical x1 = x2
//slope will be infinity
//so lets derive another solution
if (Math.Abs(x1 - x2) < tolerance)
{
//compute slope of line 2 (m2) and c2
var m2 = (y4 - y3) / (x4 - x3);
var c2 = -m2 * x3 + y3;
//equation of vertical line is x = c
//if line 1 and 2 intersect then x1=c1=x
//subsitute x=x1 in (4) => -m2x1 + y = c2
// => y = c2 + m2x1
x = x1;
y = c2 + m2 * x1;
}
//lineB is vertical x3 = x4
//slope will be infinity
//so lets derive another solution
else if (Math.Abs(x3 - x4) < tolerance)
{
//compute slope of line 1 (m1) and c2
var m1 = (y2 - y1) / (x2 - x1);
var c1 = -m1 * x1 + y1;
//equation of vertical line is x = c
//if line 1 and 2 intersect then x3=c3=x
//subsitute x=x3 in (3) => -m1x3 + y = c1
// => y = c1 + m1x3
x = x3;
y = c1 + m1 * x3;
}
//lineA and lineB are not vertical
//(could be horizontal we can handle it with slope = 0)
else
{
//compute slope of line 1 (m1) and c2
var m1 = (y2 - y1) / (x2 - x1);
var c1 = -m1 * x1 + y1;
//compute slope of line 2 (m2) and c2
var m2 = (y4 - y3) / (x4 - x3);
var c2 = -m2 * x3 + y3;
//solving equations (3) and (4) => x = (c1-c2)/(m2-m1)
//plugging x value in equation (4) => y = c2 + m2 * x
x = (c1 - c2) / (m2 - m1);
y = c2 + m2 * x;
//verify by plugging intersection point (x, y)
//in orginal equations (1) and (2) to see if they intersect
//otherwise x,y values will not be finite and will fail this check
if (!(Math.Abs(-m1 * x + y - c1) < tolerance
&& Math.Abs(-m2 * x + y - c2) < tolerance))
return null;
}
var result = new Point(x, y);
//x,y can intersect outside the line segment since line is infinitely long
//so finally check if x, y is within both the line segments
if (IsInsideLine(lineA, result, tolerance) &&
IsInsideLine(lineB, result, tolerance))
return result;
//return default null (no intersection)
return null;
}
/// <summary>
/// Returns true if given point(x,y) is inside the given line segment.
/// </summary>
private static bool IsInsideLine(Line line, Point p, double tolerance)
{
double x = p.X, y = p.Y;
var leftX = line.Left.X;
var leftY = line.Left.Y;
var rightX = line.Right.X;
var rightY = line.Right.Y;
return (x.IsGreaterThanOrEqual(leftX, tolerance) && x.IsLessThanOrEqual(rightX, tolerance)
|| x.IsGreaterThanOrEqual(rightX, tolerance) && x.IsLessThanOrEqual(leftX, tolerance))
&& (y.IsGreaterThanOrEqual(leftY, tolerance) && y.IsLessThanOrEqual(rightY, tolerance)
|| y.IsGreaterThanOrEqual(rightY, tolerance) && y.IsLessThanOrEqual(leftY, tolerance));
}
}
/// <summary>
/// Line extensions.
/// </summary>
public static class LineExtensions
{
public static bool Intersects(this Line lineA, Line lineB, int precision = 5)
{
return LineIntersection.Find(lineA, lineB, precision) != null;
}
public static Point Intersection(this Line lineA, Line lineB, int precision = 5)
{
return LineIntersection.Find(lineA, lineB, precision);
}
internal static bool Intersects(this Line lineA, Line lineB, double tolerance)
{
return LineIntersection.FindIntersection(lineA, lineB, tolerance) != null;
}
internal static Point Intersection(this Line lineA, Line lineB, double tolerance)
{
return LineIntersection.FindIntersection(lineA, lineB, tolerance);
}
}