1. 程式人生 > >使用叠代逼近的方式求解線段與圓柱體的交點

使用叠代逼近的方式求解線段與圓柱體的交點

ntp 問題 es2017 while 使用 依據 () 線段 lec

需求來源:

UE4.15版本

我們的VR項目中,需要實現護盾的功能,玩家的左手柄方向的延長線與玩家周圍的一個圓柱體相交的位置,即為護盾的位置,而護盾的方向,與圓柱體相切,如下圖所示.

綠色為護盾,紅色的為圓柱體,藍色的線從左手柄射出.

技術分享

玩家的左手一定會在圓柱體內,那麽要解決的問題就變為,求解線段與圓柱體的交點.

如何使用叠代的方式求解呢,思路如下:

在玩家的左手柄方向的延長線上選擇一點,此點的位置要在圓柱體外.設為PointOut.玩家左手柄位置為PointIn,求出兩個點的中點:PointMiddle.那麽交點一定位於[PointIn, PointMiddle] 或者[PointIn, PointOut]之間,以此類推,就可以不斷逼近交點.

 1 UCLASS()
 2 class SHOOTERGAME_API UShooterBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
 3 {
 4     GENERATED_UCLASS_BODY()
 5     //求解一個點是否在圓柱體內
 6     //LowCircleCenter:圓柱體底面的圓心位置
 7     //HightCircleCenter:圓柱體的頂面的圓心位置
 8     //R:圓柱體的半徑
 9     //TestPoint需要測試的點
10     UFUNCTION(BlueprintPure, Category = "
Math") 11 static bool IsInCylinder(FVector LowCircleCenter, FVector HighCircleCenter, float R, FVector TestPoint); 12 13 //線段與圓柱的交點 采用叠代逼近的方式 14 //兩個點必須一個在圓柱體內,一個在圓柱體外 15 //LowCircleCenter:圓柱體底面的圓心位置 16 //HightCircleCenter:圓柱體的頂面的圓心位置 17 //R:圓柱體的半徑 18 //StartPoint:線段起始點 19 //EndPoint:線段終點
20 //Tolerance:叠代精度 21 //HasIntersection:是否有交點 22 //InSurface:交點的位置,在柱面上 0,在底面 -1,在頂面 1 23 UFUNCTION(BlueprintPure, Category = "Math") 24 static FVector LineAndCylinderIntersection(FVector LowCircleCenter, FVector HighCircleCenter, float R, FVector StartPoint, FVector EndPoint, float Tolerance, bool& HasIntersection, int32& InSurface); 25 }

 1 //求解一個點是否在圓柱體內
 2 //LowCircleCenter:圓柱體底面的圓心位置
 3 //HightCircleCenter:圓柱體的頂面的圓心位置
 4 //R:圓柱體的半徑
 5 //TestPoint需要測試的點
 6 
 7 bool UShooterBlueprintFunctionLibrary::IsInCylinder(FVector LowCircleCenter, FVector HighCircleCenter, float R, FVector TestPoint)
 8 {
 9     FVector MiddlePoint = (LowCircleCenter + HighCircleCenter) / 2;
10 
11     FVector CylinderDir = (HighCircleCenter - LowCircleCenter);
12 
13     float Height = CylinderDir.Size();
14 
15     CylinderDir.Normalize();
16 
17     FVector StartDir = TestPoint - MiddlePoint;
18 
19     float StartProj = FVector::DotProduct(StartDir, CylinderDir);
20     //判斷此點是否在圓柱體的高度範圍內
21     if (FMath::Abs(StartProj) > Height / 2)
22     {
23         return false;
24     }
25     
26     //判斷此點是否在圓柱體的橫向距離內
27     float TestDist = FMath::PointDistToLine(TestPoint, CylinderDir, MiddlePoint);
28 
29     return TestDist <= R;
30 
31 }
 1 //求解線段與圓柱體的交點,兩個點必須一個在圓柱體內,一個在圓柱體外
 2 //LowCircleCenter:圓柱體底面的圓心位置
 3 //HightCircleCenter:圓柱體的頂面的圓心位置
 4 //R:圓柱體的半徑
 5 //StartPoint:線段起始點
 6 //EndPoint:線段終點
 7 //Tolerance:叠代精度
 8 //HasIntersection:是否有交點
 9 //InSurface:交點的位置,在柱面上 0,在底面 -1,在頂面 1
10 FVector UShooterBlueprintFunctionLibrary::LineAndCylinderIntersection(FVector LowCircleCenter, FVector HighCircleCenter, float R, FVector StartPoint, FVector EndPoint, float Tolerance, bool& HasIntersection, int32& InSurface)
11 {
12     Tolerance = Tolerance <= 0.1f ? 0.1f : Tolerance;
13     //兩個點必須一個在圓柱體內,一個在圓柱體外
14     if (!(IsInCylinder(LowCircleCenter, HighCircleCenter, R, StartPoint) ^ IsInCylinder(LowCircleCenter, HighCircleCenter, R, EndPoint)))
15     {
16         HasIntersection = false;
17         return FVector::ZeroVector;
18     }
19 
20     HasIntersection = true;
21 
22     FVector Point1 = StartPoint;
23     FVector Point2 = EndPoint;
24 
25     FVector MiddlePoint = (Point1 + Point2) / 2;
26 
27     float DiffSize = (Point1 - Point2).SizeSquared();
28 
29     int32 Times = 0;
30     //叠代算法
31     while (DiffSize > Tolerance)
32     {
33         //選擇下一個叠代點
34         if (IsInCylinder(LowCircleCenter, HighCircleCenter, R, Point1) ^ IsInCylinder(LowCircleCenter, HighCircleCenter, R, MiddlePoint))
35         {
36             Point2 = MiddlePoint;
37         }
38         else
39         {
40             Point1 = MiddlePoint;
41         }
42 
43         MiddlePoint = (Point1 + Point2) / 2;
44 
45         DiffSize = (Point1 - Point2).SizeSquared();
46 
47         Times++;
48 
49         if (Times >= 40)
50         {
51             break;
52         }
53     }
54 
55     FVector CircleCenterMiddlePoint = (LowCircleCenter + HighCircleCenter) / 2;
56     FVector CircleCenterDir = (HighCircleCenter - LowCircleCenter);
57     CircleCenterDir.Normalize();
58 
59     float TestDist = FMath::PointDistToLine(MiddlePoint, CircleCenterDir, CircleCenterMiddlePoint);
60     //交點在側面,那麽交點與圓柱體中心線的距離小於Tolerance
61     if ((TestDist -  R) * (TestDist - R) <= Tolerance)
62     {
63         InSurface = 0;
64     }
65     else
66     {
67         //可以依據交點與中心點的方向來判斷交點在底面還是頂面
68         if (FVector::DotProduct(MiddlePoint - CircleCenterMiddlePoint, CircleCenterDir) >= 0.0f)
69         {
70             InSurface = 1;
71         }
72         else
73         {
74             InSurface = -1;
75         }
76     }
77 
78     return MiddlePoint;
79 }

使用叠代逼近的方式求解線段與圓柱體的交點