-
-
Notifications
You must be signed in to change notification settings - Fork 231
/
Intersect3D.cs
526 lines (454 loc) · 20.6 KB
/
Intersect3D.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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
using UnityEngine;
namespace ProceduralToolkit
{
/// <summary>
/// Collection of intersection algorithms
/// </summary>
public static partial class Intersect
{
#region Point-Line
/// <summary>
/// Tests if the point lies on the line
/// </summary>
public static bool PointLine(Vector3 point, Line3 line)
{
return PointLine(point, line.origin, line.direction);
}
/// <summary>
/// Tests if the point lies on the line
/// </summary>
public static bool PointLine(Vector3 point, Vector3 lineOrigin, Vector3 lineDirection)
{
return Distance.PointLine(point, lineOrigin, lineDirection) < Geometry.Epsilon;
}
#endregion Point-Line
#region Point-Ray
/// <summary>
/// Tests if the point lies on the ray
/// </summary>
public static bool PointRay(Vector3 point, Ray ray)
{
return PointRay(point, ray.origin, ray.direction);
}
/// <summary>
/// Tests if the point lies on the ray
/// </summary>
public static bool PointRay(Vector3 point, Vector3 rayOrigin, Vector3 rayDirection)
{
return Distance.PointRay(point, rayOrigin, rayDirection) < Geometry.Epsilon;
}
#endregion Point-Ray
#region Point-Segment
/// <summary>
/// Tests if the point lies on the segment
/// </summary>
public static bool PointSegment(Vector3 point, Segment3 segment)
{
return PointSegment(point, segment.a, segment.b);
}
/// <summary>
/// Tests if the point lies on the segment
/// </summary>
public static bool PointSegment(Vector3 point, Vector3 segmentA, Vector3 segmentB)
{
return Distance.PointSegment(point, segmentA, segmentB) < Geometry.Epsilon;
}
#endregion Point-Segment
#region Point-Sphere
/// <summary>
/// Tests if the point is inside the sphere
/// </summary>
public static bool PointSphere(Vector3 point, Sphere sphere)
{
return PointSphere(point, sphere.center, sphere.radius);
}
/// <summary>
/// Tests if the point is inside the sphere
/// </summary>
public static bool PointSphere(Vector3 point, Vector3 sphereCenter, float sphereRadius)
{
// For points on the sphere's surface magnitude is more stable than sqrMagnitude
return (point - sphereCenter).magnitude < sphereRadius + Geometry.Epsilon;
}
#endregion Point-Sphere
#region Line-Line
/// <summary>
/// Computes an intersection of the lines
/// </summary>
public static bool LineLine(Line3 lineA, Line3 lineB)
{
return LineLine(lineA.origin, lineA.direction, lineB.origin, lineB.direction, out Vector3 intersection);
}
/// <summary>
/// Computes an intersection of the lines
/// </summary>
public static bool LineLine(Line3 lineA, Line3 lineB, out Vector3 intersection)
{
return LineLine(lineA.origin, lineA.direction, lineB.origin, lineB.direction, out intersection);
}
/// <summary>
/// Computes an intersection of the lines
/// </summary>
public static bool LineLine(Vector3 originA, Vector3 directionA, Vector3 originB, Vector3 directionB)
{
return LineLine(originA, directionA, originB, directionB, out Vector3 intersection);
}
/// <summary>
/// Computes an intersection of the lines
/// </summary>
public static bool LineLine(Vector3 originA, Vector3 directionA, Vector3 originB, Vector3 directionB,
out Vector3 intersection)
{
float sqrMagnitudeA = directionA.sqrMagnitude;
float sqrMagnitudeB = directionB.sqrMagnitude;
float dotAB = Vector3.Dot(directionA, directionB);
float denominator = sqrMagnitudeA*sqrMagnitudeB - dotAB*dotAB;
Vector3 originBToA = originA - originB;
float a = Vector3.Dot(directionA, originBToA);
float b = Vector3.Dot(directionB, originBToA);
Vector3 closestPointA;
Vector3 closestPointB;
if (Mathf.Abs(denominator) < Geometry.Epsilon)
{
// Parallel
float distanceB = dotAB > sqrMagnitudeB ? a/dotAB : b/sqrMagnitudeB;
closestPointA = originA;
closestPointB = originB + directionB*distanceB;
}
else
{
// Not parallel
float distanceA = (sqrMagnitudeA*b - dotAB*a)/denominator;
float distanceB = (dotAB*b - sqrMagnitudeB*a)/denominator;
closestPointA = originA + directionA*distanceA;
closestPointB = originB + directionB*distanceB;
}
if ((closestPointB - closestPointA).sqrMagnitude < Geometry.Epsilon)
{
intersection = closestPointA;
return true;
}
intersection = Vector3.zero;
return false;
}
#endregion Line-Line
#region Line-Sphere
/// <summary>
/// Computes an intersection of the line and the sphere
/// </summary>
public static bool LineSphere(Line3 line, Sphere sphere)
{
return LineSphere(line.origin, line.direction, sphere.center, sphere.radius, out IntersectionLineSphere intersection);
}
/// <summary>
/// Computes an intersection of the line and the sphere
/// </summary>
public static bool LineSphere(Line3 line, Sphere sphere, out IntersectionLineSphere intersection)
{
return LineSphere(line.origin, line.direction, sphere.center, sphere.radius, out intersection);
}
/// <summary>
/// Computes an intersection of the line and the sphere
/// </summary>
public static bool LineSphere(Vector3 lineOrigin, Vector3 lineDirection, Vector3 sphereCenter, float sphereRadius)
{
return LineSphere(lineOrigin, lineDirection, sphereCenter, sphereRadius, out IntersectionLineSphere intersection);
}
/// <summary>
/// Computes an intersection of the line and the sphere
/// </summary>
public static bool LineSphere(Vector3 lineOrigin, Vector3 lineDirection, Vector3 sphereCenter, float sphereRadius,
out IntersectionLineSphere intersection)
{
Vector3 originToCenter = sphereCenter - lineOrigin;
float centerProjection = Vector3.Dot(lineDirection, originToCenter);
float sqrDistanceToLine = originToCenter.sqrMagnitude - centerProjection*centerProjection;
float sqrDistanceToIntersection = sphereRadius*sphereRadius - sqrDistanceToLine;
if (sqrDistanceToIntersection < -Geometry.Epsilon)
{
intersection = IntersectionLineSphere.None();
return false;
}
if (sqrDistanceToIntersection < Geometry.Epsilon)
{
intersection = IntersectionLineSphere.Point(lineOrigin + lineDirection*centerProjection);
return true;
}
float distanceToIntersection = Mathf.Sqrt(sqrDistanceToIntersection);
float distanceA = centerProjection - distanceToIntersection;
float distanceB = centerProjection + distanceToIntersection;
Vector3 pointA = lineOrigin + lineDirection*distanceA;
Vector3 pointB = lineOrigin + lineDirection*distanceB;
intersection = IntersectionLineSphere.TwoPoints(pointA, pointB);
return true;
}
#endregion Line-Sphere
#region Ray-Sphere
/// <summary>
/// Computes an intersection of the ray and the sphere
/// </summary>
public static bool RaySphere(Ray ray, Sphere sphere)
{
return RaySphere(ray.origin, ray.direction, sphere.center, sphere.radius, out IntersectionRaySphere intersection);
}
/// <summary>
/// Computes an intersection of the ray and the sphere
/// </summary>
public static bool RaySphere(Ray ray, Sphere sphere, out IntersectionRaySphere intersection)
{
return RaySphere(ray.origin, ray.direction, sphere.center, sphere.radius, out intersection);
}
/// <summary>
/// Computes an intersection of the ray and the sphere
/// </summary>
public static bool RaySphere(Vector3 rayOrigin, Vector3 rayDirection, Vector3 sphereCenter, float sphereRadius)
{
return RaySphere(rayOrigin, rayDirection, sphereCenter, sphereRadius, out IntersectionRaySphere intersection);
}
/// <summary>
/// Computes an intersection of the ray and the sphere
/// </summary>
public static bool RaySphere(Vector3 rayOrigin, Vector3 rayDirection, Vector3 sphereCenter, float sphereRadius,
out IntersectionRaySphere intersection)
{
Vector3 originToCenter = sphereCenter - rayOrigin;
float centerProjection = Vector3.Dot(rayDirection, originToCenter);
if (centerProjection + sphereRadius < -Geometry.Epsilon)
{
intersection = IntersectionRaySphere.None();
return false;
}
float sqrDistanceToLine = originToCenter.sqrMagnitude - centerProjection*centerProjection;
float sqrDistanceToIntersection = sphereRadius*sphereRadius - sqrDistanceToLine;
if (sqrDistanceToIntersection < -Geometry.Epsilon)
{
intersection = IntersectionRaySphere.None();
return false;
}
if (sqrDistanceToIntersection < Geometry.Epsilon)
{
if (centerProjection < -Geometry.Epsilon)
{
intersection = IntersectionRaySphere.None();
return false;
}
intersection = IntersectionRaySphere.Point(rayOrigin + rayDirection*centerProjection);
return true;
}
// Line intersection
float distanceToIntersection = Mathf.Sqrt(sqrDistanceToIntersection);
float distanceA = centerProjection - distanceToIntersection;
float distanceB = centerProjection + distanceToIntersection;
if (distanceA < -Geometry.Epsilon)
{
if (distanceB < -Geometry.Epsilon)
{
intersection = IntersectionRaySphere.None();
return false;
}
intersection = IntersectionRaySphere.Point(rayOrigin + rayDirection*distanceB);
return true;
}
Vector3 pointA = rayOrigin + rayDirection*distanceA;
Vector3 pointB = rayOrigin + rayDirection*distanceB;
intersection = IntersectionRaySphere.TwoPoints(pointA, pointB);
return true;
}
#endregion Ray-Sphere
#region Segment-Sphere
/// <summary>
/// Computes an intersection of the segment and the sphere
/// </summary>
public static bool SegmentSphere(Segment3 segment, Sphere sphere)
{
return SegmentSphere(segment.a, segment.b, sphere.center, sphere.radius, out IntersectionSegmentSphere intersection);
}
/// <summary>
/// Computes an intersection of the segment and the sphere
/// </summary>
public static bool SegmentSphere(Segment3 segment, Sphere sphere, out IntersectionSegmentSphere intersection)
{
return SegmentSphere(segment.a, segment.b, sphere.center, sphere.radius, out intersection);
}
/// <summary>
/// Computes an intersection of the segment and the sphere
/// </summary>
public static bool SegmentSphere(Vector3 segmentA, Vector3 segmentB, Vector3 sphereCenter, float sphereRadius)
{
return SegmentSphere(segmentA, segmentB, sphereCenter, sphereRadius, out IntersectionSegmentSphere intersection);
}
/// <summary>
/// Computes an intersection of the segment and the sphere
/// </summary>
public static bool SegmentSphere(Vector3 segmentA, Vector3 segmentB, Vector3 sphereCenter, float sphereRadius,
out IntersectionSegmentSphere intersection)
{
Vector3 segmentAToCenter = sphereCenter - segmentA;
Vector3 fromAtoB = segmentB - segmentA;
float segmentLength = fromAtoB.magnitude;
if (segmentLength < Geometry.Epsilon)
{
float distanceToPoint = segmentAToCenter.magnitude;
if (distanceToPoint < sphereRadius + Geometry.Epsilon)
{
if (distanceToPoint > sphereRadius - Geometry.Epsilon)
{
intersection = IntersectionSegmentSphere.Point(segmentA);
return true;
}
intersection = IntersectionSegmentSphere.None();
return true;
}
intersection = IntersectionSegmentSphere.None();
return false;
}
Vector3 segmentDirection = fromAtoB.normalized;
float centerProjection = Vector3.Dot(segmentDirection, segmentAToCenter);
if (centerProjection + sphereRadius < -Geometry.Epsilon ||
centerProjection - sphereRadius > segmentLength + Geometry.Epsilon)
{
intersection = IntersectionSegmentSphere.None();
return false;
}
float sqrDistanceToLine = segmentAToCenter.sqrMagnitude - centerProjection*centerProjection;
float sqrDistanceToIntersection = sphereRadius*sphereRadius - sqrDistanceToLine;
if (sqrDistanceToIntersection < -Geometry.Epsilon)
{
intersection = IntersectionSegmentSphere.None();
return false;
}
if (sqrDistanceToIntersection < Geometry.Epsilon)
{
if (centerProjection < -Geometry.Epsilon ||
centerProjection > segmentLength + Geometry.Epsilon)
{
intersection = IntersectionSegmentSphere.None();
return false;
}
intersection = IntersectionSegmentSphere.Point(segmentA + segmentDirection*centerProjection);
return true;
}
// Line intersection
float distanceToIntersection = Mathf.Sqrt(sqrDistanceToIntersection);
float distanceA = centerProjection - distanceToIntersection;
float distanceB = centerProjection + distanceToIntersection;
bool pointAIsAfterSegmentA = distanceA > -Geometry.Epsilon;
bool pointBIsBeforeSegmentB = distanceB < segmentLength + Geometry.Epsilon;
if (pointAIsAfterSegmentA && pointBIsBeforeSegmentB)
{
Vector3 pointA = segmentA + segmentDirection*distanceA;
Vector3 pointB = segmentA + segmentDirection*distanceB;
intersection = IntersectionSegmentSphere.TwoPoints(pointA, pointB);
return true;
}
if (!pointAIsAfterSegmentA && !pointBIsBeforeSegmentB)
{
// The segment is inside, but no intersection
intersection = IntersectionSegmentSphere.None();
return true;
}
bool pointAIsBeforeSegmentB = distanceA < segmentLength + Geometry.Epsilon;
if (pointAIsAfterSegmentA && pointAIsBeforeSegmentB)
{
// Point A intersection
intersection = IntersectionSegmentSphere.Point(segmentA + segmentDirection*distanceA);
return true;
}
bool pointBIsAfterSegmentA = distanceB > -Geometry.Epsilon;
if (pointBIsAfterSegmentA && pointBIsBeforeSegmentB)
{
// Point B intersection
intersection = IntersectionSegmentSphere.Point(segmentA + segmentDirection*distanceB);
return true;
}
intersection = IntersectionSegmentSphere.None();
return false;
}
#endregion Segment-Sphere
#region Sphere-Sphere
/// <summary>
/// Computes an intersection of the spheres
/// </summary>
/// <returns>True if the spheres intersect or one sphere is contained within the other</returns>
public static bool SphereSphere(Sphere sphereA, Sphere sphereB)
{
return SphereSphere(sphereA.center, sphereA.radius, sphereB.center, sphereB.radius, out IntersectionSphereSphere intersection);
}
/// <summary>
/// Computes an intersection of the spheres
/// </summary>
/// <returns>True if the spheres intersect or one sphere is contained within the other</returns>
public static bool SphereSphere(Sphere sphereA, Sphere sphereB, out IntersectionSphereSphere intersection)
{
return SphereSphere(sphereA.center, sphereA.radius, sphereB.center, sphereB.radius, out intersection);
}
/// <summary>
/// Computes an intersection of the spheres
/// </summary>
/// <returns>True if the spheres intersect or one sphere is contained within the other</returns>
public static bool SphereSphere(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB)
{
return SphereSphere(centerA, radiusA, centerB, radiusB, out IntersectionSphereSphere intersection);
}
/// <summary>
/// Computes an intersection of the spheres
/// </summary>
/// <returns>True if the spheres intersect or one sphere is contained within the other</returns>
public static bool SphereSphere(Vector3 centerA, float radiusA, Vector3 centerB, float radiusB,
out IntersectionSphereSphere intersection)
{
Vector3 fromBtoA = centerA - centerB;
float distanceFromBtoASqr = fromBtoA.sqrMagnitude;
if (distanceFromBtoASqr < Geometry.Epsilon)
{
if (Mathf.Abs(radiusA - radiusB) < Geometry.Epsilon)
{
// Spheres are coincident
intersection = IntersectionSphereSphere.Sphere(centerA, radiusA);
return true;
}
// One sphere is inside the other
intersection = IntersectionSphereSphere.None();
return true;
}
// For intersections on the sphere's edge magnitude is more stable than sqrMagnitude
float distanceFromBtoA = Mathf.Sqrt(distanceFromBtoASqr);
float sumOfRadii = radiusA + radiusB;
if (Mathf.Abs(distanceFromBtoA - sumOfRadii) < Geometry.Epsilon)
{
// One intersection outside
intersection = IntersectionSphereSphere.Point(centerB + fromBtoA*(radiusB/sumOfRadii));
return true;
}
if (distanceFromBtoA > sumOfRadii)
{
// No intersections, spheres are separate
intersection = IntersectionSphereSphere.None();
return false;
}
float differenceOfRadii = radiusA - radiusB;
float differenceOfRadiiAbs = Mathf.Abs(differenceOfRadii);
if (Mathf.Abs(distanceFromBtoA - differenceOfRadiiAbs) < Geometry.Epsilon)
{
// One intersection inside
intersection = IntersectionSphereSphere.Point(centerB - fromBtoA*(radiusB/differenceOfRadii));
return true;
}
if (distanceFromBtoA < differenceOfRadiiAbs)
{
// One sphere is contained within the other
intersection = IntersectionSphereSphere.None();
return true;
}
// Circle intersection
float radiusASqr = radiusA*radiusA;
float distanceToMiddle = 0.5f*(radiusASqr - radiusB*radiusB)/distanceFromBtoASqr + 0.5f;
Vector3 middle = centerA - fromBtoA*distanceToMiddle;
float discriminant = radiusASqr/distanceFromBtoASqr - distanceToMiddle*distanceToMiddle;
float radius = distanceFromBtoA*Mathf.Sqrt(discriminant);
intersection = IntersectionSphereSphere.Circle(middle, -fromBtoA.normalized, radius);
return true;
}
#endregion Sphere-Sphere
}
}