使用迭代逼近的方式求解线段与圆柱体的交点

需求来源:

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 }

 

posted @ 2017-10-25 18:33  sichuanke  阅读(918)  评论(0编辑  收藏  举报