admin 发表于 2024-3-2 20:50:27

C++ UE4 将凹多边形拆分为多个凸多边

/*从多边形的有序的点数组获取顶点和三角面数据信息(凹凸多边形)
        参数1: 顶点数组
        参数2: 是否是逆时针
        */
TArray<TArray<FVector>> GetPolygonDataFromOrderVertexs(TArray<FVector> 采用points, bool 采用antiClockwise)
{
        int t采用pointsNum = 采用points.Num();
        if (t采用pointsNum > 3)
        {
                TArray<TArray<FVector>> polygonDividedArr = DividePolygonIfConcave(采用points, 采用antiClockwise);//递归
                if (polygonDividedArr.Num() > 0)
                {
                        UE采用LOG(LogTemp, Warning, TEXT("多边形分割成了%d个多边形"), polygonDividedArr.Num());
                }
                return polygonDividedArr;
        }
        else
                UE采用LOG(LogTemp, Error, TEXT("多边形分割失败"));
        return TArray<TArray<FVector>>();
}

//分割
/*检查多边形是否是凹多边形,如果是就切割
        参数1:点集
        参数2:需要返回的被切割多边形1
        参数2:需要返回的被切割多边形2
        */
TArray<TArray<FVector>> DividePolygonIfConcave(TArray<FVector> 采用points, bool 采用antiClockwise)
{
        TArray<TArray<FVector>> polygonDividedArr;

        int t采用pointsNum = 采用points.Num();
        if (采用points.Num() < 3)
        {
                return polygonDividedArr;
        }
        else if (t采用pointsNum == 3)
        {
                polygonDividedArr.Add(采用points);
                return polygonDividedArr;
        }
        else if (t采用pointsNum > 3)
        {
                TArray<FVector> 采用dividePolygonA;
                TArray<FVector> 采用dividePolygonB;
                float t采用hei = 采用points.Z;
                SetPointsZvalueBySpecify(采用points, 0);

                FVector t采用p1;
                FVector t采用p2;
                TArray<FVector> t采用dirs = GetVectorArrByPointsArr(采用points);

                bool t采用divideResult = false;
                int t采用indexNew = 0;
                for (int i = 0; i < t采用dirs.Num(); i++)
                {
                        t采用p1 = t采用dirs;
                        if (i == t采用dirs.Num() - 1)
                        {
                                t采用p2 = t采用dirs;
                                t采用indexNew = 0;
                        }
                        else
                        {
                                t采用p2 = t采用dirs;
                                t采用indexNew = i + 1;
                        }

                        float t采用rotateDir = FVector::CrossProduct(t采用p1, t采用p2).Z;

                        //检查出是凹多边形
                        if (t采用rotateDir < -0.01f && 采用antiClockwise == true || t采用rotateDir > 0.01f && 采用antiClockwise == false)
                        {
                                //UE采用LOG(LogTemp, Warning, TEXT("t采用rotateDir:   %f"), t采用rotateDir);
                                UE采用LOG(LogTemp, Warning, TEXT("是凹多边形~~~~~~~~~~~"));
                                //求分割点

                                t采用divideResult = GetRayIntersectionOfVecInVecArr(t采用dirs, 采用points, i, i, t采用pointsNum - 1, 采用dividePolygonA, 采用dividePolygonB);
                                if (t采用divideResult == false)
                                {
                                        t采用divideResult = GetRayIntersectionOfVecInVecArr(t采用dirs, 采用points, i, 0, i - 1, 采用dividePolygonA, 采用dividePolygonB);
                                }
                                if (t采用divideResult == false)
                                {
                                        UE采用LOG(LogTemp, Error, TEXT("线段%d没有得到分割点"), i);
                                }
                                break;
                        }
                }

                if (t采用divideResult == false)
                {
                        SetPointsZvalueBySpecify(采用points, t采用hei);
                        polygonDividedArr.Add(采用points);
                }
                else
                {
                        if (采用dividePolygonA.Num() > 2)
                        {
                                SetPointsZvalueBySpecify(采用dividePolygonA, t采用hei);
                                DividePolygonIfConcave(采用dividePolygonA, 采用antiClockwise);
                        }
                        if (采用dividePolygonB.Num() > 2)
                        {
                                SetPointsZvalueBySpecify(采用dividePolygonB, t采用hei);
                                DividePolygonIfConcave(采用dividePolygonB, 采用antiClockwise);
                        }
                }
        }

        return polygonDividedArr;
}

/*给定点数组的Z值统一化
        */
bool SetPointsZvalueBySpecify(TArray<FVector>& 采用points, float 采用zValue)
{
        if (采用points.Num() > 0)
        {
                for (int i = 0; i < 采用points.Num(); i++)
                {
                        采用points.Z = 采用zValue;
                }
                return true;
        }

        return false;
}

/*根据点数组获取向量数组
        */
TArray<FVector> GetVectorArrByPointsArr(const TArray<FVector> 采用points)
{
        TArray<FVector> t采用res;
        int t采用pointsNum = 采用points.Num();
        if (t采用pointsNum > 1)
        {
                FVector t采用p1;
                FVector t采用p2;
                for (int i = 0; i < 采用points.Num(); i++)
                {
                        t采用p1 = 采用points;
                        if (i == t采用pointsNum - 1)
                        {
                                t采用p2 = 采用points;
                        }
                        else
                        {
                                t采用p2 = 采用points;
                        }

                        t采用res.Add(t采用p2 - t采用p1);
                }
        }

        return t采用res;
}

bool IsRectCross(const FVector& p1, const FVector& p2, const FVector& q1, const FVector& q2)
{
        bool ret = FMath::Min(p1.X, p2.X) <= FMath::Max(q1.X, q2.X) &&
                FMath::Min(q1.X, q2.X) <= FMath::Max(p1.X, p2.X) &&
                FMath::Min(p1.Y, p2.Y) <= FMath::Max(q1.Y, q2.Y) &&
                FMath::Min(q1.Y, q2.Y) <= FMath::Max(p1.Y, p2.Y);
        return ret;
}


//跨立判断
bool IsLineSegmentCross(const FVector& P1, const FVector& P2, const FVector& Q1, const FVector& Q2)
{
        if (
                ((Q1.X - P1.X) * (Q1.Y - Q2.Y) - (Q1.Y - P1.Y) * (Q1.X - Q2.X)) * ((Q1.X - P2.X) * (Q1.Y - Q2.Y) - (Q1.Y - P2.Y) * (Q1.X - Q2.X)) < 0 ||
                ((P1.X - Q1.X) * (P1.Y - P2.Y) - (P1.Y - Q1.Y) * (P1.X - P2.X)) * ((P1.X - Q2.X) * (P1.Y - P2.Y) - (P1.Y - Q2.Y) * (P1.X - P2.X)) < 0
                )
                return true;
        else
                return false;
}


/**
求线段P1P2与Q1Q2的交点。
先进行快速排斥实验和跨立实验确定有交点再进行计算。
交点(X,Y)使用引用返回。
没有验证过
**/
int CheckTwoLineIntersectionResult(const FVector& p1, const FVector& p2, const FVector& q1, const FVector& q2, FVector& t采用intersectionFVector)
{
        if (IsRectCross(p1, p2, q1, q2))
        {
                if (IsLineSegmentCross(p1, p2, q1, q2))
                {
                        //求交点
                        float tmpLeft, tmpRight;
                        tmpLeft = (q2.X - q1.X) * (p1.Y - p2.Y) - (p2.X - p1.X) * (q1.Y - q2.Y);
                        tmpRight = (p1.Y - q1.Y) * (p2.X - p1.X) * (q2.X - q1.X) + q1.X * (q2.Y - q1.Y) * (p2.X - p1.X) - p1.X * (p2.Y - p1.Y) * (q2.X - q1.X);

                        float X = ((float)tmpRight / (float)tmpLeft);

                        tmpLeft = (p1.X - p2.X) * (q2.Y - q1.Y) - (p2.Y - p1.Y) * (q1.X - q2.X);
                        tmpRight = p2.Y * (p1.X - p2.X) * (q2.Y - q1.Y) + (q2.X - p2.X) * (q2.Y - q1.Y) * (p1.Y - p2.Y) - q2.Y * (q1.X - q2.X) * (p2.Y - p1.Y);
                        float Y = ((float)tmpRight / (float)tmpLeft);

                        t采用intersectionFVector = FVector(X, Y, p1.Z);
                        return true;
                }
        }
        return false;
}

//       
TArray<FVector> GetPointsByIndexRange(const TArray<FVector> 采用points, int startIndex, int endIndex)
{
        TArray<FVector> pts;
        int idx = startIndex;
        while ( idx <= endIndex && idx < 采用points.Num() )
        {
                pts.Add(采用points);
                idx++;

                if (idx >= 采用points.Num())
                        idx = 0;
        }
        return pts;
}

/*从向量数组中获取一个向量在这个数组中的延长线与其他向量的交点
        注意:顺序必须先从这个向量的下标开始,不能是0;交点不包括向量端点
        参数1:方向向量数组
        参数2:对应的点数组(长度需保持一致)
        参数3:这个向量的下标
        参数4,5:开始和结束下标
        参数6,7: 根据交点被切分的两组点数组
        返回值:true 为成功,反之无
        */
bool GetRayIntersectionOfVecInVecArr(const TArray<FVector> 采用dirs, const TArray<FVector> 采用points, const int 采用vecIndex, const int 采用beginIndex, const int 采用endIndex,
        TArray<FVector>& 采用dividePolygonA, TArray<FVector>& 采用dividePolygonB)
{
        int t采用dirsNum = 采用dirs.Num();
        int t采用pointsNum = 采用points.Num();

        if (t采用dirsNum > 3 && t采用pointsNum > 3)
        {
                if (t采用dirsNum == t采用pointsNum)
                {
                        if (采用beginIndex >= 0 && 采用beginIndex < t采用dirsNum)
                        {
                                if (采用endIndex >= 0 && 采用endIndex < t采用dirsNum)
                                {
                                        int t采用indexNew = 采用vecIndex == (t采用dirsNum - 1) ? 0 : 采用vecIndex + 1;
                                        FVector t采用beginA = 采用points[采用vecIndex];
                                        FVector t采用endA = t采用beginA + 采用dirs[采用vecIndex];
                                        FVector t采用intersectionPoint;

                                        for (int j = 采用beginIndex; j <= 采用endIndex; j++)
                                        {
                                                if (j != 采用vecIndex && j != t采用indexNew)
                                                {
                                                        FVector t采用beginB = 采用points;
                                                        if (CheckTwoLineIntersectionResult(t采用beginA, t采用endA, t采用beginB, t采用beginB + 采用dirs, t采用intersectionPoint) == 2)
                                                        {
                                                                //给分割的多边形点组加点
                                                                采用dividePolygonA = GetPointsByIndexRange(采用points, t采用indexNew, j);
                                                                采用dividePolygonA.Add(t采用intersectionPoint);
                                                                UE采用LOG(LogTemp, Warning, TEXT("采用dividePolygonA向量数组个数: %d"), 采用dividePolygonA.Num());

                                                                采用dividePolygonB = GetPointsByIndexRange(采用points, j, t采用indexNew);
                                                                if (采用dividePolygonB.Num() > 0)
                                                                {
                                                                        采用dividePolygonB = t采用intersectionPoint;
                                                                }
                                                                UE采用LOG(LogTemp, Warning, TEXT("采用dividePolygonB向量数组个数: %d"), 采用dividePolygonB.Num());
                                                                return true;
                                                        }
                                                }
                                        }
                                }
                        }
                }
        }

        return false;
}
页: [1]
查看完整版本: C++ UE4 将凹多边形拆分为多个凸多边