📄 vertexcollapse.cpp
字号:
// Magic Software, Inc.
// http://www.magic-software.com
// http://www.wild-magic.com
// Copyright (c) 2003. All Rights Reserved
//
// The Wild Magic Library (WML) source code is supplied under the terms of
// the license agreement http://www.magic-software.com/License/WildMagic.pdf
// and may not be copied or disclosed except in accordance with the terms of
// that agreement.
#include <cassert>
#include <cfloat>
#include "WmlDistVec3Lin3.h"
#include "VertexCollapse.h"
//----------------------------------------------------------------------------
VertexCollapse::VertexCollapse (int iVQuantity, Vector3f*& rakVertex,
bool bClosed, int*& raiMap, int& riEQuantity, int*& raiEdge)
{
raiMap = new int[iVQuantity];
if ( bClosed )
{
riEQuantity = iVQuantity;
raiEdge = new int[2*riEQuantity];
if ( iVQuantity == 3 )
{
raiMap[0] = 0;
raiMap[1] = 1;
raiMap[2] = 3;
raiEdge[0] = 0; raiEdge[1] = 1;
raiEdge[2] = 1; raiEdge[3] = 2;
raiEdge[4] = 2; raiEdge[5] = 0;
return;
}
}
else
{
riEQuantity = iVQuantity-1;
raiEdge = new int[2*riEQuantity];
if ( iVQuantity == 2 )
{
raiMap[0] = 0;
raiMap[1] = 1;
raiEdge[0] = 0; raiEdge[1] = 1;
return;
}
}
// create the heap of records
InitializeHeap(iVQuantity,rakVertex,bClosed);
BuildHeap();
assert( IsValid() );
// create the level of detail information for the polyline
int* aiCollapse = new int[iVQuantity];
CollapseVertices(iVQuantity,rakVertex,aiCollapse);
ComputeEdges(iVQuantity,bClosed,aiCollapse,raiMap,riEQuantity,raiEdge);
ReorderVertices(iVQuantity,rakVertex,aiCollapse,riEQuantity,raiEdge);
delete[] aiCollapse;
}
//----------------------------------------------------------------------------
VertexCollapse::~VertexCollapse ()
{
delete[] m_akRecord;
delete[] m_apkHeap;
}
//----------------------------------------------------------------------------
float VertexCollapse::GetWeight (int iM, int iZ, int iP, Vector3f* akVertex)
{
Segment3f kSegment;
kSegment.Origin() = akVertex[iM];
kSegment.Direction() = akVertex[iP] - akVertex[iM];
float fSqrDist = SqrDistance(akVertex[iZ],kSegment);
float fSqrLen = kSegment.Direction().SquaredLength();
return ( fSqrLen > 0.0f ? fSqrDist/fSqrLen : FLT_MAX );
}
//----------------------------------------------------------------------------
void VertexCollapse::InitializeHeap (int iVQuantity, Vector3f* akVertex,
bool bClosed)
{
// Build the initial heap of weights, a max heap. The weights are set
// to negative values so that we get a min heap. TO DO: Modify the
// code to directly implement a min heap.
m_iHQuantity = iVQuantity;
m_akRecord = new Record[m_iHQuantity];
m_apkHeap = new Record*[m_iHQuantity];
int i;
for (i = 0; i < m_iHQuantity; i++)
{
m_akRecord[i].m_iVIndex = i;
m_akRecord[i].m_iHIndex = i;
m_akRecord[i].m_pkLAdj = &m_akRecord[(m_iHQuantity+i-1)%m_iHQuantity];
m_akRecord[i].m_pkRAdj = &m_akRecord[(i+1)%m_iHQuantity];
m_apkHeap[i] = &m_akRecord[i];
}
int iQm1 = m_iHQuantity - 1;
if ( bClosed )
{
int iQm2 = m_iHQuantity - 2;
m_akRecord[0].m_fWeight = GetWeight(iQm1,0,1,akVertex);
m_akRecord[iQm1].m_fWeight = GetWeight(iQm2,iQm1,0,akVertex);
}
else
{
m_akRecord[0].m_fWeight = FLT_MAX;
m_akRecord[iQm1].m_fWeight = FLT_MAX;
}
for (int iM = 0, iZ = 1, iP = 2; iZ < iQm1; iM++, iZ++, iP++)
m_akRecord[iZ].m_fWeight = GetWeight(iM,iZ,iP,akVertex);
}
//----------------------------------------------------------------------------
void VertexCollapse::BuildHeap ()
{
int iLast = m_iHQuantity - 1;
for (int iLeft = iLast/2; iLeft >= 0; iLeft--)
{
Record* pkRecord = m_apkHeap[iLeft];
int iPa = iLeft, iCh = 2*iLeft + 1;
while ( iCh <= iLast )
{
if ( iCh < iLast )
{
if ( m_apkHeap[iCh]->m_fWeight > m_apkHeap[iCh+1]->m_fWeight )
iCh++;
}
if ( m_apkHeap[iCh]->m_fWeight >= pkRecord->m_fWeight )
break;
m_apkHeap[iCh]->m_iHIndex = iPa;
m_apkHeap[iPa] = m_apkHeap[iCh];
iPa = iCh;
iCh = 2*iCh + 1;
}
pkRecord->m_iHIndex = iPa;
m_apkHeap[iPa] = pkRecord;
}
}
//----------------------------------------------------------------------------
int VertexCollapse::RemoveRoot (Vector3f* akVertex)
{
Record* pkRoot = m_apkHeap[0];
int iLast = m_iHQuantity - 1;
Record* pkRecord = m_apkHeap[iLast];
int iPa = 0, iCh = 1;
while ( iCh <= iLast )
{
if ( iCh < iLast )
{
if ( m_apkHeap[iCh]->m_fWeight > m_apkHeap[iCh+1]->m_fWeight )
iCh++;
}
if ( m_apkHeap[iCh]->m_fWeight >= pkRecord->m_fWeight )
break;
m_apkHeap[iCh]->m_iHIndex = iPa;
m_apkHeap[iPa] = m_apkHeap[iCh];
iPa = iCh;
iCh = 2*iCh + 1;
}
pkRecord->m_iHIndex = iPa;
m_apkHeap[iPa] = pkRecord;
m_iHQuantity--;
// remove root from the doubly-linked list
Record* pkLAdj = pkRoot->m_pkLAdj;
Record* pkRAdj = pkRoot->m_pkRAdj;
pkLAdj->m_pkRAdj = pkRAdj;
pkRAdj->m_pkLAdj = pkLAdj;
// update the weights of the vertices affected by the removal
int iM, iZ, iP;
float fWeight;
if ( pkLAdj->m_fWeight != FLT_MAX )
{
iZ = pkLAdj->m_iVIndex;
iM = pkLAdj->m_pkLAdj->m_iVIndex;
iP = pkLAdj->m_pkRAdj->m_iVIndex;
fWeight = GetWeight(iM,iZ,iP,akVertex);
Update(pkLAdj->m_iHIndex,fWeight);
assert( IsValid() );
}
if ( pkRAdj->m_fWeight != FLT_MAX )
{
iZ = pkRAdj->m_iVIndex;
iM = pkRAdj->m_pkLAdj->m_iVIndex;
iP = pkRAdj->m_pkRAdj->m_iVIndex;
fWeight = GetWeight(iM,iZ,iP,akVertex);
Update(pkRAdj->m_iHIndex,fWeight);
assert( IsValid() );
}
return pkRoot->m_iVIndex;
}
//----------------------------------------------------------------------------
void VertexCollapse::Update (int iHIndex, float fWeight)
{
Record* pkRecord = m_apkHeap[iHIndex];
int iPa, iCh, iChP, iMaxCh;
if ( fWeight > pkRecord->m_fWeight )
{
pkRecord->m_fWeight = fWeight;
// new weight larger than old, propagate it towards the leaves
iPa = iHIndex;
iCh = 2*iPa+1;
while ( iCh < m_iHQuantity )
{
// at least one child exists
if ( iCh < m_iHQuantity-1 )
{
// two children exist
iChP = iCh+1;
if ( m_apkHeap[iCh]->m_fWeight <= m_apkHeap[iChP]->m_fWeight )
iMaxCh = iCh;
else
iMaxCh = iChP;
}
else
{
// one child exists
iMaxCh = iCh;
}
if ( m_apkHeap[iMaxCh]->m_fWeight >= fWeight )
break;
m_apkHeap[iMaxCh]->m_iHIndex = iPa;
m_apkHeap[iPa] = m_apkHeap[iMaxCh];
pkRecord->m_iHIndex = iMaxCh;
m_apkHeap[iMaxCh] = pkRecord;
iPa = iMaxCh;
iCh = 2*iPa+1;
}
}
else if ( fWeight < pkRecord->m_fWeight )
{
pkRecord->m_fWeight = fWeight;
// new weight smaller than old, propagate it towards the root
iCh = iHIndex;
while ( iCh > 0 )
{
// a parent exists
iPa = (iCh-1)/2;
if ( m_apkHeap[iPa]->m_fWeight <= fWeight )
break;
m_apkHeap[iPa]->m_iHIndex = iCh;
m_apkHeap[iCh] = m_apkHeap[iPa];
pkRecord->m_iHIndex = iPa;
pkRecord->m_fWeight = fWeight;
m_apkHeap[iPa] = pkRecord;
iCh = iPa;
}
}
}
//----------------------------------------------------------------------------
void VertexCollapse::CollapseVertices (int iVQuantity, Vector3f* akVertex,
int* aiCollapse)
{
for (int i = iVQuantity-1; i >= 0; i--)
aiCollapse[i] = RemoveRoot(akVertex);
}
//----------------------------------------------------------------------------
void VertexCollapse::ComputeEdges (int iVQuantity, bool bClosed,
int* aiCollapse, int* aiMap, int iEQuantity, int* aiEdge)
{
// Compute the edges (first to collapse is last in array). Do not
// collapse last line segment of open polyline. Do not collapse last
// triangle of closed polyline.
int i, iVIndex, iEIndex = 2*iEQuantity-1;
if ( bClosed )
{
for (i = iVQuantity-1; i >= 0; i--)
{
iVIndex = aiCollapse[i];
aiEdge[iEIndex--] = (iVIndex+1) % iVQuantity;
aiEdge[iEIndex--] = iVIndex;
}
}
else
{
for (i = iVQuantity-1; i >= 2; i--)
{
iVIndex = aiCollapse[i];
aiEdge[iEIndex--] = iVIndex+1;
aiEdge[iEIndex--] = iVIndex;
}
iVIndex = aiCollapse[0];
aiEdge[0] = iVIndex;
aiEdge[1] = iVIndex+1;
}
// In the given edge order, find the index in the edge array that
// corresponds to a collapse vertex and save the index for the dynamic
// change in level of detail. This relies on the assumption that a
// vertex is shared by at most two edges.
iEIndex = 2*iEQuantity-1;
for (i = iVQuantity-1; i >= 0; i--)
{
iVIndex = aiCollapse[i];
for (int iE = 0; iE < 2*iEQuantity; iE++)
{
if ( iVIndex == aiEdge[iE] )
{
aiMap[i] = iE;
aiEdge[iE] = aiEdge[iEIndex];
break;
}
}
iEIndex -= 2;
if ( bClosed )
{
if ( iEIndex == 5 )
break;
}
else
{
if ( iEIndex == 1 )
break;
}
}
// restore the edge array to full level of detail
if ( bClosed )
{
for (i = 3; i < iVQuantity; i++)
{
iVIndex = aiCollapse[i];
aiEdge[aiMap[i]] = iVIndex;
}
}
else
{
for (i = 2; i < iVQuantity; i++)
{
iVIndex = aiCollapse[i];
aiEdge[aiMap[i]] = iVIndex;
}
}
}
//----------------------------------------------------------------------------
void VertexCollapse::ReorderVertices (int iVQuantity, Vector3f*& rakVertex,
int* aiCollapse, int iEQuantity, int* aiEdge)
{
int* aiPermute = new int[iVQuantity];
Vector3f* akPVertex = new Vector3f[iVQuantity];
int i;
for (i = 0; i < iVQuantity; i++)
{
int iVIndex = aiCollapse[i];
aiPermute[iVIndex] = i;
akPVertex[i] = rakVertex[iVIndex];
}
for (i = 0; i < 2*iEQuantity; i++)
aiEdge[i] = aiPermute[aiEdge[i]];
delete[] rakVertex;
rakVertex = akPVertex;
delete[] aiPermute;
}
//----------------------------------------------------------------------------
bool VertexCollapse::IsValid (int iStart, int iFinal)
{
for (int iC = iStart; iC <= iFinal; iC++)
{
int iP = (iC-1)/2;
if ( iP > iStart )
{
if ( m_apkHeap[iP]->m_fWeight > m_apkHeap[iC]->m_fWeight )
return false;
if ( m_apkHeap[iP]->m_iHIndex != iP )
return false;
}
}
return true;
}
//----------------------------------------------------------------------------
bool VertexCollapse::IsValid ()
{
return IsValid(0,m_iHQuantity-1);
}
//----------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -