//---------------------------------------------------------------------------- // EgalTech 2015-2015 //---------------------------------------------------------------------------- // File : EXE_Nesting.cpp Data : 20.09.14 Versione : 1.6e1 // Contenuto : Funzioni Nesting per EXE. // // // // Modifiche : 20.09.14 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "EXE.h" #include "EXE_Macro.h" #include "/EgtDev/Include/EXeExecutor.h" #include "/EgtDev/Include/EXeConst.h" #include "/EgtDev/Include/EGkCurveComposite.h" #include "/EgtDev/Include/EGkIntersCurves.h" #include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EGkExtText.h" #include "/EgtDev/Include/EGkGdbIterator.h" #include "/EgtDev/Include/EGkIntervals.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include using namespace std ; //---------------------------------------------------------------------------- static bool ApproxCurveIfNeeded( IGdbIterator* pEnt) { const double LIN_TOL_STD = 0.25 ; const double ANG_TOL_STD_DEG = 15 ; const double LIN_FEA_STD = 20 ; // deve essere una curva int nGeoType = pEnt->GetGeoType() ; if ( (nGeoType & GEO_CURVE) == 0) return false ; // verifico se da approssimare if ( nGeoType == CRV_LINE || nGeoType == CRV_ARC) return true ; else if ( nGeoType == CRV_COMPO) { ICurveComposite* pCrvCo = GetCurveComposite( pEnt->GetGeoObj()) ; double dLen ; pCrvCo->GetLength( dLen) ; int nCrvs = pCrvCo->GetCurveCount() ; if ( nCrvs <= 5 && dLen > nCrvs * LIN_FEA_STD) return true ; } // esecuzione dell'approssimazione ICurve* pCrv = GetCurve( pEnt->GetGeoObj()) ; PtrOwner pCC( CreateCurveComposite()) ; PolyArc PA ; bool bOk = pCrv->ApproxWithArcsEx( LIN_TOL_STD, ANG_TOL_STD_DEG, LIN_FEA_STD, PA) && pCC->FromPolyArc( PA) ; bOk = bOk && pEnt->GetGDB()->ReplaceGeoObj( pEnt->GetId(), Release( pCC)) ; return bOk ; } //---------------------------------------------------------------------------- static bool FilterAndApprox( void) { IGeomDB* pGeomDB = GetCurrGeomDB() ; VERIFY_GEOMDB( pGeomDB, false) // preparo gli iteratori PtrOwner pPar( CreateGdbIterator( pGeomDB)) ; PtrOwner pLay( CreateGdbIterator( pGeomDB)) ; PtrOwner pEnt( CreateGdbIterator( pGeomDB)) ; if ( IsNull( pPar) || IsNull( pLay) || IsNull( pEnt)) return false ; // ciclo su tutti gli oggetti del DB geometrico bool bPok = pPar->GoToFirstInGroup( GDB_ID_ROOT) ; while ( bPok) { // sotto la radice sono ammessi solo gruppi di livello User -> Pezzi int nLev ; if ( pPar->GetGdbType() != GDB_TY_GROUP || ! pPar->GetCalcLevel( nLev) || nLev != GDB_LV_USER) { bPok = pPar->EraseAndGoToNext() ; continue ; } // ciclo su tutti gli oggetti del pezzo bool bLok = pLay->GoToFirstInGroup( *Get( pPar)) ; while ( bLok) { // sotto ogni pezzo sono ammessi solo gruppi di livello User -> Layer int nLev ; if ( pLay->GetGdbType() != GDB_TY_GROUP || ! pLay->GetCalcLevel( nLev) || nLev != GDB_LV_USER) { bLok = pLay->EraseAndGoToNext() ; continue ; } // ciclo su tutti gli oggetti del layer bool bEok = pEnt->GoToFirstInGroup( *Get( pLay)) ; while ( bEok) { // sotto ogni layer sono ammesse solo curve di livello User int nLev ; if ( ( pEnt->GetGeoType() & GEO_CURVE) == 0 || ! pEnt->GetCalcLevel( nLev) || nLev != GDB_LV_USER) { bEok = pEnt->EraseAndGoToNext() ; continue ; } // eventuale approx della curva se Bezier o Composita con molti piccoli segmenti ApproxCurveIfNeeded( Get( pEnt)) ; // passo alla successiva entità bEok = pEnt->GoToNext() ; } // passo al successivo layer bLok = pLay->GoToNext() ; } // passo al successivo pezzo bPok = pPar->GoToNext() ; } return true ; } //---------------------------------------------------------------------------- static bool ChainCurves( int& nLay) { // seleziono tutte le curve bool bOk = ExeSelectAll( false) ; // creo un nuovo pezzo con layer in cui mettere tutti i concatenati int nPart = ExeCreateGroup( GDB_ID_ROOT, Frame3d(), RTY_GLOB) ; nLay = ExeCreateGroup( nPart, Frame3d(), RTY_GLOB) ; bOk = bOk && nLay != GDB_ID_NULL ; // concateno tutte le curve INTVECTOR vIds ; vIds.push_back( GDB_ID_SEL) ; int nCount ; int nFirstId = ( bOk ? ExeCreateCurveCompoByChain( nLay, vIds, ORIG, true, RTY_GLOB, &nCount) : GDB_ID_NULL) ; bOk = bOk && nFirstId != GDB_ID_NULL ; // cancello i gruppi rimasti vuoti ExeEraseEmptyParts() ; return bOk ; } //---------------------------------------------------------------------------- bool CreateFlatPartsByRegions( int nLay) { IGeomDB* pGeomDB = GetCurrGeomDB() ; VERIFY_GEOMDB( pGeomDB, false) // recupero il padre del layer int nParentId = pGeomDB->GetParentId( nLay) ; // preparo gli iteratori PtrOwner pEnt( CreateGdbIterator( pGeomDB)) ; if ( IsNull( pEnt)) return false ; // separo le curve aperte da quelle chiuse e salvo vettore aree di queste ultime INTVECTOR vOpenIds ; INTVECTOR vClosedIds ; typedef std::pair INDAREA ; // coppia indice, area typedef std::vector INDAREAVECTOR ; // vettore di coppie indice, area INDAREAVECTOR vArea ; bool bEok = pEnt->GoToFirstInGroup( nLay) ; while ( bEok) { ICurve* pCrv = GetCurve( pEnt->GetGeoObj()) ; if ( pCrv == nullptr) { bEok = pEnt->EraseAndGoToNext() ; } else if ( ! pCrv->IsClosed()) { vOpenIds.push_back( pEnt->GetId()) ; bEok = pEnt->GoToNext() ; } else { vClosedIds.push_back( pEnt->GetId()) ; double dArea ; pCrv->GetAreaXY( dArea) ; if ( fabs( dArea) > 1) { if ( dArea < 0) { pCrv->Invert() ; dArea = - dArea ; } vArea.emplace_back( pEnt->GetId(), dArea) ; } else vOpenIds.push_back( pEnt->GetId()) ; bEok = pEnt->GoToNext() ; } } // ordino le aree in senso decrescente sort( vArea.begin(), vArea.end(), []( const INDAREA& a, const INDAREA&b) { return fabs( a.second) > fabs( b.second) ; }) ; // creo i pezzi a partire dalle curve chiuse più grandi e dalle curve che vi sono contenute for ( int i = 0 ; i < int( vArea.size()) ; ++ i) { if ( vArea[i].first == GDB_ID_NULL) continue ; // creo pezzo con questa geometria int nPartId = pGeomDB->InsertGroup( GDB_ID_NULL, nParentId, GDB_BEFORE, Frame3d()) ; // creo layer nel pezzo int nLayId = pGeomDB->AddGroup( GDB_ID_NULL, nPartId, Frame3d()) ; // sposto il contorno nel pezzo pGeomDB->RelocateGlob( vArea[i].first, nLayId) ; // cerco altri contorni chiusi che siano contenuti completamente o parzialmente ICurve* pCrv = GetCurve( pGeomDB->GetGeoObj( vArea[i].first)) ; for ( int j = i + 1 ; j < int( vArea.size()) ; ++ j) { if ( vArea[j].first == GDB_ID_NULL) continue ; ICurve* pCrv2 = GetCurve( pGeomDB->GetGeoObj( vArea[j].first)) ; // intersezione e classificazione CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCrv2, *pCrv) ; if ( ! intCC.GetCurveClassification( 0, ccClass)) continue ; // se completamente interna if ( ccClass.size() == 1 && ccClass[0].nClass == CRVC_IN) { pGeomDB->RelocateGlob( vArea[j].first, nLayId) ; vArea[j].first = GDB_ID_NULL ; } // se più di un tratto else if ( ccClass.size() > 1) { // verifico se parzialmente interna bool bIn = false ; bool bOther = false ; for ( int k = 0 ; k < int( ccClass.size()) ; ++ k) { if ( ccClass[k].nClass == CRVC_IN) bIn = true ; else bOther = true ; } // se parzialmente interna, la sposto negli aperti if ( bIn && bOther) { vOpenIds.push_back( vArea[j].first) ; vArea[j].first = GDB_ID_NULL ; } } } // verifico i contorni aperti che siano contenuti completamente o parzialmente for ( int j = i + 1 ; j < int( vOpenIds.size()) ; ++ j) { if ( vOpenIds[j] == GDB_ID_NULL) continue ; ICurve* pCrv2 = GetCurve( pGeomDB->GetGeoObj( vOpenIds[j])) ; Intervals inCrv ; double dParS, dParE ; pCrv2->GetDomain( dParS, dParE) ; inCrv.Set( dParS, dParE) ; // intersezione e classificazione CRVCVECTOR ccClass ; IntersCurveCurve intCC( *pCrv2, *pCrv) ; if ( ! intCC.GetCurveClassification( 0, ccClass)) continue ; // conservo solo le parti interne for ( int k = 0 ; k < int( ccClass.size()) ; ++ k) { if ( ccClass[k].nClass != CRVC_IN) inCrv.Subtract( ccClass[k].dParS, ccClass[k].dParE) ; } // tratti di curva rimasti int nNum = inCrv.GetCount() ; // se rimangono uno o più tratti if ( nNum >= 1) { double dParS, dParE ; bool bInt = inCrv.GetFirst( dParS, dParE) ; while ( bInt) { // copio la curva e la modifico int nCopyId = pGeomDB->Copy( vOpenIds[j], GDB_ID_NULL, nLayId) ; // modifico l'originale ICurve* pOriCrv = GetCurve( pGeomDB->GetGeoObj( nCopyId)) ; pOriCrv->TrimStartEndAtParam( dParS, dParE) ; // passo al successivo intervallo bInt = inCrv.GetNext( dParS, dParE) ; } } } } // elimino la geometria originale rimasta pGeomDB->Erase( nParentId) ; return true ; } //---------------------------------------------------------------------------- static bool CreateFlatPartsByLayers( void) { IGeomDB* pGeomDB = GetCurrGeomDB() ; VERIFY_GEOMDB( pGeomDB, false) // preparo gli iteratori PtrOwner pPar( CreateGdbIterator( pGeomDB)) ; PtrOwner pLay( CreateGdbIterator( pGeomDB)) ; if ( IsNull( pPar) || IsNull( pLay)) return false ; // ciclo su tutti i gruppi della radice (pezzi) bool bPok = pPar->GoToFirstGroupInGroup( GDB_ID_ROOT) ; int nFirstId = pPar->GetId() ; while ( bPok) { // ciclo su tutti i sottogruppi (layer) bool bLok = pLay->GoToFirstGroupInGroup( *Get( pPar)) ; while ( bLok) { // creo pezzo con questa geometria int nPartId = pGeomDB->InsertGroup( GDB_ID_NULL, nFirstId, GDB_BEFORE, Frame3d()) ; // passo al pezzo l'eventuale nome del layer string sName ; if ( pLay->GetName( sName)) { pGeomDB->SetName( nPartId, sName) ; pLay->RemoveName() ; } // sposto il layer sotto questo pezzo if ( ! pGeomDB->RelocateGlob( pLay->GetId(), nPartId)) return false ; // altro layer del gruppo originale bLok = pLay->GoToFirstGroupInGroup( *Get( pPar)) ; } // passo al successivo bPok = pPar->GoToNextGroup() ; } // cancello i gruppi svuotati bPok = pPar->GoTo( nFirstId) ; while ( bPok) bPok = pPar->EraseAndGoToNext() ; return true ; } //---------------------------------------------------------------------------- static bool VerifyAndAdjustFlatParts( void) { IGeomDB* pGeomDB = GetCurrGeomDB() ; VERIFY_GEOMDB( pGeomDB, false) // preparo gli iteratori PtrOwner pPar( CreateGdbIterator( pGeomDB)) ; PtrOwner pEnt( CreateGdbIterator( pGeomDB)) ; if ( IsNull( pPar) || IsNull( pEnt)) return false ; // ciclo sui pezzi bool bPok = pPar->GoToFirstGroupInGroup( GDB_ID_ROOT) ; while ( bPok) { // seleziono tutte le curve del pezzo bool bOk = ExeSelectPartObjs( pPar->GetId()) ; // creo un nuovo layer in cui mettere tutti i concatenati int nRegId = ExeCreateGroup( pPar->GetId(), Frame3d(), RTY_GLOB) ; bOk = bOk && nRegId != GDB_ID_NULL ; bOk = bOk && pGeomDB->SetName( nRegId, "Region") ; // concateno tutte le curve INTVECTOR vIds ; vIds.push_back( GDB_ID_SEL) ; int nCount ; int nFirstId = ( bOk ? ExeCreateCurveCompoByChain( nRegId, vIds, ORIG, false, RTY_GLOB, &nCount) : GDB_ID_NULL) ; bOk = bOk && nFirstId != GDB_ID_NULL ; // deseleziono tutto pGeomDB->ClearSelection() ; // elimino le curve aperte del nuovo layer e uso le chiuse per creare la regione INTVECTOR vOpenIds ; STRVECTOR vClosedIds ; SurfFlatRegionByContours SfrCntr( false, false) ; bool bEok = pEnt->GoToFirstInGroup( nRegId) ; while ( bEok) { ICurve* pCrv = GetCurve( pEnt->GetGeoObj()) ; if ( pCrv == nullptr) { bEok = pEnt->EraseAndGoToNext() ; } else if ( ! pCrv->IsClosed()) { // salvo Id di origine nell'elenco curve aperte da controllare INTVECTOR vTmpIds ; if ( pGeomDB->GetInfo( pEnt->GetId(), "ORIG", vTmpIds)) vOpenIds.insert( vOpenIds.end(), vTmpIds.begin(), vTmpIds.end()) ; bEok = pEnt->EraseAndGoToNext() ; } else { // salvo Id entità corrente e preparo passaggio a prossima int nEntId = pEnt->GetId() ; bEok = pEnt->GoToNext() ; // salvo stringa con elenco Id di origine string sInfo ; if ( pGeomDB->GetInfo( nEntId, "ORIG", sInfo)) { vClosedIds.emplace_back( sInfo) ; pCrv->SetTempProp( int( vClosedIds.size())) ; } // estraggo curva da entità che viene cancellata SfrCntr.AddCurve( pCrv) ; pGeomDB->RemoveGeoObjAndErase( nEntId) ; } } // creo la regione ISurfFlatRegion* pSfr = SfrCntr.GetSurf() ; bOk = bOk && ( pSfr != nullptr) ; // inserisco eventuali curve chiuse non usate nell'elenco delle aperte if ( ! SfrCntr.AllCurvesUsed()) { // recupero indice da proprietà temporanea di curve non usate INTVECTOR vUnId ; SfrCntr.GetUnusedCurveTempProps( vUnId) ; // recupero Id originali associati for ( auto nUnId : vUnId) { if ( nUnId > 0 && nUnId <= int( vClosedIds.size())) { INTVECTOR vTmpIds ; if ( FromString( vClosedIds[nUnId - 1], vTmpIds)) vOpenIds.insert( vOpenIds.end(), vTmpIds.begin(), vTmpIds.end()) ; } } } // inserisco la superficie nel DB int nNewId = ( bOk ? pGeomDB->AddGeoObj( GDB_ID_NULL, nRegId, pSfr) : GDB_ID_NULL) ; bOk = bOk && nNewId != GDB_ID_NULL ; // assegno il colore Color cCol = AQUA ; cCol.SetAlpha( 0.3) ; bOk = bOk && pGeomDB->SetMaterial( nNewId, cCol) ; // scrittura delle dimensioni nel layer della regione if ( bOk) { BBox3d b3Reg ; pGeomDB->GetGlobalBBox( nNewId, b3Reg) ; Point3d ptMin, ptMax ; b3Reg.GetMinMax( ptMin, ptMax) ; double dDimX = ptMax.x - ptMin.x ; double dDimY = ptMax.y - ptMin.y ; string sOut = ToString( ExeToUiUnits( dDimX), 2) + "x" + ToString( ExeToUiUnits( dDimY), 2) ; Point3d ptCen = Media( ptMin, ptMax, 0.5) ; Vector3d vtDir = ( dDimX >= dDimY ? X_AX : Y_AX) ; double dDimT = min( 0.05 * max( dDimX, dDimY), 0.5 * min( dDimX, dDimY)) ; PtrOwner pText( CreateExtText()) ; if ( ! IsNull( pText) && pText->Set( ptCen, Z_AX, vtDir, sOut, "", 100, false, dDimT, 1, 0, ETXT_IPMC)) { int nTextId = pGeomDB->AddGeoObj( GDB_ID_NULL, nRegId, Release( pText)) ; pGeomDB->SetMaterial( nTextId, BLACK) ; } } // taglio le curve aperte con la regione for ( int i = 0 ; bOk && i < int( vOpenIds.size()) ; ++i) { ICurve* pCrv = GetCurve( pGeomDB->GetGeoObj( vOpenIds[i])) ; // calcolo la classificazione della curva rispetto alla regione CRVCVECTOR ccClass ; if ( ! pSfr->GetCurveClassification( *pCrv, ccClass)) continue ; // conservo solo gli intervalli di curva interni Intervals inOk ; for ( auto& ccOne : ccClass) { if ( ccOne.nClass == CRVC_IN) inOk.Add( ccOne.dParE, ccOne.dParS) ; } // recupero un intervallo di id contigui per tutte le parti int nFirstId = pGeomDB->GetNewId() ; pGeomDB->ChangeId( vOpenIds[i], nFirstId) ; int nCurrId = nFirstId ; // eseguo la divisione nCount = 0 ; double dParS, dParE ; bool bFound = inOk.GetFirst( dParS, dParE) ; while ( bFound) { // copio la curva int nCopyId = pGeomDB->Copy( nCurrId, GDB_ID_NULL, nCurrId, GDB_AFTER) ; ICurve* pCopyCrv = GetCurve( pGeomDB->GetGeoObj( nCopyId)) ; if ( pCopyCrv == nullptr) break ; ++ nCount ; // modifico l'originale bOk = bOk && pCrv->TrimStartEndAtParam( dParS, dParE) ; // la copia diventa il nuovo corrente nCurrId = nCopyId ; pCrv = pCopyCrv ; // passo alla successiva divisione bFound = inOk.GetNext( dParS, dParE) ; } // cancello l'ultima copia (se non c'erano intervalli validi è l'originale) if ( nCurrId != GDB_ID_NULL) pGeomDB->Erase( nCurrId) ; } // se errore sul pezzo, lo elimino if ( ! bOk) bPok = pPar->EraseAndGoToNextGroup() ; // passo al pezzo successivo else bPok = pPar->GoToNextGroup() ; } return true ; } //---------------------------------------------------------------------------- bool ExeCreateFlatParts( int nType) { // disabilito registrazione comandi bool bOldCmdLog = SetCmdLog( false) ; // filtro e approssimo le curve bool bOk = FilterAndApprox() ; // creazione basata sulle regioni if ( nType == FPC_REGION) { int nLay ; // concateno bOk = bOk && ChainCurves( nLay) ; // costruisco i pezzi bOk = bOk && CreateFlatPartsByRegions( nLay) ; } // creazione basata sui layer else if ( nType == FPC_LAYER) { // creazione dei pezzi bOk = bOk && CreateFlatPartsByLayers() ; } // creazione basata sui pezzi NGE // sono già a posto // verifica e aggiustamento dei pezzi bOk = bOk && VerifyAndAdjustFlatParts() ; // ripristino stato registrazione comandi bOldCmdLog = SetCmdLog( bOldCmdLog) ; return bOk ; } //---------------------------------------------------------------------------- static bool VerifyBox( const BBox3d& b3Test, const Vector3d& vtMove, const BOXVECTOR& vb3OnPlace) { BBox3d b3Moved = b3Test ; b3Moved.Translate( vtMove) ; for ( const auto& Box : vb3OnPlace) { if ( b3Moved.OverlapsXY( Box)) return false ; } return true ; } //---------------------------------------------------------------------------- static bool VerifyBoxAndMoveOnX( const BBox3d& b3Test, const Vector3d& vtMove, const BOXVECTOR& vb3OnPlace, double dXmax, double& dExtraMove) { BBox3d b3Moved = b3Test ; b3Moved.Translate( vtMove) ; dExtraMove = 0 ; for ( const auto& Box : vb3OnPlace) { if ( b3Moved.OverlapsXY( Box)) { double dNewMove = Box.GetMax().x - b3Moved.GetMin().x + 2 * EPS_SMALL ; dExtraMove += dNewMove ; b3Moved.Translate( Vector3d( dNewMove, 0, 0)) ; if ( b3Moved.GetMax().x > dXmax + EPS_SMALL) return false ; } } return true ; } //---------------------------------------------------------------------------- bool ExePackPart( int nId, double dXmin, double dYmin, double dXmax, double dYmax, double dOffs, bool bBottomUp) { IGeomDB* pGeomDB = GetCurrGeomDB() ; VERIFY_GEOMDB( pGeomDB, false) // verifiche sui parametri if ( dOffs < - EPS_ZERO || dXmax < dXmin + 2 * dOffs + EPS_SMALL || dYmax < dYmin + 2 * dOffs + EPS_SMALL) return false ; // Determino il box del pezzo BBox3d b3Part ; if ( ! pGeomDB->GetGlobalBBox( nId, b3Part, BBF_ONLY_VISIBLE | BBF_IGNORE_TEXT | BBF_IGNORE_DIM)) return false ; double dLarPz = b3Part.GetMax().x - b3Part.GetMin().x ; Point3d ptPart = ( bBottomUp ? b3Part.GetMin() : Point3d( b3Part.GetMin().x, b3Part.GetMax().y, b3Part.GetMin().z)) ; b3Part.Expand( dOffs - 2 * EPS_SMALL, dOffs - 2 * EPS_SMALL, 0) ; // Box della regione di interesse BBox3d b3Region( dXmin, dYmin, 0, dXmax, dYmax, 0) ; // Verifico se pezzo sotto la radice o pezzo in altro gruppo int nGroupId = pGeomDB->GetParentId( nId) ; bool bInRoot = ( nGroupId == GDB_ID_ROOT) ; // Determino il box di tutti gli altri pezzi compresi nella regione di interesse BOXVECTOR vBox ; int nId2 = ( bInRoot ? ExeGetFirstPart( true) : ExeGetFirstGroupInGroup( nGroupId)) ; while ( nId2 != GDB_ID_NULL) { if ( nId2 != nId) { BBox3d b3Part2 ; if ( pGeomDB->GetGlobalBBox( nId2, b3Part2, BBF_ONLY_VISIBLE | BBF_IGNORE_TEXT | BBF_IGNORE_DIM) && b3Region.OverlapsXY( b3Part2)) vBox.emplace_back( b3Part2) ; } nId2 = ( bInRoot ? ExeGetNextPart( nId2, true) : ExeGetNextGroup( nId2)) ; } // Ordino i box dei pezzi secondo la X crescente sort( vBox.begin(), vBox.end(), []( const BBox3d& a, const BBox3d& b) { return a.GetMin().x < b.GetMin().x ; }) ; // Determino le Y interessanti DBLVECTOR vY ; if ( bBottomUp) { // Ymin del rettangolo e Ymax dei vari box vY.push_back( dYmin) ; for ( auto& Box : vBox) { double dY = Box.GetMax().y + dOffs ; if ( dY > dYmin && dY < dYmax) vY.push_back( dY) ; } // Le ordino in senso crescente sort( vY.begin(), vY.end()) ; } else { // Ymax del rettangolo e Ymin dei vari box vY.push_back( dYmax) ; for ( auto& Box : vBox) { double dY = Box.GetMin().y - dOffs ; if ( dY > dYmin && dY < dYmax) vY.push_back( dY) ; } // Le ordino in senso decrescente sort( vY.begin(), vY.end(), greater()) ; } // Elimino i valori coincidenti vY.erase( unique( vY.begin(), vY.end(), [](double a, double b){ return fabs( a - b) < EPS_SMALL ;}), vY.end()) ; // Testo le Y in ordine opportuno double dXok = INFINITO, dYok = INFINITO ; for ( auto dY : vY) { double dExtraMove ; if ( VerifyBoxAndMoveOnX( b3Part, Point3d( dXmin, dY, 0) - ptPart, vBox, dXmax + dOffs, dExtraMove)) { dXok = dXmin + dExtraMove ; dYok = dY ; break ; } } // Verifico che il pezzo nella nuova posizione non esca da Ymax o Y min if ( bBottomUp && b3Part.GetMax().y - ptPart.y + dYok > dYmax + dOffs + EPS_SMALL) return false ; else if ( ! bBottomUp && b3Part.GetMin().y - ptPart.y + dYok < dYmin - dOffs - EPS_SMALL) return false ; // Porto il pezzo nella giusta posizione if ( ! pGeomDB->TranslateGlob( nId, Point3d( dXok, dYok, ptPart.z) - ptPart)) return false ; ExeSetModified() ; return true ; } //---------------------------------------------------------------------------- bool ExePackCluster( const INTVECTOR& vIds, double dXmin, double dYmin, double dXmax, double dYmax, double dOffs, bool bBottomUp) { IGeomDB* pGeomDB = GetCurrGeomDB() ; VERIFY_GEOMDB( pGeomDB, false) // verifiche sui parametri if ( dOffs < - EPS_ZERO || dXmax < dXmin + 2 * dOffs + EPS_SMALL || dYmax < dYmin + 2 * dOffs + EPS_SMALL) return false ; // Risolvo eventuali riferimenti a oggetti selezionati INTVECTOR vTrueIds ; vTrueIds.reserve( vIds.size()) ; for ( auto nId : vIds) { int nTrueId = (( nId != GDB_ID_SEL) ? nId : pGeomDB->GetFirstSelectedObj()) ; while ( nTrueId != GDB_ID_NULL) { vTrueIds.push_back( nTrueId) ; // passo al successivo nTrueId = (( nId != GDB_ID_SEL) ? GDB_ID_NULL : pGeomDB->GetNextSelectedObj()) ; } } // Se non sono rimasti oggetti, esco con successo if ( vTrueIds.empty()) return true ; // Flag per calcolo box const int BBF_MY_FLAG = BBF_ONLY_VISIBLE | BBF_IGNORE_TEXT | BBF_IGNORE_DIM ; // Determino il box del cluster (insieme di pezzi) BBox3d b3Cluster ; for ( auto nTrueId : vTrueIds) { BBox3d b3Part ; if ( ! pGeomDB->GetGlobalBBox( nTrueId, b3Part, BBF_MY_FLAG)) return false ; b3Cluster.Add( b3Part) ; } double dLarCl = b3Cluster.GetMax().x - b3Cluster.GetMin().x ; Point3d ptCluster = ( bBottomUp ? b3Cluster.GetMin() : Point3d( b3Cluster.GetMin().x, b3Cluster.GetMax().y, b3Cluster.GetMin().z)) ; b3Cluster.Expand( dOffs - 2 * EPS_SMALL, dOffs - 2 * EPS_SMALL, 0) ; // Box della regione di interesse BBox3d b3Region( dXmin, dYmin, 0, dXmax, dYmax, 0) ; // Verifico se pezzi sotto la radice o pezzi in altro gruppo int nGroupId = pGeomDB->GetParentId( vTrueIds[0]) ; bool bInRoot = ( nGroupId == GDB_ID_ROOT) ; // Determino il box di tutti gli altri pezzi compresi nella regione di interesse BOXVECTOR vBox ; int nId2 = ( bInRoot ? ExeGetFirstPart( true) : ExeGetFirstGroupInGroup( nGroupId)) ; while ( nId2 != GDB_ID_NULL) { if ( find( vTrueIds.begin(), vTrueIds.end(), nId2) == vTrueIds.end()) { BBox3d b3Part2 ; if ( pGeomDB->GetGlobalBBox( nId2, b3Part2, BBF_MY_FLAG) && b3Region.OverlapsXY( b3Part2)) vBox.emplace_back( b3Part2) ; } nId2 = ( bInRoot ? ExeGetNextPart( nId2, true) : ExeGetNextGroup( nId2)) ; } // Ordino i box dei pezzi secondo la X crescente sort( vBox.begin(), vBox.end(), []( const BBox3d& a, const BBox3d& b) { return a.GetMin().x < b.GetMin().x ; }) ; // Determino le Y interessanti DBLVECTOR vY ; if ( bBottomUp) { // Ymin del rettangolo e Ymax dei vari box vY.push_back( dYmin) ; for ( auto& Box : vBox) { double dY = Box.GetMax().y + dOffs ; if ( dY > dYmin && dY < dYmax) vY.push_back( dY) ; } // Le ordino in senso crescente sort( vY.begin(), vY.end()) ; } else { // Ymax del rettangolo e Ymin dei vari box vY.push_back( dYmax) ; for ( auto& Box : vBox) { double dY = Box.GetMin().y - dOffs ; if ( dY > dYmin && dY < dYmax) vY.push_back( dY) ; } // Le ordino in senso decrescente sort( vY.begin(), vY.end(), greater()) ; } // Elimino i valori coincidenti vY.erase( unique( vY.begin(), vY.end(), [](double a, double b){ return fabs( a - b) < EPS_SMALL ;}), vY.end()) ; // Testo le Y in ordine opportuno double dXok = INFINITO, dYok = INFINITO ; for ( auto dY : vY) { double dExtraMove ; if ( VerifyBoxAndMoveOnX( b3Cluster, Point3d( dXmin, dY, 0) - ptCluster, vBox, dXmax + dOffs, dExtraMove)) { dXok = dXmin + dExtraMove ; dYok = dY ; break ; } } // Verifico che il cluster nella nuova posizione non esca da Ymax if ( bBottomUp && b3Cluster.GetMax().y - ptCluster.y + dYok > dYmax + dOffs + EPS_SMALL) return false ; else if ( ! bBottomUp && b3Cluster.GetMin().y - ptCluster.y + dYok < dYmin - dOffs - EPS_SMALL) return false ; // Porto il cluster nella giusta posizione for ( auto nTrueId : vTrueIds) { pGeomDB->TranslateGlob( nTrueId, Point3d( dXok, dYok, ptCluster.z) - ptCluster) ; } ExeSetModified() ; return true ; } //----------------------------------------------------------------------------- bool ExeGetClusterBBoxGlob( const INTVECTOR& vIds, BBox3d& b3Box) { IGeomDB* pGeomDB = GetCurrGeomDB() ; VERIFY_GEOMDB( pGeomDB, false) // Flag per calcolo box const int BBF_MY_FLAG = BBF_ONLY_VISIBLE | BBF_IGNORE_TEXT | BBF_IGNORE_DIM ; // Determino il box del cluster (insieme di pezzi) b3Box.Reset() ; for ( auto nId : vIds) { int nTrueId = (( nId != GDB_ID_SEL) ? nId : pGeomDB->GetFirstSelectedObj()) ; while ( nTrueId != GDB_ID_NULL) { BBox3d b3Part ; if ( ! pGeomDB->GetGlobalBBox( nTrueId, b3Part, BBF_MY_FLAG)) return false ; b3Box.Add( b3Part) ; // passo al successivo nTrueId = (( nId != GDB_ID_SEL) ? GDB_ID_NULL : pGeomDB->GetNextSelectedObj()) ; } } return true ; } //---------------------------------------------------------------------------- static void VerifyBoxMoveInBox( const BBox3d& b3Region, const BBox3d& b3Box, Vector3d& vtMoveXY) { BBox3d b3Moved = b3Box ; b3Moved.Translate( vtMoveXY) ; if ( vtMoveXY.x < - EPS_SMALL) { double dDeltaX = b3Moved.GetMin().x - b3Region.GetMin().x ; if ( dDeltaX < - EPS_SMALL) { double dDeltaY = dDeltaX * vtMoveXY.y / vtMoveXY.x ; Vector3d vtCorr( - dDeltaX, - dDeltaY, 0) ; vtMoveXY += vtCorr ; b3Moved.Translate( vtCorr) ; } } if ( vtMoveXY.y < - EPS_SMALL) { double dDeltaY = b3Moved.GetMin().y - b3Region.GetMin().y ; if ( dDeltaY < - EPS_SMALL) { double dDeltaX = dDeltaY * vtMoveXY.x / vtMoveXY.y ; Vector3d vtCorr( - dDeltaX, - dDeltaY, 0) ; vtMoveXY += vtCorr ; b3Moved.Translate( vtCorr) ; } } if ( vtMoveXY.x > EPS_SMALL) { double dDeltaX = b3Moved.GetMax().x - b3Region.GetMax().x ; if ( dDeltaX > EPS_SMALL) { double dDeltaY = dDeltaX * vtMoveXY.y / vtMoveXY.x ; Vector3d vtCorr( - dDeltaX, - dDeltaY, 0) ; vtMoveXY += vtCorr ; b3Moved.Translate( vtCorr) ; } } if ( vtMoveXY.y > EPS_SMALL) { double dDeltaY = b3Moved.GetMax().y - b3Region.GetMax().y ; if ( dDeltaY > EPS_SMALL) { double dDeltaX = dDeltaY * vtMoveXY.x / vtMoveXY.y ; Vector3d vtCorr( - dDeltaX, - dDeltaY, 0) ; vtMoveXY += vtCorr ; b3Moved.Translate( vtCorr) ; } } } //---------------------------------------------------------------------------- static void VerifyBoxMoveOutBox( const BBox3d& b3Other, const BBox3d& b3Box, Vector3d& vtMoveXY) { // box spostato BBox3d b3Moved = b3Box ; b3Moved.Translate( vtMoveXY) ; // verifico se collidono in modo significativo BBox3d b3Int ; if ( ! b3Other.FindIntersectionXY( b3Moved, b3Int) || b3Int.IsEpsilonXY( 2 * EPS_SMALL)) return ; // calcolo le possibili riduzioni di movimento Vector3d vtCorr[4] ; vtCorr[0] = - vtMoveXY ; if ( vtMoveXY.x < - EPS_SMALL) { double dDeltaX = b3Moved.GetMin().x - b3Other.GetMax().x ; if ( dDeltaX < EPS_SMALL) { double dDeltaY = dDeltaX * vtMoveXY.y / vtMoveXY.x ; vtCorr[0].Set( - dDeltaX, - dDeltaY, 0) ; } } vtCorr[1] = - vtMoveXY ; if ( vtMoveXY.y < - EPS_SMALL) { double dDeltaY = b3Moved.GetMin().y - b3Other.GetMax().y ; if ( dDeltaY < EPS_SMALL) { double dDeltaX = dDeltaY * vtMoveXY.x / vtMoveXY.y ; vtCorr[1].Set( - dDeltaX, - dDeltaY, 0) ; } } vtCorr[2] = - vtMoveXY ; if ( vtMoveXY.x > EPS_SMALL) { double dDeltaX = b3Moved.GetMax().x - b3Other.GetMin().x ; if ( dDeltaX > - EPS_SMALL) { double dDeltaY = dDeltaX * vtMoveXY.y / vtMoveXY.x ; vtCorr[2].Set( - dDeltaX, - dDeltaY, 0) ; } } vtCorr[3] = - vtMoveXY ; if ( vtMoveXY.y > EPS_SMALL) { double dDeltaY = b3Moved.GetMax().y - b3Other.GetMin().y ; if ( dDeltaY > - EPS_SMALL) { double dDeltaX = dDeltaY * vtMoveXY.x / vtMoveXY.y ; vtCorr[3].Set( - dDeltaX, - dDeltaY, 0) ; } } // prendo la riduzione più piccola in modulo Vector3d vtMinCorr = vtCorr[0] ; for ( int i = 1 ; i < 4 ; ++ i) { if ( vtCorr[i].SqLenXY() < vtMinCorr.SqLenXY()) vtMinCorr = vtCorr[i] ; } vtMoveXY += vtMinCorr ; } //---------------------------------------------------------------------------- bool ExeMoveCluster( const INTVECTOR& vIds, Vector3d& vtMove, double dXmin, double dYmin, double dXmax, double dYmax, double dOffs) { IGeomDB* pGeomDB = GetCurrGeomDB() ; VERIFY_GEOMDB( pGeomDB, false) // Verifiche sui parametri if ( dOffs < - EPS_ZERO || dXmax < dXmin + 2 * dOffs + EPS_SMALL || dYmax < dYmin + 2 * dOffs + EPS_SMALL) return false ; // Vettore movimento nel solo piano XY Vector3d vtMoveXY( vtMove.x, vtMove.y, 0) ; vtMove = V_NULL ; if ( vtMoveXY.IsSmall()) return true ; // Box della regione di interesse BBox3d b3Region( dXmin, dYmin, 0, dXmax, dYmax, 0) ; // Flag per calcolo box const int BBF_MY_FLAG = BBF_ONLY_VISIBLE | BBF_IGNORE_TEXT | BBF_IGNORE_DIM ; // Risolvo eventuali riferimenti a oggetti selezionati, escludo oggetti non completamente nella regione e // calcolo il box del cluster risultante INTVECTOR vTrueIds ; vTrueIds.reserve( vIds.size()) ; BBox3d b3Cluster ; for ( auto nId : vIds) { int nTrueId = (( nId != GDB_ID_SEL) ? nId : pGeomDB->GetFirstSelectedObj()) ; while ( nTrueId != GDB_ID_NULL) { BBox3d b3Part ; if ( pGeomDB->GetGlobalBBox( nTrueId, b3Part, BBF_MY_FLAG) && b3Region.EnclosesXY( b3Part)) { // inserisco l'identificativo nel vettore dei validi vTrueIds.push_back( nTrueId) ; // aggiorno il box del cluster b3Cluster.Add( b3Part) ; } // passo al successivo nTrueId = (( nId != GDB_ID_SEL) ? GDB_ID_NULL : pGeomDB->GetNextSelectedObj()) ; } } // Se non sono rimasti oggetti, esco con successo if ( vTrueIds.empty()) return true ; // Verifico se pezzi sotto la radice o pezzi in altro gruppo int nGroupId = pGeomDB->GetParentId( vTrueIds[0]) ; bool bInRoot = ( nGroupId == GDB_ID_ROOT) ; // Determino i box di tutti gli altri pezzi compresi nella regione di interesse BOXVECTOR vBox ; int nId2 = ( bInRoot ? ExeGetFirstPart( true) : ExeGetFirstGroupInGroup( nGroupId)) ; while ( nId2 != GDB_ID_NULL) { if ( find( vTrueIds.begin(), vTrueIds.end(), nId2) == vTrueIds.end()) { BBox3d b3Part2 ; if ( pGeomDB->GetGlobalBBox( nId2, b3Part2, BBF_MY_FLAG) && b3Region.OverlapsXY( b3Part2)) vBox.emplace_back( b3Part2) ; } nId2 = ( bInRoot ? ExeGetNextPart( nId2, true) : ExeGetNextGroup( nId2)) ; } // Ordino questi box in senso contrario al movimento if ( fabs( vtMoveXY.x) >= fabs( vtMoveXY.y)) { if ( vtMoveXY.x > 0) // movimento prevalente in X+ -> ordine secondo X decrescente sort( vBox.begin(), vBox.end(), []( const BBox3d& a, const BBox3d& b) { return a.GetMin().x > b.GetMin().x ; }) ; else // movimento prevalente in X- -> ordine secondo X crescente sort( vBox.begin(), vBox.end(), []( const BBox3d& a, const BBox3d& b) { return a.GetMin().x < b.GetMin().x ; }) ; } else { if ( vtMoveXY.y > 0) // movimento prevalente in Y+ -> ordine secondo Y decrescente sort( vBox.begin(), vBox.end(), []( const BBox3d& a, const BBox3d& b) { return a.GetMin().y > b.GetMin().y ; }) ; else // movimento prevalente in Y- -> ordine secondo Y crescente sort( vBox.begin(), vBox.end(), []( const BBox3d& a, const BBox3d& b) { return a.GetMin().y < b.GetMin().y ; }) ; } // Verifico movimento del box del cluster rispetto alla regione VerifyBoxMoveInBox( b3Region, b3Cluster, vtMoveXY) ; // Verifico movimento dei pezzi del cluster rispetto agli altri pezzi for ( auto nTrueId : vTrueIds) { // recupero box del pezzo, lo espando della minima distanza e verifico il mosso BBox3d b3Part ; pGeomDB->GetGlobalBBox( nTrueId, b3Part, BBF_MY_FLAG) ; b3Part.Expand( dOffs, dOffs, 0) ; // lo confronto con il box degli altri pezzi e se necessario riduco il movimento for ( const auto& Box : vBox) { VerifyBoxMoveOutBox( Box, b3Part, vtMoveXY) ; } } // Se movimento risultante nullo, non faccio alcunché if ( vtMoveXY.IsSmall()) return true ; // Eseguo movimento dei pezzi del cluster for ( auto nTrueId : vTrueIds) pGeomDB->TranslateGlob( nTrueId, vtMoveXY) ; ExeSetModified() ; vtMove = vtMoveXY ; return true ; }