//---------------------------------------------------------------------------- // EgalTech 2015-2015 //---------------------------------------------------------------------------- // File : StmFromCurves.cpp Data : 01.02.15 Versione : 1.6b1 // Contenuto : Implementazione di funzioni per creazione di superfici Stm // a partire da curve, con diversi metodi. // // // Modifiche : 01.02.15 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "GeoConst.h" #include "CurveLine.h" #include "CurveArc.h" #include "CurveComposite.h" #include "SurfTriMesh.h" #include "Voronoi.h" #include "IntersLineLine.h" #include "/EgtDev/Include/EGkSfrCreate.h" #include "/EgtDev/Include/EGkOffsetCurve.h" #include "/EgtDev/Include/EGkStmFromCurves.h" #include "/EgtDev/Include/EGkStmFromTriangleSoup.h" #include "/EgtDev/Include/EGkRotationMinimizingFrame.h" #include "/EgtDev/Include/EGkRotationXplaneFrame.h" #include "/EgtDev/Include/EGkIntersCurves.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include using namespace std ; //------------------------------------------------------------------------------- // costanti per SurfTmRectSwept static const int STM_RECTSWEPT_FLATCAP_START = -1 ; static const int STM_RECTSWEPT_FLATCAP_END = -2 ; static const int STM_RECTSWEPT_LINK = -3 ; static const int STM_RECTSWEPT_FLATCAP_EXTRA = -4 ; //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshByFlatContour( const ICurve* pCurve, double dLinTol) { // verifica parametri if ( pCurve == nullptr) return nullptr ; // calcolo la polilinea che approssima la curva PolyLine PL ; if ( ! pCurve->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL)) return nullptr ; // creo e setto la superficie trimesh PtrOwner pSTM( CreateBasicSurfTriMesh()) ; if ( IsNull( pSTM) || ! pSTM->CreateByFlatContour( PL)) return nullptr ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshByRegion( const CICURVEPVECTOR& vpCurve, double dLinTol) { // verifica parametri if ( &vpCurve == nullptr || vpCurve.empty()) return nullptr ; // calcolo le polilinee che approssimano le curve della regione POLYLINEVECTOR vPL ; vPL.resize( vpCurve.size()) ; for ( int i = 0 ; i < int( vpCurve.size()) ; ++ i) { if ( ! vpCurve[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL[i])) return nullptr ; } Vector3d vtN ; INTMATRIX vnPLIndMat ; BOOLVECTOR vbInvert ; if ( ! CalcRegionPolyLines( vPL, vtN, vnPLIndMat, vbInvert)) return nullptr ; for ( int i = 0 ; i < int( vnPLIndMat.size()) ; ++i) { for ( int j = 0 ; j < int( vnPLIndMat[i].size()) ; ++j){ if ( vbInvert[vnPLIndMat[i][j]]) vPL[vnPLIndMat[i][j]].Invert() ; } } // creo e setto la superficie trimesh PtrOwner pSTM( CreateBasicSurfTriMesh()) ; if ( IsNull( pSTM) || ! pSTM->CreateByRegion( vPL, vnPLIndMat)) return nullptr ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshByExtrusion( const ICurve* pCurve, const Vector3d& vtExtr, bool bCapEnds, double dLinTol) { // verifica parametri if ( pCurve == nullptr || &vtExtr == nullptr) return nullptr ; // calcolo la polilinea che approssima la curva PolyLine PL ; if ( ! pCurve->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL)) return nullptr ; // se richiesta chiusura agli estremi bool bDoCapEnds = false ; if ( bCapEnds) { // verifico che la curva sia chiusa e piatta Plane3d plPlane ; double dArea ; if ( PL.IsClosedAndFlat( plPlane, dArea, 50 * EPS_SMALL)) { // componente dell'estrusione perpendicolare al piano della curva double dOrthoExtr = plPlane.GetVersN() * vtExtr ; if ( ( abs( dOrthoExtr) > EPS_SMALL)) { bDoCapEnds = true ; // se negativa, inverto il senso del contorno if ( dOrthoExtr < 0) PL.Invert() ; } } } // creo e setto la superficie trimesh PtrOwner pSTM( CreateBasicSurfTriMesh()) ; if ( IsNull( pSTM) || ! pSTM->CreateByExtrusion( PL, vtExtr)) return nullptr ; // se da fare, metto i tappi sulle estremità if ( bDoCapEnds) { // creo la prima superficie di estremità SurfTriMesh STM1 ; if ( ! STM1.CreateByFlatContour( PL)) return nullptr ; // la copio SurfTriMesh STM2 = STM1 ; // inverto la prima superficie STM1.Invert() ; // traslo la seconda STM2.Translate( vtExtr) ; // le unisco alla superficie del fianco if ( ! pSTM->DoSewing( STM1) || ! pSTM->DoSewing( STM2)) return nullptr ; } // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshByRegionExtrusion( const CICURVEPVECTOR& vpCurve, const Vector3d& vtExtr, double dLinTol) { // verifica parametri if ( &vpCurve == nullptr || vpCurve.empty() || &vtExtr == nullptr) return nullptr ; // se una sola curva, uso la funzione precedente if ( vpCurve.size() == 1 ) return GetSurfTriMeshByExtrusion( vpCurve[0], vtExtr, true, dLinTol) ; // calcolo le polilinee che approssimano le curve della regione POLYLINEVECTOR vPL ; vPL.resize( vpCurve.size()) ; for ( int i = 0 ; i < int( vpCurve.size()) ; ++ i) { if ( ! vpCurve[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL[i])) return nullptr ; } Vector3d vtN ; INTMATRIX vnPLIndMat ; BOOLVECTOR vbInvert ; if ( ! CalcRegionPolyLines( vPL, vtN, vnPLIndMat, vbInvert)) return nullptr ; for ( int i = 0 ; i < int( vPL.size()) ; ++i) { if ( vbInvert[i]) vPL[i].Invert() ; } // verifico la direzione di estrusione double dOrthoExtr = vtN * vtExtr ; if ( ( abs( dOrthoExtr) < EPS_SMALL)) return nullptr ; // se componente estrusione negativa, inverto tutti i percorsi if ( dOrthoExtr < 0) { for ( int i = 0 ; i < int( vPL.size()) ; ++ i) vPL[i].Invert() ; } // creo la prima superficie di estremità PtrOwner pSTM( CreateBasicSurfTriMesh()) ; // alla funzione CreateByRegion passo anche la matrice che contiene la struttura dei chunk. Le polyline hanno già il verso giusto if ( IsNull( pSTM) || ! pSTM->CreateByRegion( vPL, vnPLIndMat)) return nullptr ; // creo la seconda superficie e la unisco alla prima { // copio la prima superficie SurfTriMesh STM2 = *pSTM ; // inverto la prima superficie pSTM->Invert() ; // traslo la seconda STM2.Translate( vtExtr) ; // la unisco alla prima if ( ! pSTM->DoSewing( STM2)) return nullptr ; } // creo e unisco le diverse superfici di estrusione for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { // estrusione SurfTriMesh STM2 ; if ( ! STM2.CreateByExtrusion( vPL[i], vtExtr)) return nullptr ; // la unisco alla superficie principale if ( ! pSTM->DoSewing( STM2)) return nullptr ; } // compatto la superficie if ( ! pSTM->DoCompacting()) return nullptr ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshByRevolve( const ICurve* pCurve, const Point3d& ptAx, const Vector3d& vtAx, bool bCapEnds, double dLinTol) { // verifica parametri if ( pCurve == nullptr || &ptAx == nullptr || &vtAx == nullptr) return nullptr ; // limite minimo su tolleranza dLinTol = max( dLinTol, EPS_SMALL) ; // calcolo la polilinea che approssima la curva PolyLine PL ; if ( ! pCurve->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL)) return nullptr ; // calcolo lo step di rotazione double dMaxRad = 0 ; if ( ! PL.GetMaxDistanceFromLine( ptAx, vtAx, 1, dMaxRad, false) || dMaxRad < EPS_SMALL) return nullptr ; double dStepRotDeg = sqrt( 8 * dLinTol / dMaxRad) * RADTODEG ; // se richiesta chiusura degli estremi if ( bCapEnds && ! PL.IsClosed()) { Vector3d vtAxN = vtAx ; vtAxN.Normalize() ; double dPosIni = 0, dPosFin = 0 ; Point3d ptP ; // proietto l'ultimo punto sull'asse di rotazione if ( PL.GetLastPoint( ptP)) { dPosFin = ( ptP - ptAx) * vtAxN ; Point3d ptPOnAx = ptAx + dPosFin * vtAxN ; // se non giace sull'asse, aggiungo il punto proiettato if ( ! AreSamePointApprox( ptP, ptPOnAx)) { double dU ; PL.GetLastU( dU) ; PL.AddUPoint( ( dU + 1), ptPOnAx) ; } } // inverto la polilinea PL.Invert() ; // proietto l'ultimo punto (era il primo) sull'asse di rotazione if ( PL.GetLastPoint( ptP)) { dPosIni = ( ptP - ptAx) * vtAxN ; Point3d ptPOnAx = ptAx + dPosIni * vtAxN ; // se non giace sull'asse, aggiungo il punto proiettato if ( ! AreSamePointApprox( ptP, ptPOnAx)) { double dU ; PL.GetLastU( dU) ; PL.AddUPoint( ( dU + 1), ptPOnAx) ; } } // decido se reinvertire la polilinea if ( dPosFin > dPosIni) PL.Invert() ; } // creo e setto la superficie trimesh PtrOwner pSTM( CreateBasicSurfTriMesh()) ; if ( IsNull( pSTM) || ! pSTM->CreateByScrewing( PL, ptAx, vtAx, ANG_FULL, dStepRotDeg, 0)) return nullptr ; // se superficie risultante chiusa, verifico che la normale sia verso l'esterno double dVol ; if ( pSTM->GetVolume( dVol) && dVol < 0) pSTM->Invert() ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshByScrewing( const ICurve* pCurve, const Point3d& ptAx, const Vector3d& vtAx, double dAngRotDeg, double dMove, bool bCapEnds, double dLinTol) { // verifica parametri if ( pCurve == nullptr || &ptAx == nullptr || &vtAx == nullptr) return nullptr ; // limite minimo su tolleranza dLinTol = max( dLinTol, EPS_SMALL) ; // calcolo la polilinea che approssima la curva PolyLine PL ; if ( ! pCurve->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL)) return nullptr ; // calcolo lo step di rotazione double dMaxRad = 0 ; if ( ! PL.GetMaxDistanceFromLine( ptAx, vtAx, 1, dMaxRad, false) || dMaxRad < EPS_SMALL) return nullptr ; double dStepRotDeg = sqrt( 8 * dLinTol / dMaxRad) * RADTODEG ; // se superficie rototraslata, necessari limiti sulla lunghezza dei segmenti if ( abs( dAngRotDeg) > EPS_ANG_SMALL && abs( dMove) > EPS_SMALL){ double dLenMax = 2.5 * abs( dMove * dStepRotDeg / dAngRotDeg) ; if ( ! PL.AdjustForMaxSegmentLen( dLenMax)) return nullptr ; } // creo e setto la superficie trimesh PtrOwner pSTM( CreateBasicSurfTriMesh()) ; if ( IsNull( pSTM) || ! pSTM->CreateByScrewing( PL, ptAx, vtAx, dAngRotDeg, dStepRotDeg, dMove)) return nullptr ; // se richiesti caps if ( bCapEnds) { // determino se la sezione è chiusa e piatta Plane3d plPlane ; double dArea ; bool bSectClosedFlat = PL.IsClosedAndFlat( plPlane, dArea, 10 * EPS_SMALL) ; // determino non sia una semplice rivoluzione bool bRevolved = ( abs( abs( dAngRotDeg) - ANG_FULL) < EPS_ANG_SMALL && abs( dMove) < EPS_SMALL) ; // se sezione chiusa e piatta e non rivoluzione, posso aggiungere i tappi if ( bSectClosedFlat && ! bRevolved) { // aggiungo il cap sull'inizio PtrOwner pSci( CreateBasicSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByFlatContour( PL)) return nullptr ; pSTM->DoSewing( *pSci) ; // aggiungo il cap sulla fine Vector3d vtMove = vtAx ; vtMove.Normalize() ; vtMove *= dMove ; PL.Translate( vtMove) ; PL.Rotate( ptAx, vtAx, dAngRotDeg) ; PtrOwner pSce( CreateBasicSurfTriMesh()) ; if ( IsNull( pSce) || ! pSce->CreateByFlatContour( PL)) return nullptr ; pSce->Invert() ; pSTM->DoSewing( *pSce) ; } } // se superficie risultante chiusa, verifico che la normale sia verso l'esterno double dVol ; if ( pSTM->GetVolume( dVol) && dVol < 0) pSTM->Invert() ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- static bool RemoveFatCurveJunctions( ICURVEPOVECTOR& vCrvs) { ICURVEPOVECTOR vResCurves ; for ( int i = 0 ; i < ssize( vCrvs) ; i ++) { int nStart = ssize( vResCurves) ; CurveComposite* pFatCrv = GetBasicCurveComposite( vCrvs[i]) ; if ( pFatCrv == nullptr) return false ; PtrOwner pCrv( CreateBasicCurveComposite()) ; if ( IsNull( pCrv)) return false ; PtrOwner pCurrCrv( pFatCrv->RemoveFirstOrLastCurve( false)) ; while ( ! IsNull( pCurrCrv)) { if ( abs( pCurrCrv->GetTempParam() - VRONI_JUNCTION_OPEN) < EPS_SMALL) { if ( pCrv->IsValid()) { // salvo la catena trovata fino ad ora e la resetto vResCurves.emplace_back( Release( pCrv)) ; pCrv.Set( CreateBasicCurveComposite()) ; if ( IsNull( pCrv)) return false ; } } else { // aggiungo la curva alla catena corrente pCrv->AddCurve( Release( pCurrCrv)) ; } pCurrCrv.Set( pFatCrv->RemoveFirstOrLastCurve( false)) ; } if ( pCrv->IsValid()) { if ( ssize( vResCurves) != nStart) { // verifico se da concatenare alla prima Point3d ptEnd ; pCrv->GetEndPoint( ptEnd) ; Point3d ptStart ; vResCurves[nStart]->GetStartPoint( ptStart) ; if ( AreSamePointApprox( ptStart, ptEnd)) { ICurveComposite* pCompo = GetBasicCurveComposite( vResCurves[nStart]) ; if ( pCompo == nullptr) return false ; pCompo->AddCurve( Release( pCrv), false) ; } else vResCurves.emplace_back( Release( pCrv)) ; } else vResCurves.emplace_back( Release( pCrv)) ; } } swap( vResCurves, vCrvs) ; return true ; } //------------------------------------------------------------------------------- static ISurfFlatRegion* CalcSweptSurface( ICurveComposite* pGuide, const Vector3d& vtNorm, double dOffs) { PtrOwner pSrf( CreateSurfFlatRegion()) ; if ( IsNull( pSrf)) return nullptr ; // per ogni sottotratto della guida calcolo la regione spazzata dalla sezione su quel tratto for ( int i = 0 ; i < pGuide->GetCurveCount() ; i ++) { PtrOwner pCrv( pGuide->GetCurve( i)->Clone()) ; pCrv->SetExtrusion( vtNorm) ; if ( pCrv->SimpleOffset( - dOffs)) { PtrOwner pBorder( CreateCurveComposite()) ; if ( IsNull( pBorder)) return nullptr ; pBorder->AddCurve( pGuide->GetCurve( i)->Clone()) ; pCrv->Invert() ; Point3d ptS ; pCrv->GetStartPoint( ptS) ; pBorder->AddLine( ptS) ; pBorder->AddCurve( Release( pCrv)) ; pBorder->Close() ; if ( ! pSrf->IsValid()) { pSrf->AddExtLoop( Release( pBorder)) ; } else { PtrOwner pSrfTmp( CreateSurfFlatRegion()) ; if ( IsNull( pSrfTmp)) return nullptr ; pSrfTmp->AddExtLoop( Release( pBorder)) ; pSrf->Add( *pSrfTmp) ; } } else { // se arco che collassa devo costruire separatamente i due spicchi ICurveArc* pArc = GetCurveArc( pCrv) ; if ( pArc == nullptr) return nullptr ; Point3d ptC = pArc->GetCenter() ; Point3d ptS ; pArc->GetStartPoint( ptS) ; Vector3d vtS = ptC - ptS ; vtS.Normalize() ; Point3d ptS2 = ptS + vtS * dOffs ; Point3d ptE ; pArc->GetEndPoint( ptE) ; Vector3d vtE = ptC - ptE ; vtE.Normalize() ; Point3d ptE2 = ptE + vtE * dOffs ; PtrOwner pArc2( CreateCurveArc()) ; if ( IsNull( pArc2) || ! pArc2->SetC2PN( ptC, ptS2, ptE2, vtNorm)) return nullptr ; PtrOwner pBorder1( CreateCurveComposite()) ; if ( IsNull( pBorder1)) return nullptr ; pBorder1->AddPoint( ptC) ; pBorder1->AddLine( ptS) ; pBorder1->AddCurve( Release( pCrv)) ; pBorder1->Close() ; PtrOwner pBorder2( CreateCurveComposite()) ; if ( IsNull( pBorder2)) return nullptr ; pBorder2->AddPoint( ptC) ; pBorder2->AddLine( ptS2) ; pBorder2->AddCurve( Release( pArc2)) ; pBorder2->Close() ; PtrOwner pSrfTmp2( CreateSurfFlatRegion()) ; if ( IsNull( pSrfTmp2)) return nullptr ; pSrfTmp2->AddExtLoop( Release( pBorder2)) ; if ( ! pSrf->IsValid()) { pSrf->AddExtLoop( Release( pBorder1)) ; pSrf->Add( *pSrfTmp2) ; } else { PtrOwner pSrfTmp1( CreateSurfFlatRegion()) ; if ( IsNull( pSrfTmp1)) return nullptr ; pSrfTmp1->AddExtLoop( Release( pBorder1)) ; pSrf->Add( *pSrfTmp1) ; pSrf->Add( *pSrfTmp2) ; } } } return Release( pSrf) ; } //------------------------------------------------------------------------------- static bool AdjustSurfTriMeshRectSweptCaps( int nCapType, ICURVEPOVECTOR& vCrvs, const Vector3d& vtNorm, const ICurve* pOrigGuide, double dOffs) { // se RSCAP_FLAT trasformo tutte le giunzioni relative agli estremi in linee controllando se necessario chiudere le curve // con opportuni tratti di offset. // RSCAP_NONE viene gestito allo stesso modo perchè per la superficie top e bottom servono curve chiuse, le curve // di giunzione saranno rimosse successivamente per il calcolo della superficie laterale if ( nCapType == RSCAP_FLAT || nCapType == RSCAP_NONE) { // spezzo le curve in corrispondenza delle junctions RemoveFatCurveJunctions( vCrvs) ; // creo le linee di junction sugli estremi della giuda ( j = 0 start, j = 1 end) for ( int j = 0 ; j < 2 ; j ++) { Point3d ptP ; Vector3d vtDir ; if ( j == 0) { pOrigGuide->GetStartPoint( ptP) ; pOrigGuide->GetStartDir( vtDir) ; } else { pOrigGuide->GetEndPoint( ptP) ; pOrigGuide->GetEndDir( vtDir) ; } vtDir.Rotate( vtNorm, ANG_RIGHT) ; Point3d pt1 = ptP + dOffs * vtDir ; Point3d pt2 = ptP - dOffs * vtDir ; // cerco su quali curve poggia la linea di junction int nCrv1 = -1 ; bool bStart1 = true ; int nCrv2 = -1 ; bool bStart2 = true ; for ( int i = 0 ; i < ssize( vCrvs) ; i ++) { Point3d ptS ; vCrvs[i]->GetStartPoint( ptS) ; Point3d ptE ; vCrvs[i]->GetEndPoint( ptE) ; if ( AreSamePointEpsilon( ptS, pt1, 10 * EPS_SMALL)) { nCrv1 = i ; bStart1 = true ; } else if ( AreSamePointEpsilon( ptS, pt2, 10 * EPS_SMALL)) { nCrv2 = i ; bStart2 = true ; } if ( AreSamePointEpsilon( ptE, pt1, 10 * EPS_SMALL)) { nCrv1 = i ; bStart1 = false ; } else if ( AreSamePointEpsilon( ptE, pt2, 10 * EPS_SMALL)) { nCrv2 = i ; bStart2 = false ; } } int nFlatCapProp = ( j == 0 ? STM_RECTSWEPT_FLATCAP_START : STM_RECTSWEPT_FLATCAP_END) ; // se congiunge due curve le unisco if ( nCrv1 != -1 && nCrv2 != -1) { if ( nCrv1 == nCrv2) { // se è la stessa curva la chiudo semplicemente con una linea ICurveComposite* pCompo = GetCurveComposite( vCrvs[nCrv1]) ; if ( pCompo == nullptr) return false ; pCompo->Close() ; pCompo->SetCurveTempProp( pCompo->GetCurveCount() - 1, nFlatCapProp) ; } else { int nFirst = ( bStart1 ? nCrv2 : nCrv1) ; int nSecond = ( bStart1 ? nCrv1 : nCrv2) ; ICurveComposite* pCompo = GetCurveComposite( vCrvs[nFirst]) ; if ( pCompo == nullptr) return false ; Point3d ptRef ; vCrvs[nSecond]->GetStartPoint( ptRef) ; pCompo->AddLine( ptRef) ; pCompo->SetCurveTempProp( pCompo->GetCurveCount() - 1, nFlatCapProp) ; pCompo->AddCurve( Release( vCrvs[nSecond])) ; // elimino la curva mergiata dal vettore swap( vCrvs[nSecond], vCrvs.back()) ; vCrvs.pop_back() ; } } // se non congiunge due curve aggiungo il tratto lineare libero sull'estremo else { if ( nCrv1 != -1) { ICurveComposite* pCompo = GetCurveComposite( vCrvs[nCrv1]) ; if ( pCompo == nullptr) return false ; pCompo->AddLine( pt2, ! bStart1) ; int nSubCrv = ( bStart1 ? 0 : pCompo->GetCurveCount() - 1) ; pCompo->SetCurveTempProp( nSubCrv, nFlatCapProp) ; } else if ( nCrv2 != -1) { ICurveComposite* pCompo = GetCurveComposite( vCrvs[nCrv2]) ; if ( pCompo == nullptr) return false ; pCompo->AddLine( pt1, ! bStart2) ; int nSubCrv = ( bStart2 ? 0 : pCompo->GetCurveCount() - 1) ; pCompo->SetCurveTempProp( nSubCrv, nFlatCapProp) ; } } } // controllo chiusura di tutte le curve for ( int i = 0 ; i < ssize( vCrvs) ; i++) { if ( ! vCrvs[i]->IsClosed()) { ICurveComposite* pCompo = GetCurveComposite( vCrvs[i]) ; if ( pCompo == nullptr) return false ; double dTempProp1 = pCompo->GetFirstCurve()->GetTempProp() ; double dTempProp2 = pCompo->GetLastCurve()->GetTempProp() ; // verifico se necessaria inversione della guida per renderla coerente con la fat curve bool bInvert = false ; if ( dTempProp1 < 0 || dTempProp2 < 0) { bInvert = ( dTempProp1 == STM_RECTSWEPT_FLATCAP_END || dTempProp2 == STM_RECTSWEPT_FLATCAP_START) ; } else { Point3d ptS ; pCompo->GetStartPoint( ptS) ; DistPointCurve distPC( ptS, *pOrigGuide) ; int nSide = 0 ; distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; bInvert = ( nSide == MDS_RIGHT) ; } // ricavo la superficie spazzata dalla guida PtrOwner pGuide( ConvertCurveToComposite( pOrigGuide->Clone())) ; if ( bInvert) pGuide->Invert() ; PtrOwner pSurf( CalcSweptSurface( pGuide, vtNorm, dOffs)) ; // verifico come trimmare bool bTrimStart = false ; bool bTrimEnd = false ; if ( dTempProp1 < 0 && dTempProp2 >= 0) bTrimStart = true ; else if ( dTempProp1 >= 0 && dTempProp2 < 0) bTrimEnd = true ; else if ( dTempProp1 < 0 && dTempProp2 < 0) { bTrimStart = true ; bTrimEnd = true ; } // recupero il loop opportuno PtrOwner pBorder( ConvertCurveToComposite( pSurf->GetLoop( 0, 0))) ; if ( pSurf->GetChunkCount() > 1) { Point3d ptRef ; pCompo->GetStartPoint( ptRef) ; if ( bTrimStart) pCompo->GetEndPoint( ptRef) ; for ( int j = 0 ; j < pSurf->GetChunkCount() ; j ++) { pBorder.Set( ConvertCurveToComposite( pSurf->GetLoop( j, 0))) ; if ( pBorder->IsPointOn( ptRef)) break ; } } double dPar1, dPar2 ; if ( ! bTrimStart && ! bTrimEnd) { Point3d ptS ; pCompo->GetStartPoint( ptS) ; Point3d ptE ; pCompo->GetEndPoint( ptE) ; pBorder->GetParamAtPoint( ptE, dPar1) ; pBorder->GetParamAtPoint( ptS, dPar2) ; } else if ( bTrimStart && bTrimEnd) { const CurveLine* pLineS = GetBasicCurveLine( pCompo->GetFirstCurve()) ; const CurveLine* pLineE = GetBasicCurveLine( pCompo->GetLastCurve()) ; if ( pLineS == nullptr || pLineE == nullptr) return false ; IntersLineLine intLL( *pLineS, *pLineE) ; if ( intLL.GetNumInters() > 0) { IntCrvCrvInfo aInfo ; intLL.GetIntCrvCrvInfo( aInfo) ; pCompo->TrimStartEndAtParam( aInfo.IciA[0].dU, pCompo->GetCurveCount() - 1 + aInfo.IciB[0].dU) ; continue ; } else { // da fare in frame locale IntersCurveCurve intCC( *pLineS, *pBorder) ; IntCrvCrvInfo aInfo ; intCC.GetIntCrvCrvInfo( intCC.GetIntersCount() - 1, aInfo) ; dPar2 = aInfo.IciB[0].dU ; pCompo->TrimStartAtParam( aInfo.IciA[0].dU) ; IntersCurveCurve intCC2( *pLineE, *pBorder) ; intCC2.GetIntCrvCrvInfo( 0, aInfo) ; dPar1 = aInfo.IciB[0].dU ; pCompo->TrimEndAtParam( pCompo->GetCurveCount() - 1 + aInfo.IciA[0].dU) ; } } else if ( bTrimStart) { Point3d ptE ; pCompo->GetEndPoint( ptE) ; pBorder->GetParamAtPoint( ptE, dPar1) ; // intersezione con lo start const ICurve* pLine = pCompo->GetFirstCurve() ; IntersCurveCurve intCC( *pLine, *pBorder) ; IntCrvCrvInfo aInfo ; intCC.GetIntCrvCrvInfo( intCC.GetIntersCount() - 1, aInfo) ; dPar2 = aInfo.IciB[0].dU ; pCompo->TrimStartAtParam( aInfo.IciA[0].dU) ; } else { Point3d ptS ; pCompo->GetStartPoint( ptS) ; pBorder->GetParamAtPoint( ptS, dPar2) ; // intersezione con end const ICurve* pLine = pCompo->GetLastCurve() ; IntersCurveCurve intCC( *pLine, *pBorder) ; IntCrvCrvInfo aInfo ; intCC.GetIntCrvCrvInfo( 0, aInfo) ; dPar1 = aInfo.IciB[0].dU ; pCompo->TrimEndAtParam( pCompo->GetCurveCount() - 1 + aInfo.IciA[0].dU) ; } pBorder->TrimStartEndAtParam( dPar1, dPar2) ; // setto temp param per identificarli for ( int j = 0 ; j < pBorder->GetCurveCount() ; j++) { pBorder->SetCurveTempProp( j, STM_RECTSWEPT_FLATCAP_EXTRA) ; } pCompo->AddCurve( Release( pBorder)) ; } } } // se RSCAP_BEVEL approssimo tutte le junction relative agli estremi else if ( nCapType == RSCAP_BEVEL) { double dStepRotDeg = ANG_STRAIGHT / 4 ; // tolleranza for ( int i = 0 ; i < ssize( vCrvs) ; i ++) { CurveComposite* pFatCrv = GetBasicCurveComposite( vCrvs[i]) ; if ( pFatCrv == nullptr) return false ; PtrOwner pNewCrv( CreateBasicCurveComposite()) ; if ( IsNull( pNewCrv)) return false ; PtrOwner pCurrCrv( pFatCrv->RemoveFirstOrLastCurve( false)) ; while ( ! IsNull( pCurrCrv)) { if ( pCurrCrv->GetTempParam() > EPS_SMALL) { // sostituisco con la sua approssimazione CurveComposite* ccTemp = CreateBasicCurveComposite() ; CurveArc* pArc = GetBasicCurveArc( pCurrCrv) ; if ( pArc == nullptr || ccTemp == nullptr) return false ; int nStep = max( 1, static_cast( ceil( pArc->GetAngCenter() / dStepRotDeg))) ; for ( int j = 0 ; j <= nStep ; ++ j) { Point3d ptArc ; pArc->GetPointD1D2( (double)j / nStep, ICurve::FROM_MINUS, ptArc) ; if ( j == 0) ccTemp->AddPoint( ptArc) ; else ccTemp->AddLine( ptArc) ; } pNewCrv->AddCurve( ccTemp) ; } else { // aggiungo la curva pNewCrv->AddCurve( Release( pCurrCrv)) ; } // passo alla curva successiva pCurrCrv.Set( pFatCrv->RemoveFirstOrLastCurve( false)) ; } vCrvs[i].Set( pNewCrv) ; } } return true ; } //------------------------------------------------------------------------------- static ISurfTriMesh* GetSurfTriMeshSharpRectSwept( double dDimH, double dDimV, const ICurve* pGuide, int nCapType, double dLinTol) { // verifico che la linea guida sia piana Plane3d plGuide ; if ( ! pGuide->IsFlat( plGuide, true, 10 * EPS_SMALL)) return nullptr ; Vector3d vtNorm ; pGuide->GetExtrusion( vtNorm) ; if ( vtNorm.IsSmall()) vtNorm = Z_AX ; // determino se la guida è chiusa bool bGuideClosed = pGuide->IsClosed() ; // calcolo fat curve ICURVEPOVECTOR vFatCrvs ; if ( ! CalcCurveFatCurve( *pGuide, vFatCrvs, dDimH / 2, false, false)) return nullptr ; // se necessario modifico i caps sugli estremi if ( ! bGuideClosed) { if ( ! AdjustSurfTriMeshRectSweptCaps( nCapType, vFatCrvs, vtNorm, pGuide, dDimH / 2)) return nullptr ; } PtrOwner pSTM ; if ( nCapType == RSCAP_NONE && ! bGuideClosed) { // se nessun cap devo costruire separatamente le superfici top/bottom e quelle laterali CICURVEPVECTOR vTopCurves ; vTopCurves.reserve( vFatCrvs.size()) ; for ( int i = 0 ; i < ssize( vFatCrvs) ; i ++) vTopCurves.emplace_back( vFatCrvs[i]) ; PtrOwner pSrfTop( GetSurfTriMeshByRegion( vTopCurves, dLinTol)) ; if ( IsNull( pSrfTop)) return nullptr ; PtrOwner pSrfBot( pSrfTop->Clone()) ; if ( IsNull( pSrfBot)) return nullptr ; pSrfBot->Translate( -dDimV * vtNorm) ; pSrfBot->Invert() ; int nBuckets = max( 4 * ( pSrfTop->GetVertexSize()), 1000) ; StmFromTriangleSoup stmSoup ; if ( ! stmSoup.Start( nBuckets)) return nullptr ; stmSoup.AddSurfTriMesh( *pSrfTop) ; stmSoup.AddSurfTriMesh( *pSrfBot) ; // rimuovo le giunzioni per costruire le superfici laterali if ( ! RemoveFatCurveJunctions( vFatCrvs)) return nullptr ; for ( int i = 0 ; i < ssize( vFatCrvs) ; i ++) { PtrOwner pSrfLat( GetSurfTriMeshByExtrusion( vFatCrvs[i], -dDimV * vtNorm, false, dLinTol)) ; if ( IsNull( pSrfLat)) return nullptr ; pSrfLat->Invert() ; stmSoup.AddSurfTriMesh( *pSrfLat) ; } if ( ! stmSoup.End()) return nullptr ; pSTM.Set( stmSoup.GetSurf()) ; } else { // costruisco la superficie per estrusione dalle fat curve CICURVEPVECTOR vCurves ; vCurves.reserve( vFatCrvs.size()) ; for ( int i = 0 ; i < ssize( vFatCrvs) ; i ++) vCurves.emplace_back( vFatCrvs[i]) ; pSTM.Set( GetSurfTriMeshByRegionExtrusion( vCurves, - dDimV * vtNorm, dLinTol)) ; } if ( IsNull( pSTM)) return nullptr ; // salvo tolleranza lineare usata e imposto angolo per smooth pSTM->SetLinearTolerance( dLinTol) ; pSTM->SetSmoothAngle( 20) ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- static ICurveComposite* Connect( const ICurve* pGuide, ICURVEPOVECTOR& vCrvs, const INTVECTOR& vIdx, double dRadius, double dBevelH, double dBevelV, const Vector3d& vtNorm, int nCapType) { double dChainTol = 50 * EPS_SMALL ; double dOffsMin = 0.5 * dRadius - dBevelH ; double dOffsMax = 0.5 * dRadius ; // calcolo il medial axis e conservo solo le porzioni interne alla regione tra i due offset ICURVEPOVECTOR vMedialAxis ; CalcCurveMedialAxis( *pGuide, vMedialAxis, 0) ; ICRVCOMPOPOVECTOR vMedialCrvs ; for ( int i = 0 ; i < ssize( vMedialAxis) ; i ++) { double dPar1 = vMedialAxis[i]->GetTempParam( 0) ; double dPar2 = vMedialAxis[i]->GetTempParam( 1) ; // se non comprende l'offset non va considerato if ( dPar1 > dOffsMax - EPS_SMALL || dPar2 < dOffsMin + EPS_SMALL) continue ; if ( dOffsMin - EPS_SMALL < dPar1 && dPar2 < dOffsMax + EPS_SMALL) { // tratto va conservato completamente vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ; } else { // determino la parte compresa tra gli offset massimo e minimo if ( vMedialAxis[i]->GetType() == CRV_LINE) { double dTrimParS = ( dOffsMin - dPar1) / ( dPar2 - dPar1) ; double dTrimParE = ( dOffsMax - dPar1) / ( dPar2 - dPar1) ; dTrimParS = clamp( dTrimParS, 0.0, 1.0) ; dTrimParE = clamp( dTrimParE, 0.0, 1.0) ; vMedialAxis[i]->TrimStartEndAtParam( dTrimParS, dTrimParE) ; if ( dTrimParS > EPS_SMALL) vMedialAxis[i]->SetTempParam( dOffsMin, 0) ; if ( dTrimParE < 1 - EPS_SMALL) vMedialAxis[i]->SetTempParam( dOffsMax, 1) ; vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ; } else { ICurveComposite* pCompo = GetCurveComposite( vMedialAxis[i]) ; if ( pCompo == nullptr) return nullptr ; double dS, dE ; pCompo->GetDomain( dS, dE) ; double dTrimParS = dS ; double dTrimParE = dE ; for ( int j = 0 ; j < pCompo->GetCurveCount() ; j ++) { double dPar1 = pCompo->GetCurve( j)->GetTempParam( 0) ; double dPar2 = pCompo->GetCurve( j)->GetTempParam( 1) ; if ( dPar1 - EPS_SMALL < dOffsMin && dOffsMin < dPar2 + EPS_SMALL) dTrimParS = j + ( dOffsMin - dPar1) / ( dPar2 - dPar1) ; if ( dPar1 - EPS_SMALL < dOffsMax && dOffsMax < dPar2 + EPS_SMALL) { dTrimParE = j + ( dOffsMax - dPar1) / ( dPar2 - dPar1) ; break ; } } dTrimParS = clamp( dTrimParS, 0.0, dE) ; dTrimParE = clamp( dTrimParE, 0.0, dE) ; pCompo->TrimStartEndAtParam( dTrimParS, dTrimParE) ; if ( dTrimParS > EPS_SMALL) pCompo->SetCurveTempParam( 0, dOffsMin, 0) ; if ( dTrimParE < dE - EPS_SMALL) pCompo->SetCurveTempParam( pCompo->GetCurveCount() - 1, dOffsMax, 1) ; vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ; } } // aggiusto la quota if ( vMedialCrvs.back()->GetCurveCount() == 1) { double dPar1 = vMedialCrvs.back()->GetTempParam( 0) ; double dPar2 = vMedialCrvs.back()->GetTempParam( 1) ; Point3d ptS, ptE ; vMedialCrvs.back()->GetStartPoint( ptS) ; vMedialCrvs.back()->GetEndPoint( ptE) ; double dDeltaS = - dBevelV / dBevelH * ( dPar1 - dOffsMin) ; double dDeltaE = - dBevelV / dBevelH * ( dPar2 - dOffsMin) ; vMedialCrvs.back()->ModifyStart( ptS + vtNorm * dDeltaS) ; vMedialCrvs.back()->ModifyEnd( ptE + vtNorm * dDeltaE) ; } else { for ( int j = 0 ; j < vMedialCrvs.back()->GetCurveCount() ; j ++) { double dPar = vMedialCrvs.back()->GetCurve( j)->GetTempParam( 0) ; Point3d ptJ ; vMedialCrvs.back()->GetPointD1D2( j, ICurve::FROM_MINUS, ptJ) ; double dDelta = - dBevelV / dBevelH * ( dPar - dOffsMin) ; vMedialCrvs.back()->ModifyJoint( j, ptJ + vtNorm * dDelta) ; } Point3d ptE ; vMedialCrvs.back()->GetEndPoint( ptE) ; double dPar = vMedialCrvs.back()->GetLastCurve()->GetTempParam( 1) ; double dDelta = - dBevelV / dBevelH * ( dPar - dOffsMin) ; vMedialCrvs.back()->ModifyEnd( ptE + vtNorm * dDelta) ; } } // chain dei medialaxis GetChainedCurves( vMedialCrvs, 5 * EPS_SMALL, true) ; // se cap flat aggiungo tratti sugli estremi if ( nCapType == RSCAP_FLAT) { Frame3d frLoc ; frLoc.Set( ORIG, vtNorm) ; for ( int i = 0 ; i < 2 ; i ++) { // recupero il tratto start/end int nRefProp = ( i == 0 ? STM_RECTSWEPT_FLATCAP_START : STM_RECTSWEPT_FLATCAP_END) ; bool bFound = false ; for ( int j = 0 ; j < ssize( vCrvs) && ! bFound ; j ++) { ICurveComposite* pCompo = GetCurveComposite( vCrvs[j]) ; if ( pCompo == nullptr) return nullptr ; for ( int k = 0 ; k < pCompo->GetCurveCount() ; k++) { int nProp = pCompo->GetCurve( k)->GetTempProp() ; if ( nProp == nRefProp) { double dLen ; pCompo->GetCurve( k)->GetLength( dLen) ; if ( abs( dLen - 2 * dOffsMax) > EPS_SMALL) { Point3d ptP ; pCompo->GetCurve( k)->GetStartPoint( ptP) ; Vector3d vtDir ; pCompo->GetCurve( k)->GetStartDir( vtDir) ; if ( i == 0) vtDir.Invert() ; PtrOwner pLine( CreateCurveLine()) ; if ( IsNull( pLine)) return nullptr ; pLine->SetPVL( ptP, vtDir, 2 * dOffsMax - dLen) ; pLine->ToLoc( frLoc) ; // verifico se interseca una curva dei medialaxis for ( int l = 0 ; l < ssize( vMedialCrvs) ; l++) { vMedialCrvs[l]->ToLoc( frLoc) ; IntersCurveCurve intCC( *pLine, *vMedialCrvs[l]) ; vMedialCrvs[l]->ToGlob( frLoc) ; if ( intCC.GetIntersCount() == 1) { IntCrvCrvInfo aInfo ; intCC.GetIntCrvCrvInfo( 0, aInfo) ; double dTempParam1 = vMedialCrvs[l]->GetFirstCurve()->GetTempParam() ; double dTempParam2 = vMedialCrvs[l]->GetLastCurve()->GetTempParam() ; if ( dTempParam1 < dTempParam2) { vMedialCrvs[l]->TrimStartAtParam( aInfo.IciB[0].dU) ; vMedialCrvs[l]->AddLine( ptP, false) ; vMedialCrvs[l]->SetCurveTempParam( 0, dOffsMax, 0) ; } else { vMedialCrvs[l]->TrimEndAtParam( aInfo.IciB[0].dU) ; vMedialCrvs[l]->AddLine( ptP) ; vMedialCrvs[l]->SetCurveTempParam( vMedialCrvs[l]->GetCurveCount() - 1, dOffsMax, 1) ; } break ; } } } bFound = true ; break ; } } } } } // creo catena ICurveComposite* pResult = nullptr ; BOOLVECTOR vUsed1( vIdx.size(), false) ; for ( int i = 0 ; i < ssize( vMedialCrvs) ; i++) { // verifico se è una curva sensata per il collegamento ( ovvero se i parametri ai suoi estremi sono il valore di max offset) bool bValid = false ; if ( vMedialCrvs[i]->GetCurveCount() == 1) { double dPar1 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam() ; double dPar2 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam( 1) ; bValid = ( abs( dPar1 - dOffsMax) < EPS_SMALL && abs( dPar2 - dOffsMax) < EPS_SMALL) ; } else { // controllo sia parametro start ed end perchè il chain permette inversione double dParS1 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam() ; double dParS2 = vMedialCrvs[i]->GetFirstCurve()->GetTempParam( 1) ; double dParE1 = vMedialCrvs[i]->GetLastCurve()->GetTempParam( ) ; double dParE2 = vMedialCrvs[i]->GetLastCurve()->GetTempParam( 1) ; bValid = ( ( abs( dParS1 - dOffsMax) < EPS_SMALL || abs( dParS2 - dOffsMax) < EPS_SMALL) && ( abs( dParE1 - dOffsMax) < EPS_SMALL || abs( dParE2 - dOffsMax) < EPS_SMALL)) ; } if ( ! bValid) continue ; // setto tutte le temp prop ad un valore fissato per identificarla in seguito for ( int nC = 0 ; nC < vMedialCrvs[i]->GetCurveCount() ; nC ++) vMedialCrvs[i]->SetCurveTempProp( nC, STM_RECTSWEPT_LINK) ; // trovo le curve che collega Point3d ptS ; vMedialCrvs[i]->GetStartPoint( ptS) ; Point3d ptE ; vMedialCrvs[i]->GetEndPoint( ptE) ; double dParS = -1, dParE = -1 ; int nS = -1, nE = -1 ; for ( int j = 0 ; j < ssize( vIdx) ; j ++) { if ( vCrvs[vIdx[j]] == nullptr) continue ; if ( nS == -1 && vCrvs[vIdx[j]]->GetParamAtPoint( ptS, dParS, dChainTol)) nS = vIdx[j] ; else if ( nE == -1 && vCrvs[vIdx[j]]->GetParamAtPoint( ptE, dParE, dChainTol)) nE = vIdx[j] ; if ( nS != -1 && nE != -1) break ; } if ( nS == -1 || nE == -1) return nullptr ; // i parametri devono essere su un joint per costruzione dParS = round( dParS) ; dParE = round( dParE) ; pResult = GetCurveComposite( vCrvs[nS]) ; ICurve* pResultEnd = nullptr ; if ( dParS > EPS_SMALL) { pResultEnd = pResult->CopyParamRange( dParS, pResult->GetCurveCount()) ; pResult->TrimEndAtParam( dParS) ; } pResult->AddCurve( vMedialCrvs[i]->Clone(), true, dChainTol) ; GetBasicCurveComposite( vCrvs[nE])->ChangeStartPoint( dParE) ; pResult->AddCurve( Release( vCrvs[nE]), true, dChainTol) ; vMedialCrvs[i]->Invert() ; pResult->AddCurve( Release( vMedialCrvs[i]), true, dChainTol) ; if ( pResultEnd != nullptr) pResult->AddCurve( pResultEnd, true, dChainTol) ; // forzo chiusura pResult->Close() ; } return pResult ; } //------------------------------------------------------------------------------- static bool GetTrimParams( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int nOrigCrv, int nCurrCrv2, const Vector3d& vtNorm, DBLVECTOR& vPar1, DBLVECTOR& vPar2) { int nFlag ; Point3d ptP ; pCrv2->GetCurve( nCurrCrv2)->GetStartPoint( ptP) ; int nStart = int( floor( vPar1.back() + 0.5)) ; // se deriva da una curva ben definita della giuda cerco la corrispondente dal lato corretto sull'altra curva if ( nOrigCrv != 0) { for ( int j = nStart ; j < pCrv1->GetCurveCount() ; j ++) { int nOrigCrv1 = pCrv1->GetCurve( j)->GetTempProp() ; if ( nOrigCrv1 == nOrigCrv) { // se estremo di flat cap spezzo il bisettore appena trovato nel punto a minima distanza // e sulla curva 1 avrò corrispondenza con un punto if ( nOrigCrv == STM_RECTSWEPT_FLATCAP_START || nOrigCrv == STM_RECTSWEPT_FLATCAP_END) { Point3d ptRef ; pCrv1->GetCurve( j)->GetStartPoint( ptRef) ; PtrOwner pCurrBisector( pCrv2->CopyParamRange( vPar2.back(), nCurrCrv2)) ; DistPointCurve distPC( ptRef, *pCurrBisector) ; double dParTrim ; distPC.GetParamAtMinDistPoint( 0, dParTrim, nFlag) ; vPar1.emplace_back( j) ; vPar2.emplace_back( vPar2.back() + dParTrim) ; vPar1.emplace_back( j) ; vPar2.emplace_back( nCurrCrv2) ; } else { DistPointCurve distPC( ptP, *pCrv1->GetCurve( j)) ; int nSide = 0 ; double dParMinDist ; distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; if ( nSide == MDS_RIGHT) { distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ; dParMinDist += j ; vPar1.emplace_back( dParMinDist) ; vPar2.emplace_back( nCurrCrv2) ; break ; } } } } } // se deriva da raccordo devo controllare il più vicino else { double dMinDist = INFINITO ; double dParMinDist ; for ( int j = nStart ; j < pCrv1->GetCurveCount() ; j ++) { int nOrigCrv1 = pCrv1->GetCurve( j)->GetTempProp() ; if ( nOrigCrv1 == nOrigCrv) { DistPointCurve distPC( ptP, *pCrv1->GetCurve( j)) ; int nSide = 0 ; distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; if ( nSide == MDS_RIGHT) { double dCurrDist ; distPC.GetDist( dCurrDist) ; if ( dCurrDist < dMinDist) { distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ; dParMinDist += j ; dMinDist = dCurrDist ; } } } } vPar1.emplace_back( dParMinDist) ; vPar2.emplace_back( nCurrCrv2) ; } return true ; } //------------------------------------------------------------------------------- static bool Associate( const ICurveComposite* pCrv1, ICurveComposite* pCrv2, const Vector3d& vtNorm, DBLVECTOR& vPar1, DBLVECTOR& vPar2) { vPar1.emplace_back( 0) ; vPar2.emplace_back( 0) ; // assegno il punto di inizio della curva 2 il più vicino possibile a quello della curva 1 preferendo curve non problematiche int nStartCrvId = pCrv1->GetFirstCurve()->GetTempProp() ; if ( nStartCrvId == STM_RECTSWEPT_FLATCAP_START) { for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i ++) { int nOrigCrv = pCrv2->GetCurve( i)->GetTempProp() ; if ( nOrigCrv == nStartCrvId) { pCrv2->ChangeStartPoint( i) ; break ; } } } else { Point3d ptStart ; pCrv1->GetStartPoint( ptStart) ; double dPar ; int nFlag ; DistPointCurve( ptStart, *pCrv2).GetParamAtMinDistPoint(0, dPar, nFlag) ; pCrv2->ChangeStartPoint( dPar) ; } // segnalo parametro di split per le curve quando trovo l'inizio o la fine di un tratto che deriva da un bisettore // o da un tratto aggiunto per la chiusura dei flat caps bool bBisector = false ; for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i++) { int nTmpProp = pCrv2->GetCurve( i)->GetTempProp() ; if ( nTmpProp == STM_RECTSWEPT_LINK || nTmpProp == STM_RECTSWEPT_FLATCAP_EXTRA) { if ( ! bBisector) { // inizio bisettore bBisector = true ; if ( i > 0) { int nOrigCrv = pCrv2->GetCurve( i-1)->GetTempProp() ; GetTrimParams( pCrv1, pCrv2, nOrigCrv, i, vtNorm, vPar1, vPar2) ; } } } else { if ( bBisector) { // fine del bisettore bBisector = false ; int nOrigCrv = pCrv2->GetCurve(i)->GetTempProp() ; GetTrimParams( pCrv1, pCrv2, nOrigCrv, i, vtNorm, vPar1, vPar2) ; } } } // aggiungo parametro finale double dTmp, dE1, dE2 ; pCrv1->GetDomain( dTmp, dE1) ; pCrv2->GetDomain( dTmp, dE2) ; vPar1.emplace_back( dE1) ; vPar2.emplace_back( dE2) ; return true ; } //------------------------------------------------------------------------------- static POLYLINEVECTOR TrimPolyLine( const PolyLine& PLOrig, const DBLVECTOR& vPar) { POLYLINEVECTOR vPL ; vPL.reserve( vPar.size()) ; double dPar1, dPar2 ; Point3d pt1, pt2 ; bool bFound = PLOrig.GetFirstUPoint( &dPar1, &pt1) ; PolyLine PLCurr ; PLCurr.AddUPoint( 0, pt1) ; bFound = PLOrig.GetNextUPoint( &dPar2, &pt2) ; for ( int i = 0 ; i < ssize( vPar) && bFound ; i ++) { while( dPar2 < vPar[i] - EPS_SMALL && bFound) { // se non si è ancora arrivati al parametro richiesto devo aggiungere il punto e passare al successivo PLCurr.AddUPoint( dPar2, pt2) ; dPar1 = dPar2 ; pt1 = pt2 ; bFound = PLOrig.GetNextUPoint( &dPar2, &pt2) ; } if ( bFound) { // ricavo il punto di interruzione double dCoeff = ( vPar[i] - dPar1) / ( dPar2 - dPar1) ; Point3d ptSplit = Media( pt1, pt2, dCoeff) ; PLCurr.AddUPoint( vPar[i], ptSplit) ; // ho concluso una polyline e pareparo la prossima vPL.emplace_back( PLCurr) ; PLCurr.Clear() ; PLCurr.AddUPoint( vPar[i], ptSplit) ; } } // ultima curva if ( PLCurr.GetPointNbr() > 1) vPL.emplace_back( PLCurr) ; return vPL ; } //------------------------------------------------------------------------------- static ISurfTriMesh* GetSurfTriMeshBeveledRectSwept( double dDimH, double dDimV, double dBevelH, double dBevelV, const ICurve* pGuide, int nCapType, double dLinTol) { // verifico che la linea guida sia piana Plane3d plGuide ; if ( ! pGuide->IsFlat( plGuide, true, 10 * EPS_SMALL)) return nullptr ; Vector3d vtNorm ; pGuide->GetExtrusion( vtNorm) ; if ( vtNorm.IsSmall()) vtNorm = Z_AX ; // determino il punto centrale della sezione Point3d ptCen ; pGuide->GetStartPoint( ptCen) ; ptCen -= dDimV / 2 * vtNorm ; // determino se la guida è chiusa bool bGuideClosed = pGuide->IsClosed() ; // calcolo le fat curve ICURVEPOVECTOR vFatCrvs1 ; if ( ! CalcCurveFatCurve( *pGuide, vFatCrvs1, dDimH / 2 - dBevelH, false, false)) return nullptr ; ICURVEPOVECTOR vFatCrvs2 ; if ( ! CalcCurveFatCurve( *pGuide, vFatCrvs2, dDimH / 2, false, false)) return nullptr ; // aggiusto per eventuali caps if ( ! bGuideClosed) { if ( ! AdjustSurfTriMeshRectSweptCaps( nCapType, vFatCrvs1, vtNorm, pGuide, dDimH / 2 - dBevelH) || ! AdjustSurfTriMeshRectSweptCaps( nCapType, vFatCrvs2, vtNorm, pGuide, dDimH / 2)) return nullptr ; } // setto estrusione for ( int i = 0 ; i < ssize( vFatCrvs1) ; i++) vFatCrvs1[i]->SetExtrusion( vtNorm) ; for ( int i = 0 ; i < ssize( vFatCrvs2) ; i++) vFatCrvs2[i]->SetExtrusion( vtNorm) ; // ordino i vettori per avere il loop esterno per primo for ( int i = 0 ; i < ssize( vFatCrvs1) ; i ++) { Plane3d plPlane ; double dTmp ; vFatCrvs1[i]->GetArea( plPlane, dTmp) ; if ( AreSameVectorApprox( plPlane.GetVersN(), vtNorm)) { swap( vFatCrvs1[i], vFatCrvs1[0]) ; break ; } } for ( int i = 0 ; i < ssize( vFatCrvs2) ; i ++) { Plane3d plPlane ; double dTmp ; vFatCrvs2[i]->GetArea( plPlane, dTmp) ; if ( AreSameVectorApprox( plPlane.GetVersN(), vtNorm)) { swap( vFatCrvs2[i], vFatCrvs2[0]) ; break ; } } // sposto il punto di start del loop esterno della FatCrv1 in modo sensato ICurveComposite* pCompo = GetCurveComposite( vFatCrvs1[0]) ; if ( pCompo == nullptr) return nullptr ; for ( int j = 0 ; j < pCompo->GetCurveCount() ; j ++) { int nOrigCrv = pCompo->GetCurve( j)->GetTempProp() ; if ( nOrigCrv > 0) { Point3d ptP ; pCompo->GetCurve( j)->GetStartPoint( ptP) ; DistPointCurve distPC( ptP, *vFatCrvs2[0]) ; double dDist = 0 ; distPC.GetDist( dDist) ; if ( abs( dDist - dBevelH) < 100 * EPS_SMALL) { pCompo->ChangeStartPoint( j + 0.5) ; break ; } } } for ( int i = 0 ; i < ssize( vFatCrvs2) ; i++) vFatCrvs2[i]->Translate( - dBevelV * vtNorm) ; // trasformo in polylines POLYLINEVECTOR vPL1( vFatCrvs1.size()) ; for ( int i = 0 ; i < ssize( vFatCrvs1) ; i++) vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ; // costruisco le parti di superficie PtrOwner pSrfTop( CreateSurfTriMesh()) ; if ( IsNull( pSrfTop) || ! pSrfTop->CreateByPolygonWithHoles( vPL1)) return nullptr ; PtrOwner pSrfBot( pSrfTop->Clone()) ; if ( IsNull( pSrfBot)) return nullptr ; pSrfBot->Translate( -dDimV * vtNorm) ; pSrfBot->Invert() ; int nBuckets = max( 4 * ( pSrfTop->GetVertexSize()), 1000) ; StmFromTriangleSoup stmSoup ; if ( ! stmSoup.Start( nBuckets)) return nullptr ; stmSoup.AddSurfTriMesh( *pSrfTop) ; stmSoup.AddSurfTriMesh( *pSrfBot) ; BOOLVECTOR bUsed( vFatCrvs2.size(), false) ; bool bRepair = false ; for ( int i = 0 ; i < ssize( vFatCrvs1) ; i ++) { // cerco le FatCurve2 che contengono la FatCurve1 corrente INTVECTOR vAssociatedIdx ; for ( int j = 0 ; j < ssize( vFatCrvs2) ; j ++) { if ( ! bUsed[j]) { double dS, dE ; vFatCrvs2[j]->GetDomain( dS, dE) ; double dU = 0.5 ; int nSide = MDS_ON ; while ( nSide == MDS_ON && dU < dE) { Point3d ptTest ; vFatCrvs2[j]->GetPointD1D2( dU, ICurve::FROM_MINUS, ptTest) ; DistPointCurve distPC( ptTest, *vFatCrvs1[i]) ; distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; if ( nSide == MDS_RIGHT) { bUsed[j] = true ; vAssociatedIdx.emplace_back( j) ; } dU += 1 ; } } } ISURFTMPOVECTOR vSurfLatTop ; ISURFTMPOVECTOR vSurfLat ; // a) se non ha associate allora collassa in un punto/curva ad una quota opportuna if ( vAssociatedIdx.empty()) { double dLimitOffs ; if ( ! CalcCurveLimitOffset( *vFatCrvs1[i], dLimitOffs)) return nullptr ; OffsetCurve OffsCrv ; OffsCrv.Make( vFatCrvs1[i], dLimitOffs, ICurve::OFF_FILLET) ; PtrOwner pSrfLatT( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfLatT)) return nullptr ; Point3d ptOffs ; Vector3d vtOut ; if ( OffsCrv.GetPointOffset( ptOffs, vtOut)) { // se collassa in un punto ptOffs.Translate( - dBevelV / dBevelH * dLimitOffs * vtNorm) ; pSrfLatT->CreateByPointCurve( ptOffs, vPL1[i]) ; pSrfLatT->Invert() ; } else { // se collassa in una curva PtrOwner pAssociatedCrv( ConvertCurveToComposite( OffsCrv.GetLongerCurve())) ; if ( IsNull( pAssociatedCrv)) return nullptr ; pAssociatedCrv->Translate( - dBevelV / dBevelH * dLimitOffs * vtNorm) ; Point3d ptStart ; vFatCrvs1[i]->GetStartPoint( ptStart) ; double dPar ; int nFlag ; DistPointCurve( ptStart, *pAssociatedCrv).GetParamAtMinDistPoint(0, dPar, nFlag) ; pAssociatedCrv->ChangeStartPoint( dPar) ; PolyLine PL2 ; pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; pSrfLatT->CreateByTwoCurves( PL2, vPL1[i], ISurfTriMesh::RLT_MINDIST) ; } vSurfLatTop.emplace_back( Release( pSrfLatT)) ; } // b) se ha una sola associata costruisco la rigata tra le due curve else if ( vAssociatedIdx.size() == 1) { ICurveComposite* pAssociatedCrv = GetCurveComposite( vFatCrvs2[vAssociatedIdx[0]]) ; if ( pAssociatedCrv == nullptr) return nullptr ; // per avere rigata ottimale modifico il punto di inizio della seconda curva Point3d ptStart ; vFatCrvs1[i]->GetStartPoint( ptStart) ; double dPar ; int nFlag ; DistPointCurve( ptStart, *pAssociatedCrv).GetParamAtMinDistPoint( 0, dPar, nFlag) ; pAssociatedCrv->ChangeStartPoint( dPar) ; PolyLine PL2 ; pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; PtrOwner pSrfLatT( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfLatT) || ! pSrfLatT->CreateByTwoCurves( PL2, vPL1[i], ISurfTriMesh::RLT_MINDIST)) return nullptr ; vSurfLatTop.emplace_back( Release( pSrfLatT)) ; PtrOwner pSrfLat( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfLat) || ! pSrfLat->CreateByExtrusion( PL2, ( - dDimV + 2 * dBevelV) * vtNorm)) return nullptr ; pSrfLat->Invert() ; vSurfLat.emplace_back( Release( pSrfLat)) ; } else { bRepair = true ; // superfici verticali for ( int j = 0 ; j < ssize( vAssociatedIdx) ; j ++) { PolyLine PL2 ; vFatCrvs2[vAssociatedIdx[j]]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; PtrOwner pSrfLat( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfLat) || ! pSrfLat->CreateByExtrusion( PL2, ( - dDimV + 2 * dBevelV) * vtNorm)) return nullptr ; pSrfLat->Invert() ; vSurfLat.emplace_back( Release( pSrfLat)) ; } // collego le curve tramite bisettori ICurveComposite* pFatCrv2 = Connect( pGuide, vFatCrvs2, vAssociatedIdx, dDimH, dBevelH, dBevelV, vtNorm, nCapType) ; if ( pFatCrv2 == nullptr) return nullptr ; // individuo i punti ottimali dove spezzare le polylines per avere delle rigate ottimali DBLVECTOR vPar1, vPar2 ; Associate( GetCurveComposite( vFatCrvs1[i]), pFatCrv2, vtNorm, vPar1, vPar2) ; PolyLine PL2 ; pFatCrv2->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; POLYLINEVECTOR vPolyLine1 = TrimPolyLine( vPL1[i], vPar1) ; POLYLINEVECTOR vPolyLine2 = TrimPolyLine( PL2, vPar2) ; for ( int j = 0 ; j < ssize( vPolyLine1) ; j ++) { if ( vPolyLine1[j].GetPointNbr() == 1 ) { // collassa Point3d pt1 ; vPolyLine1[j].GetFirstPoint( pt1) ; Point3d pt2 ; vPolyLine2[j].GetFirstPoint( pt2) ; Point3d pt3 ; vPolyLine2[j].GetLastPoint( pt3) ; vPolyLine2[j].Close() ; PtrOwner pSrfLatT( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfLatT )) return nullptr ; pSrfLatT->CreateByFlatContour( vPolyLine2[j]) ; vSurfLatTop.emplace_back( Release( pSrfLatT)) ; Triangle3d trExtra ; trExtra.Set( pt1, pt2, pt3) ; stmSoup.AddTriangle( trExtra) ; trExtra.Mirror( ptCen, vtNorm) ; stmSoup.AddTriangle( trExtra) ; } else { PtrOwner pSrfLatT( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfLatT)) return nullptr ; pSrfLatT->CreateByTwoCurves( vPolyLine1[j], vPolyLine2[j], ISurfTriMesh::RLT_MINDIST) ; pSrfLatT->Invert() ; vSurfLatTop.emplace_back( Release( pSrfLatT)) ; } } } // inserisco le superfici laterali verticali for ( int j = 0 ; j < ssize( vSurfLat) ; j ++) stmSoup.AddSurfTriMesh( *vSurfLat[j]) ; // inserisco le superfici laterali rigate e il loro mirror for ( int j = 0 ; j < ssize( vSurfLatTop) ; j ++) { stmSoup.AddSurfTriMesh( *vSurfLatTop[j]) ; vSurfLatTop[j]->Mirror( ptCen, vtNorm) ; stmSoup.AddSurfTriMesh( *vSurfLatTop[j]) ; } } if ( ! stmSoup.End()) return nullptr ; PtrOwner pSTM ; pSTM.Set( stmSoup.GetSurf()) ; if ( IsNull( pSTM)) return nullptr ; // se ho spezzato per la superficie laterale, rischio di aver creato Tjunctions if ( bRepair) pSTM->Repair() ; // salvo tolleranza lineare usata e imposto angolo per smooth pSTM->SetLinearTolerance( dLinTol) ; pSTM->SetSmoothAngle( 20) ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshRectSwept( double dDimH, double dDimV, double dBevelH, double dBevelV, const ICurve* pGuide, int nCapType, double dLinTol) { // verifica parametri if ( pGuide == nullptr || dBevelH > 0.4 * dDimH || dBevelV > 0.4 * dDimV) return nullptr ; // determino se sezione squadrata o con smusso bool bSharp = ( dBevelH < 100 * EPS_SMALL || dBevelV < 100 * EPS_SMALL) ; // eseguo if ( bSharp) return GetSurfTriMeshSharpRectSwept( dDimH, dDimV, pGuide, nCapType, dLinTol) ; else return GetSurfTriMeshBeveledRectSwept( dDimH, dDimV, dBevelH, dBevelV, pGuide, nCapType, dLinTol) ; } //------------------------------------------------------------------------------- static ISurfTriMesh* GetSurfTriMeshSweptInPlane( const ICurve* pSect, const ICurve* pGuide, const Vector3d& vtNorm, bool bCapEnds, double dLinTol) { // determino se la sezione è chiusa bool bSectClosed = pSect->IsClosed() ; // determino se la guida è chiusa bool bGuideClosed = pGuide->IsClosed() ; // riferimento all'inizio della linea guida Frame3d frStart ; Point3d ptStart ; pGuide->GetStartPoint( ptStart) ; Vector3d vtStart ; pGuide->GetStartDir( vtStart) ; frStart.Set( ptStart, -vtStart, vtStart ^ vtNorm) ; // calcolo la polilinea che approssima la sezione PolyLine PL ; if ( ! pSect->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL)) return nullptr ; // porto la sezione in questo riferimento e ve la appiattisco if ( ! PL.ToLoc( frStart) || ! PL.Flatten()) return nullptr ; // preparo collettore delle superfici componenti StmFromTriangleSoup StmFts ; if ( ! StmFts.Start()) return nullptr ; // superficie swept PtrOwner pPrevCrv ; Point3d ptP ; bool bPoint = PL.GetFirstPoint( ptP) ; while ( bPoint) { // nuova curva ( definita dall'Offset ) OffsetCurve OffsCrv ; if ( ! OffsCrv.Make( pGuide, ptP.x, ICurve::OFF_FILLET) || OffsCrv.GetCurveCount() == 0) return nullptr ; PtrOwner pCurrCrv( OffsCrv.GetLongerCurve()) ; if ( IsNull( pCurrCrv)) return nullptr ; pCurrCrv->Translate( ptP.y * frStart.VersY()) ; // se esiste la curva precedente, costruisco la rigata ( di tipo minima distanza) if ( ! IsNull( pPrevCrv)) { PtrOwner pSr( GetSurfTriMeshRuled( pPrevCrv, pCurrCrv, ISurfTriMesh::RLT_MINDIST, dLinTol)) ; if ( IsNull( pSr)) return nullptr ; // inserisco nel collettore StmFts.AddSurfTriMesh( *pSr) ; } // salvo la curva come prossima precedente pPrevCrv.Set( pCurrCrv) ; // prossimo punto bPoint = PL.GetNextPoint( ptP) ; } // recupero la supeficie risultante if ( ! StmFts.End()) return nullptr ; PtrOwner pSTM( GetBasicSurfTriMesh( StmFts.GetSurf())) ; if ( IsNull( pSTM)) return nullptr ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // se richiesti caps e sezione chiusa e guida aperta if ( bCapEnds && bSectClosed && ! bGuideClosed) { // verifico che le due estremità siano chiuse e piatte POLYLINEVECTOR vPL ; if ( ! pSTM->GetLoops( vPL) || vPL.size() != 2) return nullptr ; Plane3d plEnds ; double dArea ; if ( ! vPL[0].IsClosedAndFlat( plEnds, dArea, 100 * EPS_SMALL)) return nullptr ; if ( ! vPL[1].IsClosedAndFlat( plEnds, dArea, 100 * EPS_SMALL)) return nullptr ; // aggiungo il cap sull'inizio PtrOwner pSci( CreateBasicSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByFlatContour( PL)) return nullptr ; pSci->ToGlob( frStart) ; // unisco pSTM->DoSewing( *pSci) ; // riferimento alla fine della linea guida Frame3d frEnd ; Point3d ptEnd ; pGuide->GetEndPoint( ptEnd) ; Vector3d vtEnd ; pGuide->GetEndDir( vtEnd) ; frEnd.Set( ptEnd, -vtEnd, vtEnd ^ vtNorm) ; // aggiungo il cap sulla fine PtrOwner pSce( CreateBasicSurfTriMesh()) ; if ( IsNull( pSce) || ! pSce->CreateByFlatContour( PL)) return nullptr ; pSce->Invert() ; pSce->ToGlob( frEnd) ; // unisco pSTM->DoSewing( *pSce) ; } // se superficie risultante chiusa, verifico che la normale sia verso l'esterno double dVol ; if ( pSTM->GetVolume( dVol) && dVol < 0) pSTM->Invert() ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- static ISurfTriMesh* GetSurfTriMeshSwept3d( const ICurve* pSect, const ICurve* pGuide, const Vector3d& vtAx, bool bCapEnds, double dLinTol) { // determino se la sezione è chiusa bool bSectClosed = pSect->IsClosed() ; // determino se la guida è chiusa bool bGuideClosed = pGuide->IsClosed() ; // determino algoritmo da usare per calcolare i riferimenti lungo la curva bool bRMF = ( ! vtAx.IsValid() || vtAx.IsSmall()) ; // riferimento all'inizio della linea guida Point3d ptStart ; pGuide->GetStartPoint( ptStart) ; Vector3d vtStart ; pGuide->GetStartDir( vtStart) ; Frame3d frStart ; if ( bRMF) { if ( ! frStart.Set( ptStart, vtStart)) return nullptr ; } else { Vector3d vtAxX = vtAx ^ vtStart ; if ( vtAxX.IsSmall()) { vtAxX = FromUprightOrtho( vtAx) ; vtAxX.Rotate( vtAx, 0, 1) ; } if ( ! frStart.Set( ptStart, vtStart, vtAxX)) return nullptr ; } // calcolo la polilinea che approssima la sezione PolyLine PL ; if ( ! pSect->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL)) return nullptr ; // recupero la sezione dalla PolyLine approssimata come Curva PtrOwner pSecLocApprox( CreateBasicCurveComposite()) ; if ( IsNull( pSecLocApprox) || ! pSecLocApprox->FromPolyLine( PL) || ! pSecLocApprox->IsValid()) return nullptr ; // porto la PolyLine e la curva della sezione nel riferimento nel punto iniziale della guida PL.ToLoc( frStart) ; pSecLocApprox->ToLoc( frStart) ; // calcolo il vettore di Frames campionati lungo la guida mediante la tolleranza definita FRAME3DVECTOR vFrames ; if ( bRMF) { RotationMinimizingFrame RMF ; if ( ! RMF.Set( pGuide, frStart) || ! RMF.GetFramesByTolerance( dLinTol, vFrames) || vFrames.empty()) return nullptr ; } else { RotationXplaneFrame RXF ; if ( ! RXF.Set( pGuide, vtAx, frStart.VersX()) || ! RXF.GetFramesByTolerance( dLinTol, vFrames) || vFrames.empty()) return nullptr ; } // preparo collettore delle superfici componenti StmFromTriangleSoup StmFts ; if ( ! StmFts.Start()) return nullptr ; // per ogni Frame calcolato, la sezione va roto-traslata lungo la guida for ( int i = 0 ; i < int( vFrames.size()) - 1 ; ++ i) { // definisco la sezione allo step corrente PtrOwner pSecCurr( pSecLocApprox->Clone()) ; if ( IsNull( pSecCurr) || ! pSecCurr->IsValid()) return nullptr ; // considero la sezione ( in locale ) come vista dal globale if ( ! pSecCurr->ToGlob( vFrames[i])) return nullptr ; // definisco la sezione allo step successivo PtrOwner pSecSucc( pSecLocApprox->Clone()) ; if ( IsNull( pSecSucc) || ! pSecSucc->IsValid()) return nullptr ; // considero la sezione ( in locale ) come vista dal globale if ( ! pSecSucc->ToGlob( vFrames[i+1])) return nullptr ; // creo la rigata tra queste due sezioni PtrOwner pSr( GetSurfTriMeshRuled( pSecCurr, pSecSucc, ISurfTriMesh::RLT_ISOPAR_SMOOTH, dLinTol)) ; if ( IsNull( pSr) || ! pSr->IsValid()) return nullptr ; // la inverto pSr->Invert() ; // inserisco la superficie nel collettore StmFts.AddSurfTriMesh( *pSr) ; } // se richiesti caps e sezione chiusa e guida aperta if ( bCapEnds && bSectClosed && ! bGuideClosed) { // aggiungo il cap sull'inizio ( portandolo nel frame del punto iniziale della guida ) PtrOwner pSci( CreateBasicSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByFlatContour( PL)) return nullptr ; pSci->ToGlob( vFrames.front()) ; // aggiungo StmFts.AddSurfTriMesh( *pSci) ; // aggiungo il cap sulla fine ( portandolo nel frame del punto finale della guida ) PtrOwner pSce( CreateBasicSurfTriMesh()) ; if ( IsNull( pSce) || ! pSce->CreateByFlatContour( PL)) return nullptr ; pSce->ToGlob( vFrames.back()) ; // inverto pSce->Invert() ; // aggiungo StmFts.AddSurfTriMesh( *pSce) ; } // recupero la supeficie risultante if ( ! StmFts.End()) return nullptr ; PtrOwner pSTM( GetBasicSurfTriMesh( StmFts.GetSurf())) ; if ( IsNull( pSTM)) return nullptr ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // se superficie risultante chiusa, verifico che la normale sia verso l'esterno double dVol ; if ( pSTM->GetVolume( dVol) && dVol < 0) pSTM->Invert() ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshSwept( const ICurve* pSect, const ICurve* pGuide, const Vector3d& vtAx, bool bCapEnds, double dLinTol) { // verifica parametri if ( pSect == nullptr || pGuide == nullptr) return nullptr ; bool bIsLine = false ; if ( pGuide->GetType() == CRV_LINE) bIsLine = true ; else { const CurveComposite* pCompo = GetBasicCurveComposite( pGuide) ; Point3d ptStart, ptEnd ; if ( pCompo != nullptr && pCompo->IsALine( 10 * EPS_SMALL, ptStart, ptEnd)) bIsLine = true ; } // se la guida è piana e il vettore di riferimento è non definito oppure non nullo Plane3d plGuide ; if ( pGuide->IsFlat( plGuide, bIsLine, 10 * EPS_SMALL) && ( ! vtAx.IsValid() || ! vtAx.IsSmall())) return GetSurfTriMeshSweptInPlane( pSect, pGuide, plGuide.GetVersN(), bCapEnds, dLinTol) ; // altrimenti swept 3d return GetSurfTriMeshSwept3d( pSect, pGuide, vtAx, bCapEnds, dLinTol) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshSwept( const ISurfFlatRegion* pSfrSect, const ICurve* pGuide, const Vector3d& vtAx, bool bCapEnds, double dLinTol) { // verifica dei parametri if ( pSfrSect == nullptr || pGuide == nullptr) return nullptr ; // predispongo collettore superfici componenti StmFromTriangleSoup StmSoup ; StmSoup.Start() ; // per ogni loop della superficie, creo una Swept for ( int nC = 0 ; nC < pSfrSect->GetChunkCount() ; ++ nC) { for ( int nL = 0 ; nL < pSfrSect->GetLoopCount( nC) ; ++ nL) { // recupero il loop PtrOwner pCrvLoop( pSfrSect->GetLoop( nC, nL)) ; if ( IsNull( pCrvLoop) || ! pCrvLoop->IsValid()) return nullptr ; // creo la Trimesh Swept PtrOwner pStmLoopSwept( GetSurfTriMeshSwept( pCrvLoop, pGuide, vtAx, false, dLinTol)) ; if ( IsNull( pStmLoopSwept) || ! pStmLoopSwept->IsValid()) return nullptr ; // aggiungo la Swept ricavata al risultato finale ( come triangoli ) StmSoup.AddSurfTriMesh( *pStmLoopSwept) ; } } // Recupero la superficie if ( ! StmSoup.End()) return nullptr ; PtrOwner pStmSwept( StmSoup.GetSurf()) ; if ( IsNull( pStmSwept)) return nullptr ; // se rischiesta chiusura... // Controllo solo che la guida non sia chiusa, la sezione derivando da una Flatregion è sempre chiusa if ( bCapEnds && ! pGuide->IsClosed()) { // recupero i loop all'inizio (dalla regione e apportunamente approssimati) POLYLINEVECTOR vPLi ; for ( int nC = 0 ; nC < pSfrSect->GetChunkCount() ; ++ nC) { for ( int nL = 0 ; nL < pSfrSect->GetLoopCount( nC) ; ++ nL) { vPLi.emplace_back() ; if ( ! pSfrSect->ApproxLoopWithLines( nC, nL, dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPLi.back())) return nullptr ; } } // creo il cap sull'inizio e lo attacco alla swept ( è già in posizione giusta) PtrOwner pSci( CreateBasicSurfTriMesh()) ; INTMATRIX vnPLIndMat ; if ( ! pSci->CreateByRegion( vPLi, vnPLIndMat)) return nullptr ; pStmSwept->DoSewing( *pSci) ; // recupero i loops alla fine POLYLINEVECTOR vPLe ; if ( ! pStmSwept->GetLoops( vPLe)) return nullptr ; // creo la superficie alla fine e la attacco PtrOwner pSce( CreateBasicSurfTriMesh()) ; vnPLIndMat.clear() ; if ( ! pSce->CreateByRegion( vPLe, vnPLIndMat)) return nullptr ; // attacco la superficie finale alla swept pSce->Invert() ; pStmSwept->DoSewing( *pSce) ; } // se superficie risultante chiusa, verifico che la normale sia verso l'esterno double dVol ; if ( pStmSwept->GetVolume( dVol) && dVol < 0) pStmSwept->Invert() ; return Release( pStmSwept) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshTransSwept( const ICurve* pSect, const ICurve* pGuide, bool bCapEnds, double dLinTol) { // verifica parametri if ( pSect == nullptr || pGuide == nullptr) return nullptr ; // determino se la sezione è chiusa bool bSectClosed = pSect->IsClosed() ; // punto iniziale della sezione e vettore a inizio guida Point3d ptStart ; if ( ! pSect->GetStartPoint( ptStart)) return nullptr ; Point3d ptGuide ; if ( ! pGuide->GetStartPoint( ptGuide)) return nullptr ; Vector3d vtDelta = ptStart - ptGuide ; // calcolo la polilinea che approssima la guida PolyLine PLG ; if ( ! pGuide->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PLG)) return nullptr ; // determino se la guida è chiusa bool bGuideClosed = PLG.IsClosed() ; // calcolo la superficie PtrOwner pSTM( CreateBasicSurfTriMesh()) ; if ( IsNull( pSTM)) return nullptr ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // superficie swept PtrOwner pPrevCrv ; Point3d ptP ; bool bPoint = PLG.GetFirstPoint( ptP) ; while ( bPoint) { // nuova curva PtrOwner pCurrCrv( pSect->Clone()) ; if ( IsNull( pCurrCrv)) return nullptr ; pCurrCrv->Translate( ptP - ptStart + vtDelta) ; // se esiste la curva precedente, costruisco la rigata (di tipo minima distanza) if ( ! IsNull( pPrevCrv)) { PtrOwner pSr( GetSurfTriMeshRuled( pPrevCrv, pCurrCrv, ISurfTriMesh::RLT_ISOPAR, dLinTol)) ; if ( IsNull( pSr)) return nullptr ; pSTM->DoSewing( *pSr) ; } // salvo la curva come prossima precedente pPrevCrv.Set( pCurrCrv) ; // prossimo punto bPoint = PLG.GetNextPoint( ptP) ; } // se richiesti caps e sezione chiusa e guida aperta if ( bCapEnds && bSectClosed && ! bGuideClosed) { // calcolo la polilinea che approssima la sezione PolyLine PLS ; if ( ! pSect->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PLS)) return nullptr ; // verifico che la sezione sia chiusa e piatta Plane3d plSect ; double dArea ; if ( PLS.IsClosedAndFlat( plSect, dArea, 100 * EPS_SMALL)) { // aggiungo il cap sull'inizio PtrOwner pSci( CreateBasicSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByFlatContour( PLS)) return nullptr ; pSci->Invert() ; Point3d ptGi ; PLG.GetFirstPoint( ptGi) ; pSci->Translate( ptGi - ptStart + vtDelta) ; pSTM->DoSewing( *pSci) ; // aggiungo il cap sulla fine PtrOwner pSce( CreateBasicSurfTriMesh()) ; if ( IsNull( pSce) || ! pSce->CreateByFlatContour( PLS)) return nullptr ; Point3d ptGe ; PLG.GetLastPoint( ptGe) ; pSce->Translate( ptGe - ptStart + vtDelta) ; pSTM->DoSewing( *pSce) ; } } // se superficie risultante chiusa, verifico che la normale sia verso l'esterno double dVol ; if ( pSTM->GetVolume( dVol) && dVol < 0) pSTM->Invert() ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshRuled( const Point3d& ptP, const ICurve* pCurve, double dLinTol) { // verifica parametri if ( &ptP == nullptr || pCurve == nullptr) return nullptr ; // calcolo la polilinea che approssima la curva PolyLine PL ; if ( ! pCurve->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL)) return nullptr ; // creo e setto la superficie trimesh PtrOwner pSTM( CreateBasicSurfTriMesh()) ; if ( IsNull( pSTM) || ! pSTM->CreateByPointCurve( ptP, PL)) return nullptr ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshRuled( const ICurve* pCurve1, const ICurve* pCurve2, int nType, double dLinTol) { // verifica parametri if ( pCurve1 == nullptr || pCurve2 == nullptr) return nullptr ; // calcolo la polilinea che approssima la prima curva PolyLine PL1 ; if ( ! pCurve1->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL1)) return nullptr ; // calcolo la polilinea che approssima la seconda curva PolyLine PL2 ; if ( ! pCurve2->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2)) return nullptr ; // creo e setto la superficie trimesh PtrOwner pSTM( CreateBasicSurfTriMesh()) ; if ( IsNull( pSTM) || ! pSTM->CreateByTwoCurves( PL1, PL2, nType)) return nullptr ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // restituisco la superficie return Release( pSTM) ; }