Files
UnrealEngine/Engine/Source/Runtime/GeometryCore/Private/EdgeSpan.cpp
2025-05-18 13:04:45 +08:00

281 lines
5.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "EdgeSpan.h"
using namespace UE::Geometry;
void FEdgeSpan::Initialize(const FDynamicMesh3* mesh, const TArray<int>& vertices, const TArray<int> & edges, const TArray<int>* BowtieVerticesIn)
{
Mesh = mesh;
Vertices = vertices;
Edges = edges;
if (BowtieVerticesIn != nullptr)
{
BowtieVertices = *BowtieVerticesIn;
bBowtiesCalculated = true;
}
}
void FEdgeSpan::InitializeFromEdges(const TArray<int>& EdgesIn)
{
check(Mesh != nullptr);
Edges = EdgesIn;
int NumEdges = Edges.Num();
Vertices.SetNum(NumEdges + 1);
FIndex2i start_ev = Mesh->GetEdgeV(Edges[0]);
FIndex2i prev_ev = start_ev;
if (NumEdges > 1)
{
for (int i = 1; i < Edges.Num(); ++i)
{
FIndex2i next_ev = Mesh->GetEdgeV(Edges[i]);
Vertices[i] = IndexUtil::FindSharedEdgeVertex(prev_ev, next_ev);
prev_ev = next_ev;
}
Vertices[0] = IndexUtil::FindEdgeOtherVertex(start_ev, Vertices[1]);
Vertices[Vertices.Num() - 1] = IndexUtil::FindEdgeOtherVertex(prev_ev, Vertices[Vertices.Num() - 2]);
}
else
{
Vertices[0] = start_ev[0];
Vertices[1] = start_ev[1];
}
}
bool FEdgeSpan::InitializeFromVertices(const TArray<int>& VerticesIn, bool bAutoOrient)
{
check(Mesh != nullptr);
Vertices = VerticesIn;
int NumVertices = Vertices.Num();
Edges.SetNum(NumVertices - 1);
for (int i = 0; i < NumVertices - 1; ++i)
{
int a = Vertices[i], b = Vertices[i + 1];
Edges[i] = Mesh->FindEdge(a, b);
if (Edges[i] == FDynamicMesh3::InvalidID)
{
checkf(false, TEXT("EdgeSpan.FromVertices: invalid edge [%d,%d]"), a, b);
return false;
}
}
if (bAutoOrient)
{
SetCorrectOrientation();
}
return true;
}
void FEdgeSpan::CalculateBowtieVertices()
{
BowtieVertices.Reset();
int NumVertices = Vertices.Num();
for (int i = 0; i < NumVertices; ++i)
{
if (Mesh->IsBowtieVertex(Vertices[i]))
{
BowtieVertices.Add(Vertices[i]);
}
}
bBowtiesCalculated = true;
}
FAxisAlignedBox3d FEdgeSpan::GetBounds() const
{
FAxisAlignedBox3d box = FAxisAlignedBox3d::Empty();
for (int i = 0; i < Vertices.Num(); ++i)
{
box.Contain(Mesh->GetVertex(Vertices[i]));
}
return box;
}
void FEdgeSpan::GetPolyline(FPolyline3d& PolylineOut) const
{
PolylineOut.Clear();
for (int i = 0; i < Vertices.Num(); ++i)
{
PolylineOut.AppendVertex(Mesh->GetVertex(Vertices[i]));
}
}
bool FEdgeSpan::SetCorrectOrientation()
{
int NumEdges = Edges.Num();
for (int i = 0; i < NumEdges; ++i)
{
int eid = Edges[i];
if (Mesh->IsBoundaryEdge(eid))
{
int a = Vertices[i], b = Vertices[i + 1];
FIndex2i ev = Mesh->GetOrientedBoundaryEdgeV(eid);
if (ev.A == b && ev.B == a)
{
Reverse();
return true;
}
else
{
return false;
}
}
}
return false;
}
bool FEdgeSpan::IsInternalspan() const
{
int NumEdges = Edges.Num();
for (int i = 0; i < NumEdges; ++i)
{
if (Mesh->IsBoundaryEdge(Edges[i]))
{
return false;
}
}
return true;
}
bool FEdgeSpan::IsBoundaryspan(const FDynamicMesh3* TestMesh) const
{
const FDynamicMesh3* UseMesh = (TestMesh != nullptr) ? TestMesh : Mesh;
int NumEdges = Edges.Num();
for (int i = 0; i < NumEdges; ++i)
{
if (UseMesh->IsBoundaryEdge(Edges[i]) == false)
{
return false;
}
}
return true;
}
int FEdgeSpan::FindVertexIndex(int VertexID) const
{
int N = Vertices.Num();
for (int i = 0; i < N; ++i)
{
if (Vertices[i] == VertexID)
{
return i;
}
}
return -1;
}
int FEdgeSpan::FindNearestVertexIndex(const FVector3d& QueryPoint) const
{
int iNear = -1;
double fNearSqr = TNumericLimits<double>::Max();
int N = Vertices.Num();
for (int i = 0; i < N; ++i)
{
FVector3d lv = Mesh->GetVertex(Vertices[i]);
double d2 = DistanceSquared(QueryPoint, lv);
if (d2 < fNearSqr)
{
fNearSqr = d2;
iNear = i;
}
}
return iNear;
}
bool FEdgeSpan::CheckValidity(EValidityCheckFailMode FailMode) const
{
bool is_ok = true;
TFunction<void(bool)> CheckOrFailF = [&](bool b)
{
is_ok = is_ok && b;
};
if (FailMode == EValidityCheckFailMode::Check)
{
CheckOrFailF = [&](bool b)
{
checkf(b, TEXT("FEdgeSpan::CheckValidity failed!"));
is_ok = is_ok && b;
};
}
else if (FailMode == EValidityCheckFailMode::Ensure)
{
CheckOrFailF = [&](bool b)
{
ensureMsgf(b, TEXT("FEdgeSpan::CheckValidity failed!"));
is_ok = is_ok && b;
};
}
CheckOrFailF(Vertices.Num() == (Edges.Num() + 1));
for (int ei = 0; ei < Edges.Num(); ++ei)
{
FIndex2i ev = Mesh->GetEdgeV(Edges[ei]);
CheckOrFailF(Mesh->IsVertex(ev.A));
CheckOrFailF(Mesh->IsVertex(ev.B));
CheckOrFailF(Mesh->FindEdge(ev.A, ev.B) != FDynamicMesh3::InvalidID);
CheckOrFailF(Vertices[ei] == ev.A || Vertices[ei] == ev.B);
CheckOrFailF(Vertices[ei + 1] == ev.A || Vertices[ei + 1] == ev.B);
}
for (int vi = 0; vi < Vertices.Num() - 1; ++vi)
{
int a = Vertices[vi], b = Vertices[vi + 1];
CheckOrFailF(Mesh->IsVertex(a));
CheckOrFailF(Mesh->IsVertex(b));
CheckOrFailF(Mesh->FindEdge(a, b) != FDynamicMesh3::InvalidID);
// @todo rewrite this test for span, has to handle endpoint vertices that only have one nbr
if (vi < Vertices.Num() - 2) {
int n = 0, edge_before_b = Edges[vi], edge_after_b = Edges[(vi + 1) % Vertices.Num()];
for (int nbr_e : Mesh->VtxEdgesItr(b))
{
if (nbr_e == edge_before_b || nbr_e == edge_after_b)
{
n++;
}
}
CheckOrFailF(n == 2);
}
}
return is_ok;
}
void FEdgeSpan::VertexSpanToEdgeSpan(const FDynamicMesh3* Mesh, const TArray<int>& VertexSpan, TArray<int>& OutEdgeSpan)
{
// @todo this function should be in a utility class?
int NV = VertexSpan.Num();
OutEdgeSpan.SetNum(NV - 1);
for (int i = 0; i < NV - 1; ++i)
{
int v0 = VertexSpan[i];
int v1 = VertexSpan[i + 1];
OutEdgeSpan[i] = Mesh->FindEdge(v0, v1);
}
}