//---------------------------------------------------------------------------- // 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 "SurfFlatRegion.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 RS_FLATCAP_START = -1 ; static const int RS_FLATCAP_END = -2 ; static const int RS_LINK = -3 ; static const int RS_LINK_BACK = -4 ; static const int RS_FLATCAP_EXTRA = -5 ; //------------------------------------------------------------------------------- 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 || pCurrCrv->GetTempProp() == RS_FLATCAP_START || pCurrCrv->GetTempProp() == RS_FLATCAP_END) { 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 SurfFlatRegion* CalcSideSweptRegion( const ICurve* pOrigGuide, const Vector3d& vtNorm, double dOffs) { PtrOwner pSrf( CreateBasicSurfFlatRegion()) ; if ( IsNull( pSrf)) return nullptr ; PtrOwner pGuide( ConvertCurveToBasicComposite( pOrigGuide->Clone())) ; if ( IsNull( pGuide)) return nullptr ; // per ogni sottotratto della guida calcolo la regione spazzata dal segmento su quel tratto for ( int i = 0 ; i < pGuide->GetCurveCount() ; i ++) { // raccordo con curva precedente if ( i > 0) { Vector3d vtS ; pGuide->GetCurve( i-1)->GetEndDir( vtS) ; Vector3d vtE ; pGuide->GetCurve( i)->GetStartDir( vtE) ; double dAng ; bool bDet ; vtS.GetRotation( vtE, vtNorm, dAng, bDet) ; if ( dAng * dOffs > EPS_SMALL) { Point3d ptC ; pGuide->GetCurve( i)->GetStartPoint( ptC) ; vtS.Rotate( vtNorm, -90) ; vtE.Rotate( vtNorm, -90) ; PtrOwner pBorder( CreateBasicCurveComposite()) ; if ( IsNull( pBorder)) return nullptr ; pBorder->AddPoint( ptC) ; pBorder->AddLine( ptC + vtS * dOffs) ; PtrOwner pArc( CreateBasicCurveArc()) ; if ( IsNull( pArc) || ! pArc->SetC2PN( ptC, ptC + vtS * dOffs, ptC + vtE * dOffs, vtNorm)) pBorder->AddLine( ptC + vtE * dOffs) ; else pBorder->AddCurve( Release( pArc)) ; pBorder->Close() ; pBorder->Invert() ; PtrOwner pSrfFillet( CreateBasicSurfFlatRegion()) ; if ( IsNull( pSrfFillet)) return nullptr ; pSrfFillet->AddExtLoop( Release( pBorder)) ; if ( pSrfFillet->IsValid()) pSrf->Add( *pSrfFillet) ; } } PtrOwner pCurr( pGuide->GetCurve( i)->Clone()) ; pCurr->SetExtrusion( vtNorm) ; PtrOwner pCrvOffs( pCurr->Clone()) ; PtrOwner pBorder( CreateBasicCurveComposite()) ; if ( IsNull( pBorder)) return nullptr ; if ( pCrvOffs->SimpleOffset( dOffs)) { pBorder->AddCurve( Release( pCurr)) ; pCrvOffs->Invert() ; Point3d ptS ; pCrvOffs->GetStartPoint( ptS) ; pBorder->AddLine( ptS) ; pBorder->AddCurve( Release( pCrvOffs)) ; pBorder->Close() ; } else { // se arco che collassa costruisco il bordo del settore circolare CurveArc* pArc = GetBasicCurveArc( pCurr) ; if ( pArc == nullptr) return nullptr ; Point3d ptC = pArc->GetCenter() ; Point3d ptS ; pArc->GetStartPoint( ptS) ; pBorder->AddPoint( ptC) ; pBorder->AddLine( ptS) ; pBorder->AddCurve( Release( pCurr)) ; pBorder->Close() ; } PtrOwner pCurrSrf( CreateBasicSurfFlatRegion()) ; if ( IsNull( pCurrSrf) || ! pCurrSrf->AddExtLoop( Release( pBorder))) return nullptr ; if ( pSrf->IsValid()) { if ( ! pSrf->Add( *pCurrSrf)) return nullptr ; } else pSrf.Set( pCurrSrf) ; } return Release( pSrf) ; } //------------------------------------------------------------------------------- static ICurveComposite* CalcSegmentSweptRegionBorder( const ICurve* pGuide, const Vector3d& vtNorm, double dOffs) { // costruisco le regioni spazzate a destra e sinistra PtrOwner pSrfP( CalcSideSweptRegion( pGuide, vtNorm, dOffs)) ; PtrOwner pSrfM( CalcSideSweptRegion( pGuide, vtNorm, - dOffs)) ; if ( IsNull( pSrfP) || IsNull( pSrfM)) return nullptr ; pSrfP->Invert() ; if ( ! pSrfP->Add( *pSrfM)) return nullptr ; PtrOwner pBorder( ConvertCurveToBasicComposite( pSrfP->GetLoop( 0, 0))) ; return Release( pBorder) ; } //------------------------------------------------------------------------------- static bool AdjustSurfTriMeshRectSweptCaps( int nCapType, ICURVEPOVECTOR& vCrvs, const Vector3d& vtNorm, const ICurve* pGuide, double dOffs) { // se RSCAP_FLAT trasformo tutte le giunzioni relative agli estremi in linee controllando se necessario chiudere le curve // con opportuni tratti della regione spazzata. // 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 i flat caps sugli estremi della guida ( j = 0 start, j = 1 end) Point3d ptGuideS ; pGuide->GetStartPoint( ptGuideS) ; Vector3d vtGuideS ; pGuide->GetStartDir( vtGuideS) ; vtGuideS.Rotate( vtNorm, ANG_RIGHT) ; Point3d ptGuideE ; pGuide->GetEndPoint( ptGuideE) ; Vector3d vtGuideE ; pGuide->GetEndDir( vtGuideE) ; vtGuideE.Rotate( vtNorm, ANG_RIGHT) ; for ( int j = 0 ; j < 2 ; j ++) { Point3d ptP = ( j == 0 ? ptGuideS : ptGuideE) ; Vector3d vtDir = ( j == 0 ? vtGuideS : vtGuideE) ; Point3d pt1 = ptP + dOffs * vtDir ; Point3d pt2 = ptP - dOffs * vtDir ; // cerco su quali curve poggia la linea del cap int nCrv1 = -1 ; bool bStart1 = true ; int nCrv2 = -1 ; for ( int i = 0 ; i < ssize( vCrvs) ; i ++) { Point3d ptS ; vCrvs[i]->GetStartPoint( ptS) ; Point3d ptE ; vCrvs[i]->GetEndPoint( ptE) ; if ( AreSamePointEpsilon( ptS, pt1, 50 * EPS_SMALL)) { nCrv1 = i ; bStart1 = true ; } else if ( AreSamePointEpsilon( ptS, pt2, 50 * EPS_SMALL)) { nCrv2 = i ; } if ( AreSamePointEpsilon( ptE, pt1, 50 * EPS_SMALL)) { nCrv1 = i ; bStart1 = false ; } else if ( AreSamePointEpsilon( ptE, pt2, 50 * EPS_SMALL)) { nCrv2 = i ; } } int nFlatCapProp = ( j == 0 ? RS_FLATCAP_START : RS_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 CurveComposite* pCompo = GetBasicCurveComposite( 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) ; CurveComposite* pCompo = GetBasicCurveComposite( 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() ; } } } // recupero le curve aperte e cerco di chiuderle INTVECTOR vOpenIdx ; for ( int i = 0 ; i < ssize( vCrvs) ; i++) { if ( ! vCrvs[i]->IsClosed()) vOpenIdx.emplace_back( i) ; } if ( vOpenIdx.empty()) return true ; // ricavo il bordo della regione spazzata da un segmento che scorre lungo la guida con il suo punto medio PtrOwner pBorder( CalcSegmentSweptRegionBorder( pGuide, vtNorm, dOffs)) ; if ( IsNull( pBorder)) return false ; // ricavo le porzioni del bordo della regione spazzata che servono per collegare i tratti aperti Point3d ptNewStart ; vCrvs[vOpenIdx[0]]->GetStartPoint( ptNewStart) ; double dUStart = 0 ; pBorder->GetParamAtPoint( ptNewStart, dUStart) ; pBorder->ChangeStartPoint( dUStart) ; INTDBLVECTOR vTrims ; vTrims.reserve( 2 * vOpenIdx.size()) ; vTrims.emplace_back( 0, 0.0) ; for ( int i = 0 ; i < ssize( vOpenIdx) ; i ++) { double dParS, dParE ; if ( i > 0) { Point3d ptS ; vCrvs[vOpenIdx[i]]->GetStartPoint( ptS) ; if ( ! pBorder->GetParamAtPoint( ptS, dParS, 50 * EPS_SMALL)) return false ; vTrims.emplace_back( i, dParS) ; } Point3d ptE ; vCrvs[vOpenIdx[i]]->GetEndPoint( ptE) ; if ( ! pBorder->GetParamAtPoint( ptE, dParE, 50 * EPS_SMALL)) return false ; vTrims.emplace_back( i, dParE) ; } double dS, dE ; pBorder->GetDomain( dS, dE) ; vTrims.emplace_back( -1, dE) ; sort( vTrims.begin(), vTrims.end(), [](const INTDBL& a, const INTDBL& b) { return a.second < b.second ;}) ; // costruisco la curva chiusa risultante CurveComposite* pResult = GetBasicCurveComposite( vCrvs[vOpenIdx[0]]) ; if ( pResult == nullptr) return false ; for ( int i = 1 ; i < ssize( vTrims) ; i = i + 2) { // ricavo la porzione di bordo PtrOwner pBorderPart( ConvertCurveToBasicComposite( pBorder->CopyParamRange( vTrims[i].second, vTrims[i+1].second))) ; if ( IsNull( pBorderPart)) return false ; // identifico se ha tratti corrispondenti al flat cap for ( int j = 0 ; j < pBorderPart->GetCurveCount() ; j ++) { pBorderPart->SetCurveTempProp( j, RS_FLATCAP_EXTRA) ; if ( pBorderPart->GetCurve( j)->GetType() == CRV_LINE) { Vector3d vtS ; pBorderPart->GetCurve( j)->GetStartDir( vtS) ; if ( AreSameOrOppositeVectorApprox( vtS, vtGuideS)) { Point3d ptRef ; pBorderPart->GetCurve( j)->GetStartPoint( ptRef) ; Vector3d vtDist = ptRef - ptGuideS ; Vector3d vtOrtho = vtDist - vtDist * vtGuideS * vtGuideS ; if ( vtDist * vtGuideS < dOffs + EPS_SMALL && vtOrtho.SqLen() < 100 * SQ_EPS_SMALL) pBorderPart->SetCurveTempProp( j, RS_FLATCAP_START) ; } if ( AreSameOrOppositeVectorApprox( vtS, vtGuideE)) { Point3d ptRef ; pBorderPart->GetCurve( j)->GetStartPoint( ptRef) ; Vector3d vtDist = ptRef - ptGuideE ; Vector3d vtOrtho = vtDist - vtDist * vtGuideE * vtGuideE ; if ( vtDist * vtGuideE < dOffs + EPS_SMALL && vtOrtho.SqLen() < 100 * SQ_EPS_SMALL) pBorderPart->SetCurveTempProp( j, RS_FLATCAP_END) ; } } } // aggiungo la curva aperta successiva e la elimino dal vettore delle curve int nNextIdx = vTrims[i+1].first ; if ( nNextIdx != -1) { pResult->AddCurve( Release( vCrvs[vOpenIdx[nNextIdx]])) ; swap( vCrvs[vOpenIdx[nNextIdx]], vCrvs.back()) ; vCrvs.pop_back() ; } } } // 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 dOffsMin = 0.5 * dRadius - dBevelH ; double dOffsMax = 0.5 * dRadius ; ICURVEPOVECTOR vMedialAxis ; CalcCurveMedialAxis( *pGuide, vMedialAxis, WMAT_BOTHSIDES) ; // ricavo i punti di collasso nella regione tra i due offset per identificare se sono necessari dei bisettori // per ricreare al meglio la curva di autointersezione della superficie laterale del solido di swept PNTVECTOR vPtRef ; for ( int i = 0 ; i < ssize( vMedialAxis) ; i ++) { for ( int j = 0 ; j < 2 ; j ++) { // j = 0 start, j = 1 end double dPar = vMedialAxis[i]->GetTempParam( j) ; if ( dOffsMin + EPS_SMALL < dPar && dPar < dOffsMax + EPS_SMALL) { Point3d ptP ; if ( j == 0) vMedialAxis[i]->GetStartPoint( ptP) ; else vMedialAxis[i]->GetEndPoint( ptP) ; // traslazione per quota finale double dDelta = - dBevelV / dBevelH * ( dPar - dOffsMin) ; ptP.Translate( dDelta * vtNorm) ; // verifico se è già stato individuato bool bFound = false ; for ( int k = 0 ; k < ssize( vPtRef) ; k ++) { if ( AreSamePointApprox( vPtRef[k], ptP)) { bFound = true ; break ; } } if ( bFound) continue ; // traslo sul piano delle curve per trovare la distanza ptP.Translate( - ( dDelta + dBevelV) * vtNorm) ; DistPointCurve distPC( ptP, *vCrvs[0]) ; double dDist ; distPC.GetDist( dDist) ; if ( dDist > dBevelH + EPS_SMALL) { ptP.Translate( ( dBevelV + dDelta) * vtNorm) ; vPtRef.emplace_back( ptP) ; } } } } // se non serve collegamento perchè singola curva e non servono bisettori extra, non modifico la curva passata if ( ssize( vIdx) == 1 && vPtRef.empty()) return nullptr ; // conservo solo i tratti di medial axis compresi tra i due offset ( dOffsMin < <= dOffsMax) ICRVCOMPOPOVECTOR vMedialCrvs ; Voronoi* pVoronoi = GetCurveVoronoi( *pGuide) ; if ( pVoronoi == nullptr) return nullptr ; PNTVECTOR vFlatCapsPtRef ; double dTmp, dLast ; pGuide->GetDomain( dTmp, dLast) ; 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_ZERO || dPar2 < dOffsMin + EPS_ZERO) continue ; if ( abs( dPar1 - dOffsMax) < EPS_ZERO && abs( dPar2 - dOffsMax) > EPS_ZERO) continue ; // se flat caps ignoro i bisettori che hanno come sito un estremo della giuda if ( nCapType == RSCAP_FLAT) { int nBisector = vMedialAxis[i]->GetTempProp() ; int nCrv1, nSub1, nCrv2, nSub2, nTmp1, nTmp2 ; pVoronoi->GetBisectorSites( nBisector, nCrv1, nTmp1, nSub1, nCrv2, nTmp2, nSub2) ; if ( nSub1 == 0 || nSub2 == 0 || nSub1 == dLast || nSub2 == dLast) { // salvo i suoi estremi Point3d ptS ; vMedialAxis[i]->GetStartPoint( ptS) ; Point3d ptE ; vMedialAxis[i]->GetEndPoint( ptE) ; double dDeltaS = - dBevelV / dBevelH * ( dPar1 - dOffsMin) ; double dDeltaE = - dBevelV / dBevelH * ( dPar2 - dOffsMin) ; vFlatCapsPtRef.emplace_back( ptS + dDeltaS * vtNorm) ; vFlatCapsPtRef.emplace_back( ptE + dDeltaE * vtNorm) ; continue ; } } if ( dOffsMin + EPS_ZERO < dPar1 && dPar2 < dOffsMax + EPS_ZERO) { // tratto va conservato completamente vMedialCrvs.emplace_back( ConvertCurveToComposite( Release( vMedialAxis[i]))) ; } else { // determino solo la parte compresa dall'offset. Per maggior precisione calcolo il punto di trim esatto con Voronoi // senza affidarmi ai parametri salvati nel bisettore approssimato int nBisector = vMedialAxis[i]->GetTempProp() ; Point3d ptP ; if ( pVoronoi->GetBisectorPointAtParam( nBisector, dOffsMin, ptP)) continue ; double dTmp, dTrimParE ; vMedialAxis[i]->GetDomain( dTmp, dTrimParE) ; if ( pVoronoi->GetBisectorPointAtParam( nBisector, dOffsMax, ptP)) if ( ! vMedialAxis[i]->GetParamAtPoint( ptP, dTrimParE, 100 * EPS_SMALL)) return nullptr ; if ( dTrimParE < EPS_SMALL) continue ; // la curva si cancella completamente vMedialAxis[i]->TrimEndAtParam( dTrimParE) ; // aggiusto i temp param vMedialAxis[i]->SetTempParam( 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()->GetTempParam( 1) ; double dDelta = - dBevelV / dBevelH * ( dPar - dOffsMin) ; vMedialCrvs.back()->ModifyEnd( ptE + vtNorm * dDelta) ; } } // chain dei medialaxis if ( ! GetChainedCurves( vMedialCrvs, EPS_SMALL, true, false)) return nullptr ; // nel caso di flat caps i bisettori legati agli estremi devono essere collegati manualmente al bordo esterno if ( nCapType == RSCAP_FLAT) { for ( int i = 0 ; i < ssize( vMedialCrvs) ; i ++) { bool bConnect = false ; for ( int k = 0 ; k < 2 && ! bConnect ; k ++) { Point3d ptP ; if ( k == 0) vMedialCrvs[i]->GetStartPoint( ptP) ; else vMedialCrvs[i]->GetEndPoint( ptP) ; for ( int j = 0 ; j < ssize( vFlatCapsPtRef) ; j ++) { if ( AreSamePointApprox( ptP, vFlatCapsPtRef[j])) { CurveComposite* pCompo = GetBasicCurveComposite( vMedialCrvs[i]) ; if ( pCompo == nullptr) return nullptr ; DistPointCurve distPC( ptP, *vCrvs[0]) ; Point3d ptMinDist ; int nFlag ; distPC.GetMinDistPoint( 0, ptMinDist, nFlag) ; pCompo->AddLine( ptMinDist, k == 1) ; bConnect = true ; break ; } } } } } // creo catena double dChainTol = 50 * EPS_SMALL ; CurveComposite* pResult = nullptr ; for ( int i = 0 ; i < ssize( vMedialCrvs) ; i++) { // trovo le curve che collega Point3d ptS = P_INVALID ; vMedialCrvs[i]->GetStartPoint( ptS) ; Point3d ptE = P_INVALID ; 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 ; } // i parametri devono essere su un joint per costruzione dParS = round( dParS) ; dParE = round( dParE) ; // se non arriva su una curva va scartato if ( nS == -1 && nE == -1) continue ; // se arriva su una sola curva, verifico che non si innesti su un altro bisettore e che sia un tratto necessario // per la gestione di parti collassate controllando i ptRef if ( nS == -1 || nE == -1) { CurveComposite* pCompo = GetBasicCurveComposite( vCrvs[max(nS, nE)]) ; if ( pCompo == nullptr) return nullptr ; double dRefPar = min( max( dParS, dParE), pCompo->GetCurveCount() - 1.0) ; int nCrv = int( dRefPar + 0.5) ; int nRefProp = pCompo->GetCurve( nCrv)->GetTempProp() ; if ( nRefProp == RS_LINK || nRefProp == RS_LINK_BACK) continue ; bool bValid = false ; for ( int j = 0 ; j < ssize( vPtRef) && ! bValid ; j ++) { if ( vPtRef[j].IsValid()) bValid = vMedialCrvs[i]->IsPointOn( vPtRef[j]) ; } if ( ! bValid) continue ; // sistemo i parametri in modo che sia sempre definita la curva per lo start if ( nS == -1) { nS = nE ; dParS = dParE ; nE = -1 ; dParE = -1 ; vMedialCrvs[i]->Invert() ; } } // setto tutte le temp prop ad un valore fissato per poter identificare in seguito il tratto di bisettore for ( int nC = 0 ; nC < vMedialCrvs[i]->GetCurveCount() ; nC ++) vMedialCrvs[i]->SetCurveTempProp( nC, RS_LINK) ; pResult = GetBasicCurveComposite( 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) ; if ( nE != -1) { GetBasicCurveComposite( vCrvs[nE])->ChangeStartPoint( dParE) ; pResult->AddCurve( Release( vCrvs[nE]), true, dChainTol) ; } vMedialCrvs[i]->Invert() ; for ( int nC = 0 ; nC < vMedialCrvs[i]->GetCurveCount() ; nC ++) vMedialCrvs[i]->SetCurveTempProp( nC, RS_LINK_BACK) ; pResult->AddCurve( Release( vMedialCrvs[i]), true, dChainTol) ; if ( pResultEnd != nullptr) pResult->AddCurve( pResultEnd, true, dChainTol) ; // forzo chiusura pResult->Close() ; } // verifico di aver collegato tutte le curve INTVECTOR vResidualIdx ; for ( int i = 0 ; i < ssize( vIdx) ; i ++) { if ( !IsNull( vCrvs[vIdx[i]])) vResidualIdx.emplace_back( vIdx[i]) ; } if ( ssize( vResidualIdx) > 1) return nullptr ; // sistemo il punto di inizio della curva risultante su una curva derivante dalla guida e non su un tratto di bisettore if ( pResult != nullptr) { for ( int i = 0 ; i < pResult->GetCurveCount() ; i ++) { if ( pResult->GetCurve(i)->GetTempProp() > EPS_SMALL) { pResult->ChangeStartPoint( i + 0.5) ; break ; } } } return pResult ; } //------------------------------------------------------------------------------- static bool GetSplitParams( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, int nCrv2, bool bStart, const Vector3d& vtNorm, DBLVECTOR& vPar1, DBLVECTOR& vPar2) { int nFlag ; double dParMinDist ; Point3d ptP ; int nTmpProp = pCrv2->GetCurve( nCrv2)->GetTempProp() ; int nStart = int( floor( vPar1.back()) + 0.5) ; // se la curva di riferimento è un flat cap lo split è definito dal cap stesso if ( nTmpProp == RS_FLATCAP_START || nTmpProp == RS_FLATCAP_END) return true ; // se split per bisettore che torna su se stesso considero uno dei punti a min dist sull'altra curva else if ( nTmpProp == RS_LINK || nTmpProp == RS_LINK_BACK) { pCrv2->GetCurve( nCrv2)->GetEndPoint( ptP) ; DistPointCurve distPC( ptP, *pCrv1) ; distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ; vPar1.emplace_back( dParMinDist) ; vPar2.emplace_back( nCrv2 + 1) ; } // se deriva da una curva ben definita della giuda cerco la corrispondente dal lato corretto sull'altra curva else if ( nTmpProp != 0) { if ( bStart) pCrv2->GetCurve( nCrv2)->GetEndPoint( ptP) ; else pCrv2->GetCurve( nCrv2)->GetStartPoint( ptP) ; for ( int j = nStart ; j < pCrv1->GetCurveCount() ; j ++) { int nTmpProp1 = pCrv1->GetCurve( j)->GetTempProp() ; if ( nTmpProp1 == nTmpProp) { DistPointCurve distPC( ptP, *pCrv1->GetCurve( j)) ; int nSide = 0 ; distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; if ( nSide == MDS_RIGHT) { distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ; vPar1.emplace_back( dParMinDist + j) ; if ( bStart) vPar2.emplace_back( nCrv2 + 1) ; else vPar2.emplace_back( nCrv2) ; break ; } } } } // se deriva da raccordo devo controllare il più vicino else { pCrv2->GetCurve( nCrv2)->GetMidPoint( ptP) ; double dMinDist = INFINITO ; for ( int j = nStart ; j < pCrv1->GetCurveCount() ; j ++) { int nTmpProp1 = pCrv1->GetCurve( j)->GetTempProp() ; if ( nTmpProp1 == nTmpProp) { 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 - EPS_SMALL) { distPC.GetParamAtMinDistPoint( 0, dParMinDist, nFlag) ; dParMinDist += j ; dMinDist = dCurrDist ; } } } } vPar1.emplace_back( dParMinDist) ; vPar2.emplace_back( nCrv2 + 0.5) ; } return true ; } //------------------------------------------------------------------------------- static bool Associate( const ICurveComposite* pCrv1, const ICurveComposite* pCrv2, const Vector3d& vtNorm, DBLVECTOR& vPar1, DBLVECTOR& vPar2) { vPar1.emplace_back( 0) ; vPar2.emplace_back( 0) ; // segnalo parametro di split per le curve : // - in corrispondenza dei flat caps // - quando trovo l'inizio o la fine di un bisettore // - dove il bisettore ritorna su se stesso bool bBisector = false ; for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i++) { int nTmpProp = pCrv2->GetCurve( i)->GetTempProp() ; // se flat cap lo associo con il corrispondente if ( nTmpProp == RS_FLATCAP_START || nTmpProp == RS_FLATCAP_END) { for ( int j = 0 ; j < pCrv1->GetCurveCount() ; j ++) { int nOrigCrv1 = pCrv1->GetCurve( j)->GetTempProp() ; if ( nOrigCrv1 == nTmpProp) { vPar1.emplace_back( j) ; vPar1.emplace_back( j + 1) ; vPar2.emplace_back( i) ; vPar2.emplace_back( i + 1) ; break ; } } } // se bisettore o tratto aggiunto per chiudere flat cap else if ( nTmpProp == RS_LINK || nTmpProp == RS_LINK_BACK || nTmpProp == RS_FLATCAP_EXTRA) { if ( ! bBisector) { bBisector = true ; // inizio bisettore if ( i > 0) GetSplitParams( pCrv1, pCrv2, i-1, true, vtNorm, vPar1, vPar2) ; } else { // verfico se il bisettore sta tornando su se stesso ( ovvero si passa da link a link_back o viceversa) int nTmpPropPrev = pCrv2->GetCurve( i-1)->GetTempProp() ; if ( nTmpPropPrev != nTmpProp && ( nTmpPropPrev == RS_LINK || nTmpPropPrev == RS_LINK_BACK) && ( nTmpProp == RS_LINK || nTmpProp == RS_LINK_BACK)) GetSplitParams( pCrv1, pCrv2, i-1, true, vtNorm, vPar1, vPar2) ; } } // se fine del bisettore else { if ( bBisector) { bBisector = false ; GetSplitParams( pCrv1, pCrv2, i, false, vtNorm, vPar1, vPar2) ; } } } // parametro finale double dTmp, dE1, dE2 ; pCrv1->GetDomain( dTmp, dE1) ; pCrv2->GetDomain( dTmp, dE2) ; vPar1.emplace_back( dE1) ; vPar2.emplace_back( dE2) ; // verifico che i parametri calcolati siano ordinati for ( int i = 1 ; i < ssize( vPar1) ; i ++) if ( vPar1[i] < vPar1[i-1] - EPS_SMALL) return false ; return true ; } //------------------------------------------------------------------------------- static POLYLINEVECTOR SplitPolyLine( 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 = 1 ; 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 bool AdjustStartPoint( ICurve* pCrv1, ICurveComposite* pCrv2, int nCapType) { // se flat caps sistemo il punto di inizio sul cap start se presente if ( nCapType == RSCAP_FLAT) { ICurveComposite* pCompo1 = GetCurveComposite( pCrv1) ; if ( pCompo1 == nullptr) return false ; bool bFlatCap = false ; for ( int i = 0 ; i < pCompo1->GetCurveCount() ; i ++) { if ( pCompo1->GetCurve( i)->GetTempProp() == RS_FLATCAP_START) { pCompo1->ChangeStartPoint( i) ; bFlatCap = true ; break ; } } if ( bFlatCap) { for ( int i = 0 ; i < pCrv2->GetCurveCount() ; i ++) { if ( pCrv2->GetCurve( i)->GetTempProp() == RS_FLATCAP_START) { pCrv2->ChangeStartPoint( i) ; break ; } } return true ; } } Point3d ptStart ; pCrv1->GetStartPoint( ptStart) ; double dPar ; int nFlag ; DistPointCurve( ptStart, *pCrv2).GetParamAtMinDistPoint( 0, dPar, nFlag) ; pCrv2->ChangeStartPoint( dPar) ; return true ; } //------------------------------------------------------------------------------- static POLYLINEVECTOR RemoveBisectorsFromPolylines( const PolyLine& PL, const ICurveComposite* pCompo) { POLYLINEVECTOR vPL ; double dS, dE ; pCompo->GetDomain( dS, dE) ; double dPar ; Point3d ptP ; bool bFound = PL.GetFirstUPoint( &dPar, &ptP) ; PolyLine PLCurr ; PLCurr.AddUPoint( 0, ptP) ; // verifico se è un bisettore controllando la curva di cui è l'approssimazione int nCrv = int( floor( dPar) + 0.5) ; int nTempProp = pCompo->GetCurve( nCrv)->GetTempProp() ; bool bBisector = ( nTempProp == RS_LINK || nTempProp == RS_LINK_BACK) ; bFound = PL.GetNextUPoint( &dPar, &ptP) ; while( bFound) { // aggiungo il punto PLCurr.AddUPoint( 0, ptP) ; // verifico se ultimo punto if ( abs( dPar - dE) < EPS_SMALL) break ; // verifico se è un bisettore int nCrv = int( floor( dPar) + 0.5) ; int nTempProp = pCompo->GetCurve( nCrv)->GetTempProp() ; bool bBisectorCurr = ( nTempProp == RS_LINK || nTempProp == RS_LINK_BACK) ; // se passo da un bisettore ad una curva standard o viceversa salvo la polyline trovata fino a quel momento ( se non è bisettore) // e la resetto if ( bBisector != bBisectorCurr) { if ( ! bBisector) vPL.emplace_back( PLCurr) ; PLCurr.Clear() ; PLCurr.AddUPoint( 0, ptP) ; bBisector = bBisectorCurr ; } bFound = PL.GetNextUPoint( &dPar, &ptP) ; } // ultima curva if ( PLCurr.GetPointNbr() > 1 && ! bBisector) 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 ; } } for ( int i = 0 ; i < ssize( vFatCrvs2) ; i++) vFatCrvs2[i]->Translate( - dBevelV * vtNorm) ; // se necessario rimuovo le giunzioni per costruire le superfici laterali ICURVEPOVECTOR vFatCrvsOrig1 ; if ( ! bGuideClosed && nCapType == RSCAP_NONE) { // salvo le curve con le giunzioni per il calcolo della superficie top for ( int i = 0 ; i < ssize( vFatCrvs1) ; i++) vFatCrvsOrig1.emplace_back( vFatCrvs1[i]->Clone()) ; if ( ! RemoveFatCurveJunctions( vFatCrvs1) || ! RemoveFatCurveJunctions( vFatCrvs2)) return nullptr ; } // creo le parti di superficie StmFromTriangleSoup stmSoup ; if ( ! stmSoup.Start()) return nullptr ; // superfici laterali e verticali POLYLINEVECTOR vPL1( vFatCrvs1.size()) ; BOOLVECTOR bUsed( vFatCrvs2.size(), 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]) { CurveComposite* pCompo2 = GetBasicCurveComposite( vFatCrvs2[j]) ; if ( pCompo2 == nullptr) return nullptr ; for ( int k = 0 ; k < pCompo2->GetCurveCount() ; k ++) { int nProp = pCompo2->GetCurve( k)->GetTempProp() ; if ( nProp != RS_FLATCAP_START && nProp != RS_FLATCAP_END) { Point3d ptTest ; vFatCrvs2[j]->GetPointD1D2( k + 0.5, ICurve::FROM_MINUS, ptTest) ; DistPointCurve distPC( ptTest, *vFatCrvs1[i]) ; int nSide ; distPC.GetSideAtMinDistPoint( 0, vtNorm, nSide) ; if ( nSide == MDS_RIGHT) { bUsed[j] = true ; vAssociatedIdx.emplace_back( j) ; } break ; } } } } ISURFTMPOVECTOR vSurfLat ; ISURFTMPOVECTOR vSurfVert ; // a) se non ha associate allora collassa in una curva ad una quota opportuna if ( vAssociatedIdx.empty()) { double dLimitOffs ; if ( ! CalcCurveLimitOffset( *vFatCrvs1[i], dLimitOffs)) return nullptr ; // per un risultato migliore applico un offset leggermente più piccolo di quello limite e aggiungo una superficie di chiusura OffsetCurve OffsCrv ; OffsCrv.Make( vFatCrvs1[i], dLimitOffs - 10 * EPS_SMALL, ICurve::OFF_FILLET) ; PtrOwner pAssociatedCrv( ConvertCurveToComposite( OffsCrv.GetLongerCurve())) ; if ( IsNull( pAssociatedCrv)) return nullptr ; pAssociatedCrv->Translate( - dBevelV / dBevelH * dLimitOffs * vtNorm) ; AdjustStartPoint( vFatCrvs1[i], pAssociatedCrv, RSCAP_NONE) ; vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ; PolyLine PL2 ; pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; PtrOwner pSrfL( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfL)) return nullptr ; pSrfL->CreateByTwoCurves( vPL1[i], PL2, ISurfTriMesh::RLT_MINDIST) ; pSrfL->Invert() ; PtrOwner pSrfBase( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfBase)) return nullptr ; pSrfBase->CreateByFlatContour( PL2) ; pSrfBase->Invert() ; vSurfLat.emplace_back( Release( pSrfL)) ; vSurfLat.emplace_back( Release( pSrfBase)) ; } // b) se ha una o più curve associate else { // collego le curve con opportuni bisettori e se verifico se sono necessari dei bisettori per ricreare al meglio // la curva di autointersezione della superficie laterale del solido di swept ICurveComposite* pFatCrv2 = Connect( pGuide, vFatCrvs2, vAssociatedIdx, dDimH, dBevelH, dBevelV, vtNorm, nCapType) ; if ( ssize( vAssociatedIdx) == 1 && pFatCrv2 == nullptr) { // se aveva unica associata e non sono stati necessari bisettori extra è rigata tra le due curve ICurveComposite* pAssociatedCrv = GetCurveComposite( vFatCrvs2[vAssociatedIdx[0]]) ; if ( pAssociatedCrv == nullptr) return nullptr ; // per avere rigata ottimale modifico il punto di inizio AdjustStartPoint( vFatCrvs1[i], pAssociatedCrv, nCapType) ; // creo le polylines vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ; PolyLine PL2 ; pAssociatedCrv->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; PtrOwner pSrfL( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfL) || ! pSrfL->CreateByTwoCurves( vPL1[i], PL2, ISurfTriMesh::RLT_MINDIST)) return nullptr ; pSrfL->Invert() ; vSurfLat.emplace_back( Release( pSrfL)) ; PtrOwner pSrfV( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfV) || ! pSrfV->CreateByExtrusion( PL2, ( - dDimV + 2 * dBevelV) * vtNorm)) return nullptr ; pSrfV->Invert() ; vSurfVert.emplace_back( Release( pSrfV)) ; } else { if ( pFatCrv2 == nullptr) return nullptr ; // aggiusto il punto di inizio sulla curva 1 per avvicinarlo a quello ottimale già individuato dal connect per la curva 2 Point3d ptStart ; pFatCrv2->GetStartPoint( ptStart) ; double dPar ; int nFlag ; DistPointCurve( ptStart, *vFatCrvs1[i]).GetParamAtMinDistPoint( 0, dPar, nFlag) ; GetCurveComposite( vFatCrvs1[i])->ChangeStartPoint( dPar) ; // creo le polylines e le spezzo opportunamente per avere delle rigate ottimali ( i tratti derivanti dai bisettori // essendo sovrapposti per tratto di andata e di ritorno potrebbero creare problemi nell'identificazione dei punti // a minima distanza) vFatCrvs1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ; PolyLine PL2 ; pFatCrv2->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, PL2) ; DBLVECTOR vPar1, vPar2 ; if ( ! Associate( GetCurveComposite( vFatCrvs1[i]), pFatCrv2, vtNorm, vPar1, vPar2)) return nullptr ; POLYLINEVECTOR vPolyLine1 = SplitPolyLine( vPL1[i], vPar1) ; POLYLINEVECTOR vPolyLine2 = SplitPolyLine( PL2, vPar2) ; // visto che spezzo la polyline 1 per le superfici laterali, per evitare TJunctions con la superficie top aggiorno // la polyline 1 vPL1[i].Clear() ; for ( int j = 0 ; j < ssize( vPolyLine1) ; j ++) { PNTULIST lUPnts = vPolyLine1[j].GetUPointList() ; for ( POINTU& upt : lUPnts) vPL1[i].AddUPoint( upt.second, upt.first) ; } for ( int j = 0 ; j < ssize( vPolyLine1) ; j ++) { if ( vPolyLine1[j].GetPointNbr() > 1 && vPolyLine2[j].GetPointNbr() > 1) { PtrOwner pSrfL( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfL)) return nullptr ; pSrfL->CreateByTwoCurves( vPolyLine1[j], vPolyLine2[j], ISurfTriMesh::RLT_MINDIST) ; pSrfL->Invert() ; vSurfLat.emplace_back( Release( pSrfL)) ; } } // creazione delle superfici laterali verticali: per evitare problemi di TJunctions uso le stesse polylines // utilzzate per il calcolo delle superfici laterali, rimuovendo però i tratti derivanti dai bisettori // perchè corrispondono alle curve di autointersezione della superficie e quindi non generano pareti verticali for ( int j = 0 ; j < ssize( vPolyLine2) ; j ++) { POLYLINEVECTOR vPL = RemoveBisectorsFromPolylines( vPolyLine2[j], pFatCrv2) ; for ( int k = 0 ; k < ssize( vPL) ; k++) { PtrOwner pSrfV( CreateBasicSurfTriMesh()) ; if ( IsNull( pSrfV) || ! pSrfV->CreateByExtrusion( vPL[k], ( - dDimV + 2 * dBevelV) * vtNorm)) return nullptr ; pSrfV->Invert() ; vSurfVert.emplace_back( Release( pSrfV)) ; } } } } // inserisco le superfici verticali for ( int j = 0 ; j < ssize( vSurfVert) ; j ++) stmSoup.AddSurfTriMesh( *vSurfVert[j]) ; // inserisco le superfici laterali rigate e il loro mirror for ( int j = 0 ; j < ssize( vSurfLat) ; j ++) { stmSoup.AddSurfTriMesh( *vSurfLat[j]) ; vSurfLat[j]->Mirror( ptCen, vtNorm) ; stmSoup.AddSurfTriMesh( *vSurfLat[j]) ; } } // superficie top if ( ! vFatCrvsOrig1.empty()) { vPL1.clear() ; vPL1.resize( vFatCrvsOrig1.size()) ; for ( int i = 0 ; i < ssize( vFatCrvsOrig1) ; i++) vFatCrvsOrig1[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPL1[i]) ; } PtrOwner pSrfTop( CreateSurfTriMesh()) ; if ( IsNull( pSrfTop) || ! pSrfTop->CreateByPolygonWithHoles( vPL1)) return nullptr ; stmSoup.AddSurfTriMesh( *pSrfTop) ; // superficie bottom pSrfTop->Translate( -dDimV * vtNorm) ; pSrfTop->Invert() ; stmSoup.AddSurfTriMesh( *pSrfTop) ; if ( ! stmSoup.End()) return nullptr ; PtrOwner pSTM ; pSTM.Set( stmSoup.GetSurf()) ; 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) ; } //------------------------------------------------------------------------------- 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) ; }