교차 세그먼트의 양 끝점을 연결하는 경로를 생성한다. FCutWorkingInfo::InsertFaceVertices(), FCutWorkingInfo::InsertEdgeVertices() 를 통해 각 세그먼트들이 처리되었지만 교차 세그먼트의 양 끝에 있는 2개의 세그먼트는 연결된 엣지가 없을 수 있으므로 이를 처리하는 것이다.
의사 코드
for ( int SegIdx = 0; SegIdx < Segments.Num(); SegIdx++ )
{
FSegmentToElements& Seg = Segments[ SegIdx ];
// 퇴화 케이스 처리 (같은 점)
if ( Seg.PtOnMeshIdx[ 0 ] == Seg.PtOnMeshIdx[ 1 ] )
{
continue;
}
FPtOnMesh& PtA = IntersectionVerts[ Seg.PtOnMeshIdx[ 0 ] ];
FPtOnMesh& PtB = IntersectionVerts[ Seg.PtOnMeshIdx[ 1 ] ];
// 유효성 검사
if ( PtA.Type != EVertexType::Vertex || PtB.Type != EVertexType::Vertex )
{
bSuccess = false;
continue;
}
// 이미 같은 정점인 경우
if ( PtA.ElemID == PtB.ElemID )
{
continue;
}
// 이미 엣지로 연결된 경우
int EID = Mesh->FindEdge( PtA.ElemID, PtB.ElemID );
if ( EID != FDynamicMesh3::InvalidID )
{
// VertexChains에 추가 (2개 정점)
continue;
}
// Planar Walk 알고리즘으로 경로 찾기
FMeshSurfacePath SurfacePath( Mesh );
int StartTID = Mesh->GetVtxSingleTriangle( PtA.ElemID );
// 절단 평면 법선 계산 (삼각형 법선 × 세그먼트 방향)
FVector3d WalkPlaneNormal = BaseFaceNormals[ Seg.BaseTID ].Cross( PtB.Pos - PtA.Pos );
if ( Normalize( WalkPlaneNormal ) == 0 )
{
// 퇴화 삼각형 처리
if ( FVector3d::DistSquared( PtA.Pos, PtB.Pos ) > SnapToleranceSq )
{
continue; // 이미 연결된 것으로 간주
}
WalkPlaneNormal = GetDegenTriangleEdgeDirection( Mesh, StartTID );
}
// Planar Walk 실행
bool bWalkSuccess = SurfacePath.AddViaPlanarWalk
(
StartTID,
PtA.ElemID,
Mesh->GetVertex( PtA.ElemID ),
-1,
PtB.ElemID,
Mesh->GetVertex( PtB.ElemID ),
WalkPlaneNormal,
nullptr,
false,
FMathd::ZeroTolerance,
SnapToleranceSq,
.001
);
if ( !bWalkSuccess )
{
bSuccess = false;
continue;
}
// 경로를 메시에 실제로 삽입 (엣지 분할 등)
TArray< int > EmbeddedPath;
if ( SurfacePath.EmbedSimplePath( false, EmbeddedPath, false, SnapToleranceSq ) )
{
// VertexChains에 경로 추가
if ( VertexChains )
{
VertexChains->Add( EmbeddedPath.Num() );
VertexChains->Append( EmbeddedPath );
}
}
else
{
bSuccess = false;
}
}
return bSuccess;세그먼트의 양 끝단을 잇기 위해 FMeshSurfacePath를 이용한다.
- FMeshSurfacePath::AddViaPlanarWalk()를 통해 메쉬상의 경로( 세그먼트의 양 끝단을 잇는 )를 찾는다.
- 찾은 경로는 다시 FMeshSurfacePath::EmbedSimplePath()를 통해 메쉬에 임베딩( 삽입 )한다.
특수 케이스 처리
퇴화 삼각형
- 거의 선분이나 점으로 찌그러진 삼각형
GetDegenTriangleEdgeDirection()으로 대체 방향 계산
이미 연결된 경우
FindEdge()로 확인- 이미 엣지가 존재하면 건너뛰기
동일한 정점
- 세그먼트의 양 끝점이 같은 정점인 경우
- 퇴화 케이스로 처리
너무 가까운 점들
SnapToleranceSq기반 판단- 이미 연결된 것으로 간주
반환값
모든 세그먼트의 연결이 성공하면 true, 하나라도 실패하면 false를 반환한다.
VertexChains (선택적 출력)
- 각 세그먼트의 연결 경로 정보
- 형식:
[길이, v0, v1, ..., vn]
SegmentToChain (선택적 출력)
- 각 세그먼트가 VertexChains의 어느 인덱스에 저장되어 있는지 매핑