Bootstrap

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

记录一下根据网络内容完成的算法,将一个凹多边形拆分为多个凸多边形的算法,语法借助了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[0].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[i];
			if (i == t_dirs.Num() - 1)
			{
				t_p2 = t_dirs[0];
				t_indexNew = 0;
			}
			else
			{
				t_p2 = t_dirs[i + 1];
				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[i].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[i];
			if (i == t_pointsNum - 1)
			{
				t_p2 = _points[0];
			}
			else
			{
				t_p2 = _points[i + 1];
			}

			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]);
		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[j];
							if (CheckTwoLineIntersectionResult(t_beginA, t_endA, t_beginB, t_beginB + _dirs[j], 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[0] = t_intersectionPoint;
								}
								UE_LOG(LogTemp, Warning, TEXT("_dividePolygonB向量数组个数: %d"), _dividePolygonB.Num());
								return true;
							}
						}
					}
				}
			}
		}
	}

	return false;
}

通过调用函数 GetPolygonDataFromOrderVertexs 即可完成凹多边形的切分。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;