//---------------------------------------------------------------------------- // EgalTech 2015-2020 //---------------------------------------------------------------------------- // File : Polygon3d.cpp Data : 03.04.20 Versione : 2.2d1 // Contenuto : Implementazione della classe Polygon3d. // // // // Modifiche : 30.08.15 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "PolygonPlane.h" #include "CurveComposite.h" #include "SurfFlatRegion.h" #include "GeoConst.h" #include "/EgtDev/Include/EGkPolygon3d.h" using namespace std ; //---------------------------------------------------------------------------- bool Polygon3d::Clear( void) { m_Plane.Reset() ; m_vVert.clear() ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::ClearSides( void) { m_vVert.clear() ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::FromRectangle( double dDimX, double dDimY) { // pulisco tutto Clear() ; // verifico i dati if ( dDimX < EPS_SMALL || dDimY < EPS_SMALL) return false ; // assegno il piano m_Plane.Set( 0, Z_AX) ; // assegno i vertici del poligono m_vVert.reserve( 4) ; m_vVert.emplace_back( + 0.5 * dDimX, + 0.5 * dDimY, 0) ; m_vVert.emplace_back( - 0.5 * dDimX, + 0.5 * dDimY, 0) ; m_vVert.emplace_back( - 0.5 * dDimX, - 0.5 * dDimY, 0) ; m_vVert.emplace_back( + 0.5 * dDimX, - 0.5 * dDimY, 0) ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::FromTriangle( const Triangle3d& trTria) { // pulisco tutto Clear() ; // verifico che il triangolo sia valido if ( ! trTria.IsValid()) return false ; // assegno il piano m_Plane.Set( trTria.GetP( 0), trTria.GetN()) ; // assegno i punti m_vVert.emplace_back( trTria.GetP( 0)) ; m_vVert.emplace_back( trTria.GetP( 1)) ; m_vVert.emplace_back( trTria.GetP( 2)) ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::FromPolyLine( const PolyLine& PL) { // pulisco tutto Clear() ; // verifico sia chiusa e piana Plane3d plPlane ; double dArea ; if ( ! PL.IsClosedAndFlat( plPlane, dArea)) return false ; // assegno il piano m_Plane = plPlane ; // assegno i punti Point3d ptP ; bool bPoint = PL.GetFirstPoint( ptP, true) ; // inserisco i punti while ( bPoint) { m_vVert.emplace_back( ptP) ; bPoint = PL.GetNextPoint( ptP, true) ; } return true ; } //---------------------------------------------------------------------------- bool Polygon3d::FromPlaneTrimmedWithBox( const Plane3d& plPlane, const Point3d& ptMin, const Point3d& ptMax, bool bOnEq, bool bOnCt, double dToler) { // pulisco tutto Clear() ; // il piano e il box devono essere validi if ( plPlane.GetVersN().IsSmall() || AreSamePointApprox( ptMin, ptMax)) return false ; // assegno il piano Plane3d plTemp = plPlane ; // se piano molto vicino ad uno dei vertici del box, lo faccio passare esattamente per il vertice if ( dToler > EPS_SMALL) { PNTVECTOR vBoxVert = { Point3d( ptMin.x, ptMin.y, ptMin.z), Point3d( ptMin.x, ptMin.y, ptMax.z), Point3d( ptMax.x, ptMin.y, ptMin.z), Point3d( ptMax.x, ptMin.y, ptMax.z), Point3d( ptMin.x, ptMax.y, ptMin.z), Point3d( ptMin.x, ptMax.y, ptMax.z), Point3d( ptMax.x, ptMax.y, ptMin.z), Point3d( ptMax.x, ptMax.y, ptMax.z)} ; for ( const auto& ptP : vBoxVert) { if ( abs( DistPointPlane( ptP, plTemp)) < dToler) { Point3d ptNear = ProjectPointOnPlane( ptP, plTemp) ; Vector3d vtDelta = ptP - ptNear ; plTemp.Offset( vtDelta * plTemp.GetVersN()) ; break ; } } } // centro del box proiettato nel piano Point3d ptCen = Media( ptMin, ptMax, 0.5) ; ptCen -= (( ptCen - ORIG) * plTemp.GetVersN() - plTemp.GetDist()) * plTemp.GetVersN() ; // raggio del box double dDiam = Dist( ptMin, ptMax) ; // poligono nel suo piano FromRectangle( dDiam, dDiam) ; // riferimento del piano Frame3d frRef ; frRef.Set( ptCen, plTemp.GetVersN(), FromUprightOrtho( plTemp.GetVersN())) ; ToGlob( frRef) ; // lo trimmo con le facce del box Plane3d plFace ; plFace.Set( -ptMin.y, -Y_AX) ; // faccia 1 (Norm = Y-) Trim( plFace, true, bOnEq, bOnCt) ; plFace.Set( -ptMin.z, -Z_AX) ; // faccia 2 (Norm = Z-) Trim( plFace, true, bOnEq, bOnCt) ; plFace.Set( ptMax.y, Y_AX) ; // faccia 3 (Norm = Y+) Trim( plFace, true, bOnEq, bOnCt) ; plFace.Set( ptMax.z, Z_AX) ; // faccia 4 (Norm = Z+) Trim( plFace, true, bOnEq, bOnCt) ; plFace.Set( -ptMin.x, -X_AX) ; // faccia 5 (Norm = X-) Trim( plFace, true, bOnEq, bOnCt) ; plFace.Set( ptMax.x, X_AX) ; // faccia 6 (Norm = X+) Trim( plFace, true, bOnEq, bOnCt) ; // elimino eventuali vertici coincidenti for ( int i = 1 ; i < int( m_vVert.size()) ;) { if ( SqDist( m_vVert[i], m_vVert[i-1]) < SQ_EPS_SMALL) { m_vVert[i-1] = Media( m_vVert[i-1], m_vVert[i]) ; m_vVert.erase( m_vVert.begin() + i) ; } else ++ i ; } return true ; } //---------------------------------------------------------------------------- bool Polygon3d::FromPlaneTrimmedWithBox( const Point3d& ptOn, const Vector3d& vtN, const Point3d& ptMin, const Point3d& ptMax, bool bOnEq, bool bOnCt, double dToler) { Plane3d plPlane ; plPlane.Set( ptOn, vtN) ; return FromPlaneTrimmedWithBox( plPlane, ptMin, ptMax, bOnEq, bOnCt, dToler) ; } //---------------------------------------------------------------------------- bool Polygon3d::Trim( const Plane3d& plPlane, bool bInVsOut, bool bOnEq, bool bOnCt) { // calcolo le distanze dei punti del poligono dal piano int nNbrMinus = 0 ; int nNbrOn = 0 ; int nNbrPlus = 0 ; DBLVECTOR vDist ; vDist.reserve( m_vVert.capacity()) ; for ( auto& ptP : m_vVert) { double dDist = DistPointPlane( ptP, plPlane) ; if ( abs( dDist) < EPS_SMALL) ++ nNbrOn ; else if ( dDist < 0) ++ nNbrMinus ; else ++ nNbrPlus ; vDist.push_back( dDist) ; } // se il poligono giace nel piano if ( nNbrMinus == 0 && nNbrPlus == 0) { // determino se normali equiverse bool bEquivNormal = ( m_Plane.GetVersN() * plPlane.GetVersN() > 0) ; // se relazione tra normali opposte a entrambi i desiderati, lo annullo if ( ( bEquivNormal && ! bOnEq) || ( ! bEquivNormal && ! bOnCt)) m_vVert.clear() ; return true ; } // se altrimenti è tutto dalla parte positiva else if ( nNbrMinus == 0) { // se richiesto interno, lo annullo if ( bInVsOut) m_vVert.clear() ; return true ; } // se altrimenti è tutto dalla parte negativa else if ( nNbrPlus == 0) { // se richiesto esterno, lo annullo if ( ! bInVsOut) m_vVert.clear() ; return true ; } // determino le intersezioni dei lati con il piano for ( size_t i = 0 ; i < m_vVert.size() ; ++ i) { // indice del punto finale del lato size_t j = ( ( i + 1 < m_vVert.size()) ? i + 1 : 0) ; // se il lato attraversa il piano, inserisco il punto di intersezione nel poligono if ( ( vDist[i] > EPS_SMALL && vDist[j] < - EPS_SMALL) || ( vDist[i] < - EPS_SMALL && vDist[j] > EPS_SMALL)) { double dCoeff = abs( vDist[i]) / ( abs( vDist[i]) + abs( vDist[j])) ; Point3d ptInt = Media( m_vVert[i], m_vVert[j], dCoeff) ; m_vVert.insert( m_vVert.begin() + i + 1, ptInt) ; vDist.insert( vDist.begin() + i + 1, 0.0) ; ++ i ; } } // elimino i punti che non rispettano la posizione rispetto al piano for ( size_t i = 0 ; i < m_vVert.size() ; ++ i) { if ( ( bInVsOut && vDist[i] > EPS_SMALL) || ( ! bInVsOut && vDist[i] < - EPS_SMALL)) { m_vVert.erase( m_vVert.begin() + i) ; vDist.erase( vDist.begin() + i) ; -- i ; } } // se i punti rimasti giacciono tutti sul piano (quindi su una linea), annullo il poligono bool bIsLine = true ; for ( auto& dDist : vDist) { if ( abs( dDist) > EPS_SMALL) bIsLine = false ; } if ( bIsLine) m_vVert.clear() ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::Trim( const Polygon3d& plyOther, bool bInVsOut, bool bOnEq, bool bOnCt) { if ( GetSideCount() == 0 || ! plyOther.IsValid()) return true ; return Trim( plyOther.m_Plane, bInVsOut, bOnEq, bOnCt) ; } //---------------------------------------------------------------------------- bool Polygon3d::Add( const Polygon3d& plyOther) { // se l'altro vuoto, non devo fare alcunché if ( plyOther.GetSideCount() == 0) return true ; // se questo vuoto, devo copiare l'altro if ( GetSideCount() == 0) { *this = plyOther ; return true ; } // verifico siano complanari if ( ! AreSameVectorApprox( GetVersN(), plyOther.GetVersN()) && abs( GetPlaneDist() - plyOther.GetPlaneDist()) > EPS_SMALL) return false ; // creo la regione del poligono CurveComposite crvThis ; crvThis.FromPolyLine( GetPolyLine()) ; SurfFlatRegion frThis ; if ( ! frThis.AddExtLoop( crvThis)) return false ; // creo la regione dell'altro poligono CurveComposite crvOther ; crvOther.FromPolyLine( plyOther.GetPolyLine()) ; SurfFlatRegion frOther ; if ( ! frOther.AddExtLoop( crvOther)) return false ; // unisco le due regioni e verifico che il risultato sia un solo pezzo if ( ! frThis.Add( frOther) && frThis.GetChunkCount() != 1) return false ; // recupero il contorno della regione PolyLine PL ; if ( ! frThis.ApproxLoopWithLines( 0, 0, EPS_SMALL, ANG_TOL_MIN_DEG, ICurve::APL_STD, PL)) return false ; // lo impongo come nuovo poligono return FromPolyLine( PL) ; } //---------------------------------------------------------------------------- PolyLine Polygon3d::GetPolyLine( void) const { PolyLine PL ; // se il piano non è definito, errore if ( m_Plane.GetVersN().IsSmall()) return PL ; // converto il poligono in PolyLine for ( size_t i = 0 ; i < m_vVert.size() ; ++ i) PL.AddUPoint( int( i), m_vVert[i]) ; PL.Close() ; return PL ; } //---------------------------------------------------------------------------- Point3d Polygon3d::GetCentroid( void) const { PolygonPlane PolyPlane ; for ( const auto& ptP : m_vVert) PolyPlane.AddPoint( ptP) ; Point3d ptCen ; PolyPlane.GetCentroid( ptCen) ; return ptCen ; } //---------------------------------------------------------------------------- double Polygon3d::GetPerimeter( void) const { double dLen = 0 ; for ( size_t i = 0 ; i < m_vVert.size() ; ++ i) { size_t j = ( i + 1 < m_vVert.size() ? i + 1 : 0) ; dLen += Dist( m_vVert[i], m_vVert[j]) ; } return dLen ; } //---------------------------------------------------------------------------- double Polygon3d::GetArea( void) const { double dArea = 0 ; PolygonPlane PolyPlane ; for ( const auto& ptP : m_vVert) PolyPlane.AddPoint( ptP) ; return ( PolyPlane.GetArea( dArea) ? dArea : 0) ; } //---------------------------------------------------------------------------- void Polygon3d::Translate( const Vector3d& vtMove) { // traslo il piano m_Plane.Translate( vtMove) ; // traslo i punti for ( auto& ptP : m_vVert) ptP.Translate( vtMove) ; } //---------------------------------------------------------------------------- bool Polygon3d::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng) { // ruoto il piano if ( ! m_Plane.Rotate( ptAx, vtAx, dCosAng, dSinAng)) return false ; // ruoto i punti for ( auto& ptP : m_vVert) ptP.Rotate( ptAx, vtAx, dCosAng, dSinAng) ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double dCoeffZ) { // verifico validità della scalatura if ( abs( dCoeffX) < EPS_ZERO && abs( dCoeffY) < EPS_ZERO && abs( dCoeffZ) < EPS_ZERO) return false ; // scalo il piano if ( ! m_Plane.Scale( frRef, dCoeffX, dCoeffY, dCoeffZ)) return false ; // scalo i punti for ( auto& ptP : m_vVert) ptP.Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ; // determino se contiene anche un mirror (numero dispari di coefficienti negativi) bool bMirror = ( dCoeffX < 0) ; bMirror = ( bMirror ? ( dCoeffY > 0) : ( dCoeffY < 0)) ; bMirror = ( bMirror ? ( dCoeffZ > 0) : ( dCoeffZ < 0)) ; if ( bMirror) reverse( m_vVert.begin(), m_vVert.end()) ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::Mirror( const Point3d& ptOn, const Vector3d& vtNorm) { // specchio il piano if ( ! m_Plane.Mirror( ptOn, vtNorm)) return false ; // specchio i punti e ne inverto l'ordine for ( auto& ptP : m_vVert) ptP.Mirror( ptOn, vtNorm) ; reverse( m_vVert.begin(), m_vVert.end()) ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::Shear( const Point3d& ptOn, const Vector3d& vtNorm, const Vector3d& vtDir, double dCoeff) { // stiro il piano if ( ! m_Plane.Shear( ptOn, vtNorm, vtDir, dCoeff)) return false ; // stiro i punti for ( auto& ptP : m_vVert) ptP.Shear( ptOn, vtNorm, vtDir, dCoeff) ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::ToGlob( const Frame3d& frRef) { // trasformo il piano if ( ! m_Plane.ToGlob( frRef)) return false ; // trasformo i punti for ( auto& ptP : m_vVert) ptP.ToGlob( frRef) ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::ToLoc( const Frame3d& frRef) { // trasformo il piano if ( ! m_Plane.ToLoc( frRef)) return false ; // trasformo i punti for ( auto& ptP : m_vVert) ptP.ToLoc( frRef) ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::LocToLoc( const Frame3d& frOri, const Frame3d& frDest) { // se i due riferimenti coincidono, non devo fare alcunché if ( AreSameFrame( frOri, frDest)) return true ; // trasformo il piano if ( ! m_Plane.LocToLoc( frOri, frDest)) return false ; // trasformo i punti for ( auto& ptP : m_vVert) ptP.LocToLoc( frOri, frDest) ; return true ; } //---------------------------------------------------------------------------- bool Polygon3d::GetLocalBBox( BBox3d& b3Loc) const { // assegno il box in locale, scorrendo tutti i punti del contorno b3Loc.Reset() ; for ( const auto& ptP : m_vVert) b3Loc.Add( ptP) ; return true ; } //---------------------------------------------------------------------------- void Polygon3d::Invert( void) { reverse( m_vVert.begin(), m_vVert.end()) ; m_Plane.Invert() ; }