// SPDX-License-Identifier: BSL-1.0 // Geometric Tools, LLC // Copyright (c) 1998-2010 // Distributed under the Boost Software License, Version 1.0. // http://www.boost.org/LICENSE_1_0.txt // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // // File Version: 4.10.0 (2009/11/18) #include "Wm4FoundationPCH.h" #include "Wm4ConvexHull2.h" #include "Wm4Mapper2.h" #include "Wm4Query2Filtered.h" #include "Wm4Query2Int64.h" #include "Wm4Query2TInteger.h" #include "Wm4Query2TRational.h" namespace Wm4 { //---------------------------------------------------------------------------- template ConvexHull2::ConvexHull2 (int iVertexQuantity, Vector2* akVertex, Real fEpsilon, bool bOwner, Query::Type eQueryType) : ConvexHull(iVertexQuantity,fEpsilon,bOwner,eQueryType), m_kLineOrigin(Vector2::ZERO), m_kLineDirection(Vector2::ZERO) { assert(akVertex); m_akVertex = akVertex; m_akSVertex = nullptr; m_pkQuery = nullptr; Mapper2 kMapper(m_iVertexQuantity,m_akVertex,m_fEpsilon); if (kMapper.GetDimension() == 0) { // The values of m_iDimension, m_aiIndex, and m_aiAdjacent were // already initialized by the ConvexHull base class. return; } if (kMapper.GetDimension() == 1) { // The set is (nearly) collinear. The caller is responsible for // creating a ConvexHull1 object. m_iDimension = 1; m_kLineOrigin = kMapper.GetOrigin(); m_kLineDirection = kMapper.GetDirection(0); return; } m_iDimension = 2; int i0 = kMapper.GetExtremeIndex(0); int i1 = kMapper.GetExtremeIndex(1); int i2 = kMapper.GetExtremeIndex(2); m_akSVertex = WM4_NEW Vector2[m_iVertexQuantity]; int i; if (eQueryType != Query::QT_RATIONAL && eQueryType != Query::QT_FILTERED) { // Transform the vertices to the square [0,1]^2. Vector2 kMin = kMapper.GetMin(); Real fScale = ((Real)1.0)/kMapper.GetMaxRange(); for (i = 0; i < m_iVertexQuantity; i++) { m_akSVertex[i] = (m_akVertex[i] - kMin)*fScale; } Real fExpand; if (eQueryType == Query::QT_INT64) { // Scale the vertices to the square [0,2^{20}]^2 to allow use of // 64-bit integers. fExpand = (Real)(1 << 20); m_pkQuery = WM4_NEW Query2Int64(m_iVertexQuantity, m_akSVertex); } else if (eQueryType == Query::QT_INTEGER) { // Scale the vertices to the square [0,2^{24}]^2 to allow use of // TInteger. fExpand = (Real)(1 << 24); m_pkQuery = WM4_NEW Query2TInteger(m_iVertexQuantity, m_akSVertex); } else // eQueryType == Query::QT_REAL { // No scaling for floating point. fExpand = (Real)1.0; m_pkQuery = WM4_NEW Query2(m_iVertexQuantity,m_akSVertex); } for (i = 0; i < m_iVertexQuantity; i++) { m_akSVertex[i] *= fExpand; } } else { // No transformation needed for exact rational arithmetic or filtered // predicates. size_t uiSize = m_iVertexQuantity*sizeof(Vector2); System::Memcpy(m_akSVertex,uiSize,m_akVertex,uiSize); if (eQueryType == Query::QT_RATIONAL) { m_pkQuery = WM4_NEW Query2TRational(m_iVertexQuantity, m_akSVertex); } else // eQueryType == Query::QT_FILTERED { m_pkQuery = WM4_NEW Query2Filtered(m_iVertexQuantity, m_akSVertex,m_fEpsilon); } } Edge* pkE0; Edge* pkE1; Edge* pkE2; if (kMapper.GetExtremeCCW()) { pkE0 = WM4_NEW Edge(i0,i1); pkE1 = WM4_NEW Edge(i1,i2); pkE2 = WM4_NEW Edge(i2,i0); } else { pkE0 = WM4_NEW Edge(i0,i2); pkE1 = WM4_NEW Edge(i2,i1); pkE2 = WM4_NEW Edge(i1,i0); } pkE0->Insert(pkE2,pkE1); pkE1->Insert(pkE0,pkE2); pkE2->Insert(pkE1,pkE0); Edge* pkHull = pkE0; for (i = 0; i < m_iVertexQuantity; i++) { if (!Update(pkHull,i)) { pkHull->DeleteAll(); return; } } pkHull->GetIndices(m_iSimplexQuantity,m_aiIndex); pkHull->DeleteAll(); } //---------------------------------------------------------------------------- template ConvexHull2::~ConvexHull2 () { if (m_bOwner) { WM4_DELETE[] m_akVertex; } WM4_DELETE[] m_akSVertex; WM4_DELETE m_pkQuery; } //---------------------------------------------------------------------------- template const Vector2& ConvexHull2::GetLineOrigin () const { return m_kLineOrigin; } //---------------------------------------------------------------------------- template const Vector2& ConvexHull2::GetLineDirection () const { return m_kLineDirection; } //---------------------------------------------------------------------------- template ConvexHull1* ConvexHull2::GetConvexHull1 () const { assert(m_iDimension == 1); if (m_iDimension != 1) { return nullptr; } Real* afProjection = WM4_NEW Real[m_iVertexQuantity]; for (int i = 0; i < m_iVertexQuantity; i++) { Vector2 kDiff = m_akVertex[i] - m_kLineOrigin; afProjection[i] = m_kLineDirection.Dot(kDiff); } return WM4_NEW ConvexHull1(m_iVertexQuantity,afProjection, m_fEpsilon,true,m_eQueryType); } //---------------------------------------------------------------------------- template bool ConvexHull2::Update (Edge*& rpkHull, int i) { // Locate an edge visible to the input point (if possible). Edge* pkVisible = nullptr; Edge* pkCurrent = rpkHull; do { if (pkCurrent->GetSign(i,m_pkQuery) > 0) { pkVisible = pkCurrent; break; } pkCurrent = pkCurrent->A[1]; } while (pkCurrent != rpkHull); if (!pkVisible) { // The point is inside the current hull; nothing to do. return true; } // Remove the visible edges. Edge* pkAdj0 = pkVisible->A[0]; assert(pkAdj0); if (!pkAdj0) { return false; } Edge* pkAdj1 = pkVisible->A[1]; assert(pkAdj1); if (!pkAdj1) { return false; } pkVisible->DeleteSelf(); while (pkAdj0->GetSign(i,m_pkQuery) > 0) { rpkHull = pkAdj0; pkAdj0 = pkAdj0->A[0]; assert(pkAdj0); if (!pkAdj0) { return false; } pkAdj0->A[1]->DeleteSelf(); } while (pkAdj1->GetSign(i,m_pkQuery) > 0) { rpkHull = pkAdj1; pkAdj1 = pkAdj1->A[1]; assert(pkAdj1); if (!pkAdj1) { return false; } pkAdj1->A[0]->DeleteSelf(); } // Insert the new edges formed by the input point and the end points of // the polyline of invisible edges. Edge* pkEdge0 = WM4_NEW Edge(pkAdj0->V[1],i); Edge* pkEdge1 = WM4_NEW Edge(i,pkAdj1->V[0]); pkEdge0->Insert(pkAdj0,pkEdge1); pkEdge1->Insert(pkEdge0,pkAdj1); rpkHull = pkEdge0; return true; } //---------------------------------------------------------------------------- template ConvexHull2::ConvexHull2 (const char* acFilename) : ConvexHull(0,(Real)0.0,false,Query::QT_REAL) { m_akVertex = nullptr; m_akSVertex = nullptr; m_pkQuery = nullptr; bool bLoaded = Load(acFilename); assert(bLoaded); (void)bLoaded; // avoid warning in Release build } //---------------------------------------------------------------------------- template bool ConvexHull2::Load (const char* acFilename) { FILE* pkIFile = System::Fopen(acFilename,"rb"); if (!pkIFile) { return false; } ConvexHull::Load(pkIFile); WM4_DELETE m_pkQuery; WM4_DELETE[] m_akSVertex; if (m_bOwner) { WM4_DELETE[] m_akVertex; } m_bOwner = true; m_akVertex = WM4_NEW Vector2[m_iVertexQuantity]; m_akSVertex = WM4_NEW Vector2[m_iVertexQuantity]; size_t uiSize = sizeof(Real); int iVQ = 2*m_iVertexQuantity; if (uiSize == 4) { System::Read4le(pkIFile,iVQ,m_akVertex); System::Read4le(pkIFile,iVQ,m_akSVertex); System::Read4le(pkIFile,2,(Real*)m_kLineOrigin); System::Read4le(pkIFile,2,(Real*)m_kLineDirection); } else // iSize == 8 { System::Read8le(pkIFile,iVQ,m_akVertex); System::Read8le(pkIFile,iVQ,m_akSVertex); System::Read8le(pkIFile,2,(Real*)m_kLineOrigin); System::Read8le(pkIFile,2,(Real*)m_kLineDirection); } System::Fclose(pkIFile); switch (m_eQueryType) { case Query::QT_INT64: m_pkQuery = WM4_NEW Query2Int64(m_iVertexQuantity,m_akSVertex); break; case Query::QT_INTEGER: m_pkQuery = WM4_NEW Query2TInteger(m_iVertexQuantity, m_akSVertex); break; case Query::QT_RATIONAL: m_pkQuery = WM4_NEW Query2TRational(m_iVertexQuantity, m_akSVertex); break; case Query::QT_REAL: m_pkQuery = WM4_NEW Query2(m_iVertexQuantity,m_akSVertex); break; case Query::QT_FILTERED: m_pkQuery = WM4_NEW Query2Filtered(m_iVertexQuantity, m_akSVertex,m_fEpsilon); break; } return true; } //---------------------------------------------------------------------------- template bool ConvexHull2::Save (const char* acFilename) const { FILE* pkOFile = System::Fopen(acFilename,"wb"); if (!pkOFile) { return false; } ConvexHull::Save(pkOFile); size_t uiSize = sizeof(Real); int iVQ = 2*m_iVertexQuantity; if (uiSize == 4) { System::Write4le(pkOFile,iVQ,m_akVertex); System::Write4le(pkOFile,iVQ,m_akSVertex); System::Write4le(pkOFile,2,(const Real*)m_kLineOrigin); System::Write4le(pkOFile,2,(const Real*)m_kLineDirection); } else // iSize == 8 { System::Write8le(pkOFile,iVQ,m_akVertex); System::Write8le(pkOFile,iVQ,m_akSVertex); System::Write8le(pkOFile,2,(const Real*)m_kLineOrigin); System::Write8le(pkOFile,2,(const Real*)m_kLineDirection); } System::Fclose(pkOFile); return true; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // ConvexHull2::Edge //---------------------------------------------------------------------------- template ConvexHull2::Edge::Edge (int iV0, int iV1) { V[0] = iV0; V[1] = iV1; A[0] = nullptr; A[1] = nullptr; Sign = 0; Time = -1; } //---------------------------------------------------------------------------- template int ConvexHull2::Edge::GetSign (int i, const Query2* pkQuery) { if (i != Time) { Time = i; Sign = pkQuery->ToLine(i,V[0],V[1]); } return Sign; } //---------------------------------------------------------------------------- template void ConvexHull2::Edge::Insert (Edge* pkAdj0, Edge* pkAdj1) { pkAdj0->A[1] = this; pkAdj1->A[0] = this; A[0] = pkAdj0; A[1] = pkAdj1; } //---------------------------------------------------------------------------- template void ConvexHull2::Edge::DeleteSelf () { if (A[0]) { A[0]->A[1] = nullptr; } if (A[1]) { A[1]->A[0] = nullptr; } WM4_DELETE this; } //---------------------------------------------------------------------------- template void ConvexHull2::Edge::DeleteAll () { Edge* pkAdj = A[1]; while (pkAdj && pkAdj != this) { Edge* pkSave = pkAdj->A[1]; WM4_DELETE pkAdj; pkAdj = pkSave; } assert(pkAdj == this); WM4_DELETE this; } //---------------------------------------------------------------------------- template void ConvexHull2::Edge::GetIndices (int& riHQuantity, int*& raiHIndex) { // Count the number of edge vertices and allocate the index array. riHQuantity = 0; Edge* pkCurrent = this; do { riHQuantity++; pkCurrent = pkCurrent->A[1]; } while (pkCurrent != this); raiHIndex = WM4_NEW int[riHQuantity]; // Fill the index array. riHQuantity = 0; pkCurrent = this; do { raiHIndex[riHQuantity++] = pkCurrent->V[0]; pkCurrent = pkCurrent->A[1]; } while (pkCurrent != this); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // explicit instantiation //---------------------------------------------------------------------------- template WM4_FOUNDATION_ITEM class ConvexHull2; template WM4_FOUNDATION_ITEM class ConvexHull2; //---------------------------------------------------------------------------- }