使用叠代逼近的方式求解線段與圓柱體的交點
阿新 • • 發佈:2017-10-25
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 }
使用叠代逼近的方式求解線段與圓柱體的交點