//---------------------------------------------------------------------------- // EgalTech 2023 //---------------------------------------------------------------------------- // File : Tree.h Data : 21.04.23 Versione : // Contenuto : Implementazione della classe Cell di un albero binario Tree. // // // // Modifiche : 21.04.23 DB Creazione modulo. // // //---------------------------------------------------------------------------- #pragma once //--------------------------- Include ---------------------------------------- #include "SurfBezier.h" #include "GeoConst.h" #include "CurveLine.h" #include "/EgtDev/Include/EGkPolyLine.h" #include "/EgtDev/Include/EGkChainCurves.h" #include #include struct PairHashInt64 { size_t operator()( const std::pair& key) const { size_t h1 = std::hash{}(key.first) ; size_t h2 = std::hash{}(key.second) ; return h1 ^ (h2 << 1); // Combine hashes } } ; //---------------------------------------------------------------------------- struct Inters { int nIn ; int nOut ; PNTVECTOR vpt ; bool bCCW ; int nChunk ; bool bSortedbyStart ; // nIn e nOut sono flag che indicano da quale lato ho l'ingresso e l'uscita a partire dal lato top in senso antiorario // oltre il 3 sono le celle adiacenti in diagonale al vertice-> 4 corrisponde al ptTl e da lì in senso antiorario // -1 se la curva è sempre dentro la cella // riordino le intersezioni per lato in senso antiorario dal top // se ho più intersezioni che entrano in un lato le riordino considerando che percorro i lati in senso antiorario a partire da ptTR bool operator < ( Inters& b) { // trovo in che ordine stanno i due start, tenendo conto anche della possibilità che siano vertici INTVECTOR vEdges = { 7, 0, 4, 1, 5, 2, 6, 3} ; const auto iter1 = find( vEdges.begin(), vEdges.end(), nIn) ; int nPos1 = std::distance( vEdges.begin(), iter1) ; const auto iter2 = find( vEdges.begin(), vEdges.end(), b.nIn) ; int nPos2 = std::distance( vEdges.begin(), iter2) ; // se sono loop interni li ordino in modo decrescente rispetto all'area bool bEqIn = ( nIn == b.nIn) ; double dAreaA = 0 , dAreaB = 0 ; if ( bEqIn && nIn == -1) { PolyLine pl ; for ( int k = 0 ; k < (int)vpt.size(); ++ k) pl.AddUPoint( k, vpt[k]) ; pl.Close() ; pl.GetAreaXY( dAreaA) ; pl.Clear() ; for ( int k = 0 ; k < (int)b.vpt.size(); ++ k) pl.AddUPoint( k, b.vpt[k]) ; pl.Close() ; pl.GetAreaXY( dAreaB) ; } // se nIn è un vertice sistemo il valore int nEdgeIn = nIn ; if ( nIn > 3) nEdgeIn = nIn - 4 ; return ( nPos1 < nPos2 || ( bEqIn && nEdgeIn == -1 && abs( dAreaA) > abs( dAreaB)) || ( bEqIn && nEdgeIn == 0 && vpt[0].x > b.vpt[0].x) || ( bEqIn && nEdgeIn == 1 && vpt[0].y > b.vpt[0].y) || ( bEqIn && nEdgeIn == 2 && vpt[0].x < b.vpt[0].x) || ( bEqIn && nEdgeIn == 3 && vpt[0].y < b.vpt[0].y)) ; } static bool FirstEncounter( Inters& a, Inters& b) { // riordino in base al lato toccato, o dall'uscita o dall'ingresso, che viene prima. // ottengo l'ordine che avrei percorrendo il bordo da ptTR e considerando i loop che incontro, indipendentemente se li incontro nel punto di uscita o ingresso // nell'intersezione salvo se il taglio è stato ordinato guardando l'ingresso o l'uscita INTVECTOR vEdges = { 7, 0, 4, 1, 5, 2, 6, 3} ; // trovo i lati di ingresso e uscita const auto iter1 = find( vEdges.begin(), vEdges.end(), a.nIn) ; int nPos1 = std::distance( vEdges.begin(), iter1) ; const auto iter2 = find( vEdges.begin(), vEdges.end(), a.nOut) ; int nPos2 = std::distance( vEdges.begin(), iter2) ; const auto iter3 = find( vEdges.begin(), vEdges.end(), b.nIn) ; int nPos3 = std::distance( vEdges.begin(), iter3) ; const auto iter4 = find( vEdges.begin(), vEdges.end(), b.nOut) ; int nPos4 = std::distance( vEdges.begin(), iter4) ; int nFirstA = 0 ; int nFirstB = 0 ; // salvo l'indice del primo punto dell'intersezione che ho incontrato scorrendo il bordo da ptTR // salvo il lato che viene prima confrontando ingresso e uscita if ( nPos2 < nPos1) { nPos1 = nPos2 ; nFirstA = int( a.vpt.size()) - 1 ; } // se ingresso e uscita sono sullo stesso lato allora confronto le coordinate per capire se viene prima l'ingresso o l'uscita else if ( nPos2 == nPos1 ) { if ( nPos1 == 0 ) nFirstA = a.vpt[0].x > a.vpt.back().x ? 0 : ( int( a.vpt.size()) - 1) ; else if ( nPos1 == 1 ) nFirstA = a.vpt[0].y > a.vpt.back().y ? 0 : ( int( a.vpt.size()) - 1) ; else if ( nPos1 == 2 ) nFirstA = a.vpt[0].x < a.vpt.back().x ? 0 : ( int( a.vpt.size()) - 1) ; else if ( nPos1 == 3 ) nFirstA = a.vpt[0].y < a.vpt.back().y ? 0 : ( int( a.vpt.size()) - 1) ; } if ( nPos4 < nPos3) { nPos3 = nPos4 ; nFirstB = int( b.vpt.size()) - 1 ; } else if ( nPos4 == nPos3 ) { if ( nPos3 == 0 ) nFirstB = b.vpt[0].x > b.vpt.back().x ? 0 : ( int( b.vpt.size()) - 1) ; else if ( nPos3 == 1 ) nFirstB = b.vpt[0].y > b.vpt.back().y ? 0 : ( int( b.vpt.size()) - 1) ; else if ( nPos3 == 2 ) nFirstB = b.vpt[0].x < b.vpt.back().x ? 0 : ( int( b.vpt.size()) - 1) ; else if ( nPos3 == 3 ) nFirstB = b.vpt[0].y < b.vpt.back().y ? 0 : ( int( b.vpt.size()) - 1) ; } a.bSortedbyStart = nFirstA == 0 ; b.bSortedbyStart = nFirstB == 0 ; // se sono diversi ritorno il confronto if ( nPos1 != nPos3) return nPos1 < nPos3 ; // se sono uguali devo valutare il punto di intersezione return ( nPos1 == 0 && a.vpt[nFirstA].x > b.vpt[nFirstB].x) || ( nPos1 == 1 && a.vpt[nFirstA].y > b.vpt[nFirstB].y) || ( nPos1 == 2 && a.vpt[nFirstA].x < b.vpt[nFirstB].x) || ( nPos1 == 3 && a.vpt[nFirstA].y < b.vpt[nFirstB].y) ; } bool operator == ( Inters& b) { return AreSamePointExact( vpt[0], b.vpt[0]) ; } bool operator != ( Inters& b) { return ! AreSamePointExact( vpt[0], b.vpt[0]) ; } } ; //---------------------------------------------------------------------------- class Cell { // Edge 0 ( Top) // Edge 4 ( NW) __________________ Edge 7 ( NE) // | | // | | // Edge 1 ( Left) | | Edge 3 ( Right) // | | // | | // |_________________| // Edge 5 ( SW) Edge 2 (Bottom) Edge 6 ( SE) public : enum Collapsed { TO_VERIFY = -1, // da verificare NO_COLLAPSE = 0, // non ho coppie di lati collassati VERT_EDGES = 1, // coppia di lati verticali(1-3) sono collassati HORIZ_EDGES = 2} ; // coppia di lati verticali(0-2) sono collassati public : ~Cell( void) {} Cell( void) : m_nId( -1), m_nTop( -2), m_nBottom( -2), m_nLeft( -2), m_nRight( -2), m_nParent( -2), m_nDepth( 0), m_dSplit( 0), m_nChild1( -2), m_nChild2( -2), m_nFlag( -1), m_bLabelled( false), m_nRightEdgeIn( -1), m_bOnLeftEdge( false), m_bOnTopEdge( false), m_nVertToErase( -1), m_nCollapsed( -1), m_ptPbl( ORIG), m_ptPtr( SBZ_TREG_COEFF, SBZ_TREG_COEFF, 0), m_bProcessed( false), m_bSplitVert( true) {} Cell( const Point3d& ptBL, const Point3d& ptTR) : m_nId( -1), m_nTop( -2), m_nBottom( -2), m_nLeft( -2), m_nRight( -2), m_nParent( -2), m_nDepth( 0), m_dSplit( 0), m_nChild1( -2), m_nChild2( -2), m_nFlag( -1), m_bLabelled( 0), m_nRightEdgeIn( -1), m_bOnLeftEdge( false), m_bOnTopEdge( false), m_nVertToErase( -1), m_nCollapsed( -1), m_ptPbl( ptBL), m_ptPtr( ptTR), m_bProcessed( false), m_bSplitVert( true) {} bool IsSame( const Cell& cOtherCell) const { return ( m_nId == cOtherCell.m_nId) ; } void SetBottomLeft( const Point3d& ptBL) { m_ptPbl = ptBL ; } void SetTopRight( const Point3d& ptTR) { m_ptPtr = ptTR ; } void SetSplitDirVert( bool bVert) { m_bSplitVert = bVert ; } void SetParent( int nParent) { m_nParent = nParent ; } Point3d GetBottomLeft( void) const { return m_ptPbl ; } Point3d GetTopRight( void) const { return m_ptPtr ; } Point3d GetTopLeft( void) const { return Point3d( m_ptPbl.x, m_ptPtr.y) ; } Point3d GetBottomRight( void) const { return Point3d( m_ptPtr.x, m_ptPbl.y); } Point3d GetCenter( void) const { return ( m_ptPbl + m_ptPtr) / 2 ; } double GetSplitValue( void) const { return m_dSplit ; } bool IsSplitVert( void) const // se true la cella verrebbe splittata verticalmente, altrimenti orizzontalmente { return m_bSplitVert ; } bool IsLeaf( void) const // flag che indica se la cella ha figli o se è una foglia { return ( m_nChild1 == -2 && m_nChild2 == -2) ; } bool IsProcessed( void) const // flag che indica se tutti i figli della cella, se ce ne sono, sono stati processati { return m_bProcessed ; } void SetProcessed( bool bProcessed = true) { m_bProcessed = bProcessed ; } static bool minorX( const Cell& c1, const Cell& c2) { return c1.m_ptPbl.x < c2.m_ptPbl.x ; } static bool minorY( const Cell& c1, const Cell& c2) { return c1.m_ptPbl.y < c2.m_ptPbl.y ; } void AddPoly( int nPolyId) { m_vnPolyId.push_back( nPolyId) ;} public : int m_nId ; // Id della cella int m_nTop ; // cella adiacente al lato top int m_nBottom ; // cella adiacente al lato bottom int m_nLeft ; // cella adiacente al lato left int m_nRight ; // cella adiacente al lato right int m_nParent ; // cella genitore int m_nDepth ; // profondità della cella rispetto a root double m_dSplit ; // parametro a cui è stata splittata la cella int m_nChild1 ; // prima cella figlio int m_nChild2 ; // seconda cella figlio int m_nFlag ; // flag che indica la caratterizzazione della cella rispetto ai loop di trim // 0 esterna, 1 intersecata, 2 contiene un loop, 3 intersecata e contenente un loop, 4 contenuta in un loop bool m_bLabelled ; // flag che indica se la cella è stata attraversata durante l'ultima fase del labelling int m_nRightEdgeIn ; // 0 right edge fuori, 1 right edge dentro, 2 metà e metà bool m_bOnLeftEdge ; // flag che indica se la cella è sul lato sinistro ( per superfici chiuse sul parametro U) bool m_bOnTopEdge ; // flag che indica se la cella è sul lato top ( per superfici chiuse sul parametro V) std::vector m_vInters ; // vettore delle intersezioni della cella con i loop di trim // ogni elemento del vettore è l'insieme dei punti che caratterizza un attraversamento della cella int m_nVertToErase ; // vertice da eliminare dal poligono della cella, in caso di lato sovrapposto ad un lato di polo // contati in senso CCW a partire dal bottom left INTVECTOR m_vnPolyId ; // indici dei poligoni associati a questa cella nel vettore m_vPolygons del Tree int m_nCollapsed ; // flag che indica se la coppia di lati verticali (1) o orizzontali(2) sono collassati private : Point3d m_ptPbl ; // punto bottom left Point3d m_ptPtr ; // punto top right bool m_bProcessed ; // flag che indica se la cella è stata processata bool m_bSplitVert ; // flag che indica in quale direzione è stata divisa la cella } ; //---------------------------------------------------------------------------- class Tree { public : ~Tree( void) ; Tree( void) ; Tree( const Point3d ptBl, const Point3d ptTr) ; // da usare solo nel caso in cui si voglia aggiungere tagli ad un'unica cella e del risultato ottenere il contorno bool SetSurf( const SurfBezier* pSrfBz, const Point3d& ptMin = ORIG, const Point3d& ptMax = ORIG) ; bool GetIndependentTrees( BIPNTVECTOR& vTrees) ; // calcolo la suddivisione della superficie solo sulle singole bbox dei loop di trim ( unendo quelli vicini) bool BuildTree( double dLinTol = LIN_TOL_STD, double dSideMin = 1, // è la minima lunghezza del lato di una cella double dSideMax = INFINITO) ; // è la massima dimensione di un triangolo della trimesh bool GetPolygons( POLYLINEMATRIX& vvPolygons, POLYLINEMATRIX& vvPolygons3d, std::vector& vCCEdges3D, ICRVCOMPOPOVECTOR& vCCLoops, bool bUpdateEdges) ; bool GetPolygonsBasic( POLYLINEVECTOR& vPolygons, POLYLINEVECTOR& vPolygonsCorrected, POLYLINEVECTOR& vPolygons3d) ; // restituisce il poligono corrispondente ad ogni cella foglia dell'albero // ad ogni poligono sono stati aggiunti tutti i vertici dei vicini posizionati sui suoi lati // ad alcuni poligoni potrebbero venire tolti dei punti per evitare errori dovuti ad eventuali poli sui bordi del parametrico bool GetLeaves( std::vector& vLeaves) const ; // restituisce gli indici delle foglie nell'albero bool GetEdges3D( std::vector& mCCEdge, POLYLINEVECTOR& vPolygons) ; // restituisce gli edge 3D come polyline bool GetSplitLoops( ICRVCOMPOPOVECTOR& vCCLoopSplit) const // restituisce i loop splitatti ai confini delle celle { for ( int i = 0 ; i < int( m_vCCLoop2D.size()); ++i) vCCLoopSplit.emplace_back( m_vCCLoop2D[i]->Clone()) ; return true ; } // funzioni da usare per ricostruire tagli che vanno aggiunti allo spazio parametrico bool AddCutsToRoot( POLYLINEVECTOR& vCuts) ; // aggiunge i tagli al tree bool CreateCellContour( POLYLINEMATRIX& vPolygons) ; // crea il nuovo contorno esterno, tenendo conto dei tagli bool IsClosedU( void) const // restituisce flag di chiusara in U { return m_bClosedU ; } bool IsClosedV( void) const // restituisce flag di chiusara in V { return m_bClosedV ; } BOOLVECTOR GetPoles( void) // restituisce i flag che indicano se i lati sono collassati in dei poli { return m_vbPole ; } private : bool Split( int nId, double dSplitValue) ; // funzione di split di una cella al parametro indicato nella direzione data da bVert bool Split( int nId) ; // funzione di split di una cella dell'albero a metà nella direzione data da bVert int GetHeightLeaves( int nId, INTVECTOR& vnLeaves, int d = 0) const ; // altezza del subtree a partire dal nodo nId int GetDepth( int nId, int nRef) const ; // livello del nodo nId void GetTopNeigh( int nId, INTVECTOR& vTopNeighs, DBLDBL ddInt = DBLDBL(0,0)) const ; // restituisce le celle foglie che sono adiacenti al lato top ( la coppia di double è per dare un intervallo diverso su cui limitare i vicini) void GetBottomNeigh( int nId, INTVECTOR& vBottomNeighs, DBLDBL ddInt = DBLDBL(0,0)) const ; // restituisce le celle foglie che sono adiacenti al lato bottom void GetLeftNeigh( int nId, INTVECTOR& vLeftNeighs, DBLDBL ddInt = DBLDBL(0,0)) const ; // restituisce le celle foglie che sono adiacenti al lato left void GetRightNeigh( int nId, INTVECTOR& vRightNeighs, DBLDBL ddInt = DBLDBL(0,0)) const ; // restituisce le celle foglie che sono adiacenti al lato right void GetRootNeigh( int nEdge, INTVECTOR& vNeigh) ; // restituisce le foglie dell'albero che sono adiacenti al lato nEdge, numerato a partire dal top ( 0) in senso antiorario void ResetTree( void) ; // resetto m_bProcessed a false per tutti i nodi dell'albero INTVECTOR FindCell( const Point3d& ptToAssign, const CurveLine& cl, bool bRecurs = false) const ; // dato un punto, trova la cella foglia a cui appartiene INTVECTOR FindCell( const Point3d& ptToAssign, const CurveLine& cl, INTVECTOR vCells, bool bRecurs = false) const ; // dato un punto, trova la cella foglia a cui appartiene bool TraceLoopLabelCell( const POLYLINEVECTOR& vplPolygons) ; // tracing dei loop e labelling delle celle bool FindInters( int& nId, const CurveLine& clTrim, const PolyLine& plPolygon, PNTVECTOR& vptInters, bool bFirstInters = true) ; // trova le intersezioni tra una cella e una linea di trim // resituisce l'id della cella verso cui la curva di trim esce e il vettore delle intersezioni per la cella successiva con il primo punto bool CreateCellPolygons( int nLeafId, POLYLINEMATRIX& vPolygons, POLYLINEMATRIX& vPolygons3d, INTVECTOR& vToCheck, int& nPoly, INTVECTOR& vnParentChunk, const PolyLine& plCell, const PolyLine& plCell3d) ; // crea i poligoni della cella passata. richiede anche la funzione CreateIslandAndHoles per completare i poligoni. bool CreateIslandAndHoles( int nLeafId, POLYLINEMATRIX& vPolygons, POLYLINEMATRIX& vvPolygons3d, int& nPoly, INTVECTOR& vnParentChunk, const PolyLine& plPolygonsBasic, const PolyLine& plPolygonsBasic3d) ; // ai poligoni generati da CreatePolygonsCell aggiunge i loop che creano isole o buchi all'interno della singola cella bool CheckIfBefore( const PolyLine& pl, int nEdge) const ; // controllo se ptEnd è prima di ptStart sul lato nEdge rispetto al senso antiorario bool CheckIfBefore( const Inters& inA) const ; // controlla se l'ingresso è prima dell'uscita in senso antiorario a partire da ptTR. bool CheckIfBefore( int nEdge1, const Point3d& ptP1, int nEdge2, const Point3d& ptP2) const ; // verifico quale punto viene prima tra pt1 e pt2 a partire da ptTR girando in senso CCW (punto 1 su edge 1 e punto 2 su edge 2, rispetto al lato 3) bool CheckIfBefore( int nEdge, const Point3d& ptP1, const Point3d& ptP2, int nEdge2 = -1) const ; // sul lato nEdge controllo se ptP1 viene prima di ptP2. bool AreSameEdge( int nEdge1, int nEdge2) const ; // indica se i due edge sono lo stesso. Un vertice adiacente ad un edge viene considerato uguale a questo edge bool CheckIfBefore( int nEdgeA, int nEdgeB) const ; // per due edge uguali per la funzione AreSameEdge chiedo se EdgeA viene prima di EdgeB bool AddVertex( int nId, const PNTMATRIX& vEdgeVertex, const PNTMATRIX& vEdgeVertex3d, PolyLine& plTrimmedPoly, int& c, const Point3d& ptToAdd, PolyLine& plTrimmedPoly3d, bool ForTriangulation, Point3d& ptLast) const ; // aggiunge un punto ad un poligono in una cella, premurandosi di aggiungere eventualmente vertici o punti di celle vicine di cui tenere conto bool SetRightEdgeIn( int nId) ; // categorizza la cella in base all'edge destro per poter poi definire m_nFlag bool CategorizeCell( int nId) ; // categorizza la cella in base al flag m_nFlag (dentro, fuori, intersecata) bool CheckIfBetween( const Inters& inA, const Inters& inB) const ; // / controllo se inB è compreso tra l'end e lo start di inA (in senso CCW) bool OnWhichEdge( int nId, const Point3d& ptToAssign, int& nEdge) const ; // indica a quale edge o vertice il punto è vicino entro EPS_SMALL bool AdjustCuts( void) ; bool UpdateSplitLoop( ICurveComposite* pCC, Point3d& pt) ; bool VerifyLoopOrientation( ICURVEPLIST& vpCrv, BOOLVECTOR& vbOrientation) const ; // verifico l'orientazione ( CCW o CW) delle polyline in base a come sono contenute le une nelle altre bool AdjustLoop( PolyLine& pl, POLYLINEVECTOR& vPl, BOOLVECTOR& vbOrientation) const ; bool GetPoint(double dU, double dV, Point3d& ptP) const ; bool SavePoint( double dU, double dV, const Point3d& ptP) ; private : const SurfBezier* m_pSrfBz ; // superficie di bezier bool m_bTrimmed ; // superficie trimmata std::unordered_map m_mChunk ; // mappa in cui vengono salvati chunk di appartenza per ogni loop di trim std::vector> m_vPlApprox ; // vettore contenente le approssimazioni dei loop // il bool indica se la curva è CCW bool m_bBilinear ; // superficie bilineare bool m_bMulti ; // superficie multi-patch bool m_bClosedU ; // superficie chiusa lungo il parametro U bool m_bClosedV ; // superficie chiusa lungo il parametro V BOOLVECTOR m_vbPole ; // vettore che indica se i vari lati sono collassati in poli ( indici riferiti all'ordine degli edge) int m_nDegU ; // grado della superficie nel parametro U int m_nDegV ; // grado della superficie nel parametro V int m_nSpanU ; // numero di span lungo il parametro U int m_nSpanV ; // numero di span lungo il parametro V std::unordered_map m_mTree ; // mappa che contiene tutti i nodi e le foglie dell'albero. -2 è puntatore Null e -1 è root mutable std::unordered_map,Point3d,PairHashInt64> m_mPt3d ; // mappa che contiene tutti i punti 3d della superficie calcolati (la chiave sono le coordinate, moltiplicate per 2^24 e trasformate in int) INTVECTOR m_vnLeaves ; // vettore delle foglie INTVECTOR m_vnParents ; // vettore delle celle ottenute dalla divisione preliminare in singole patch ICRVCOMPOPOVECTOR m_vCCLoop2D ; // vettore che contiene le CurveCompo che rappresentano i loop di trim tenendo conto della divisione in celle std::vector> m_vCEdge2D ; // vettore che le chain che rappresentano ciò che resta degli edge originali, tenendo conto dei trim. } ;