//---------------------------------------------------------------------------- // 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 "/EgtDev/Include/EGkOffsetCurve.h" #include "/EgtDev/Include/EGkStmFromCurves.h" #include "/EgtDev/Include/EGkStmFromTriangleSoup.h" #include "/EgtDev/Include/EgtPointerOwner.h" #include #include #include using namespace std ; //------------------------------------------------------------------------------- static bool CalcRegionPolyLines( const CICURVEPVECTOR& vpCurve, double dLinTol, POLYLINEVECTOR& vPL, Vector3d& vtN) ; //------------------------------------------------------------------------------- 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 ; Vector3d vtN ; if ( ! CalcRegionPolyLines( vpCurve, dLinTol, vPL, vtN)) return nullptr ; // creo e setto la superficie trimesh PtrOwner pSTM( CreateBasicSurfTriMesh()) ; if ( IsNull( pSTM) || ! pSTM->CreateByRegion( vPL)) 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 ; Vector3d vtN ; if ( ! CalcRegionPolyLines( vpCurve, dLinTol, vPL, vtN)) return nullptr ; // 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()) ; if ( IsNull( pSTM) || ! pSTM->CreateByRegion( vPL)) 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( CreateSurfTriMesh()) ; 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( CreateSurfTriMesh()) ; 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( CreateSurfTriMesh()) ; 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( CreateSurfTriMesh()) ; 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 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, false, 10 * EPS_SMALL)) return nullptr ; Vector3d vtNorm = plGuide.GetVersN() ; // determino se la guida è chiusa bool bGuideClosed = pGuide->IsClosed() ; // curve di offset OffsetCurve OffsCrvR ; if ( ! OffsCrvR.Make( pGuide, dDimH / 2, ICurve::OFF_FILLET) || OffsCrvR.GetCurveCount() == 0) return nullptr ; PtrOwner pCrvR( OffsCrvR.GetLongerCurve()) ; if ( IsNull( pCrvR)) return nullptr ; OffsetCurve OffsCrvL ; if ( ! OffsCrvL.Make( pGuide, -dDimH / 2, ICurve::OFF_FILLET) || OffsCrvL.GetCurveCount() == 0) return nullptr ; PtrOwner pCrvL( OffsCrvL.GetLongerCurve()) ; if ( IsNull( pCrvL)) return nullptr ; PtrOwner pCrvRb ; PtrOwner pCrvLb ; // costruisco le parti di superficie PtrOwner pSrfTop( GetSurfTriMeshRuled( pCrvR, pCrvL, ISurfTriMesh::RLT_MINDIST, dLinTol)) ; if ( IsNull( pSrfTop)) return nullptr ; PtrOwner pSrfBot( pSrfTop->Clone()) ; if ( IsNull( pSrfBot)) return nullptr ; pSrfBot->Translate( -dDimV * vtNorm) ; pSrfBot->Invert() ; PtrOwner pSrfRgt( GetSurfTriMeshByExtrusion( pCrvR, -dDimV * vtNorm, false, dLinTol)) ; if ( IsNull( pSrfRgt)) return nullptr ; pSrfRgt->Invert() ; PtrOwner pSrfLft( GetSurfTriMeshByExtrusion( pCrvL, -dDimV * vtNorm, false, dLinTol)) ; if ( IsNull( pSrfLft)) return nullptr ; // unisco le parti PtrOwner pSTM( Release( pSrfTop)) ; pSTM->DoSewing( *pSrfRgt) ; pSTM->DoSewing( *pSrfLft) ; pSTM->DoSewing( *pSrfBot) ; // salvo tolleranza lineare usata e imposto angolo per smooth pSTM->SetLinearTolerance( dLinTol) ; pSTM->SetSmoothAngle( 20) ; // se guida aperta e tappi piatti if ( ! bGuideClosed && nCapType == RSCAP_FLAT) { // 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( CreateSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByFlatContour( vPL[0])) return nullptr ; pSci->Invert() ; pSTM->DoSewing( *pSci) ; // aggiungo il cap sulla fine PtrOwner pSce( CreateSurfTriMesh()) ; if ( IsNull( pSce) || ! pSce->CreateByFlatContour( vPL[1])) return nullptr ; pSce->Invert() ; pSTM->DoSewing( *pSce) ; } // se altrimenti guida aperta e tappi arrotondati if ( ! bGuideClosed && ( nCapType == RSCAP_ROUND || nCapType == RSCAP_BEVEL)) { // step di rotazione per rispettare la tolleranza double dStepRotDeg = ( nCapType == RSCAP_BEVEL ? ANG_STRAIGHT / 4 : sqrt( 8 * dLinTol / dDimH) * RADTODEG) ; // aggiungo il cap sull'inizio Point3d ptStart ; pGuide->GetStartPoint( ptStart) ; Vector3d vtStart ; pGuide->GetStartDir( vtStart) ; vtStart.Rotate( vtNorm, 0, 1) ; PolyLine PLStart ; PLStart.AddUPoint( 0, ptStart) ; PLStart.AddUPoint( 1, ptStart + dDimH / 2 * vtStart) ; PLStart.AddUPoint( 2, ptStart + dDimH / 2 * vtStart - dDimV * vtNorm) ; PLStart.AddUPoint( 3, ptStart - dDimV * vtNorm) ; PtrOwner pSci( CreateSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByScrewing( PLStart, ptStart, vtNorm, ANG_STRAIGHT, dStepRotDeg, 0)) return nullptr ; pSci->Invert() ; pSTM->DoSewing( *pSci) ; // aggiungo il cap sulla fine Point3d ptEnd ; pGuide->GetEndPoint( ptEnd) ; Vector3d vtEnd ; pGuide->GetEndDir( vtEnd) ; vtEnd.Rotate( vtNorm, 0, -1) ; PolyLine PLEnd ; PLEnd.AddUPoint( 0, ptEnd) ; PLEnd.AddUPoint( 1, ptEnd + dDimH / 2 * vtEnd) ; PLEnd.AddUPoint( 2, ptEnd + dDimH / 2 * vtEnd - dDimV * vtNorm) ; PLEnd.AddUPoint( 3, ptEnd - dDimV * vtNorm) ; PtrOwner pSce( CreateSurfTriMesh()) ; if ( IsNull( pSce) || ! pSce->CreateByScrewing( PLEnd, ptEnd, vtNorm, ANG_STRAIGHT, dStepRotDeg, 0)) return nullptr ; pSce->Invert() ; pSTM->DoSewing( *pSce) ; } // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- 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, false, 10 * EPS_SMALL)) return nullptr ; // assegno la normale del piano Vector3d vtNorm = plGuide.GetVersN() ; // determino il punto centrale della sezione Point3d ptCen ; pGuide->GetStartPoint( ptCen) ; ptCen -= dDimV / 2 * vtNorm ; // determino se la guida è chiusa bool bGuideClosed = pGuide->IsClosed() ; // curve di offset const int NUM_OFFS = 4 ; OffsetCurve vOffsCrv[NUM_OFFS] ; double vDist[NUM_OFFS] = { dDimH / 2 - dBevelH, -dDimH / 2 + dBevelH, dDimH / 2, -dDimH / 2} ; future vRes[NUM_OFFS] ; for ( int i = 0 ; i < NUM_OFFS ; ++ i) vRes[i] = async( launch::async, &OffsetCurve::Make, &vOffsCrv[i], pGuide, vDist[i], ICurve::OFF_FILLET) ; bool bOk = true ; int nFin = 0 ; while ( nFin < NUM_OFFS) { for ( int i = 0 ; i < NUM_OFFS ; ++ i) { if ( vRes[i].valid() && vRes[i].wait_for( chrono::nanoseconds{ 1}) == future_status::ready) { bOk = vRes[i].get() && bOk ; ++ nFin ; } } } if ( ! bOk || vOffsCrv[0].GetCurveCount() == 0 || vOffsCrv[1].GetCurveCount() == 0 || vOffsCrv[2].GetCurveCount() == 0 || vOffsCrv[3].GetCurveCount() == 0) return nullptr ; PtrOwner pCrvR( vOffsCrv[0].GetLongerCurve()) ; if ( IsNull( pCrvR)) return nullptr ; PtrOwner pCrvL( vOffsCrv[1].GetLongerCurve()) ; if ( IsNull( pCrvL)) return nullptr ; PtrOwner pCrvRb( vOffsCrv[2].GetLongerCurve()) ; if ( IsNull( pCrvRb)) return nullptr ; pCrvRb->Translate( - dBevelV * vtNorm) ; PtrOwner pCrvLb( vOffsCrv[3].GetLongerCurve()) ; if ( IsNull( pCrvLb)) return nullptr ; pCrvLb->Translate( - dBevelV * vtNorm) ; // costruisco le parti di superficie PtrOwner pSrfTop( GetSurfTriMeshRuled( pCrvR, pCrvL, ISurfTriMesh::RLT_MINDIST, dLinTol)) ; if ( IsNull( pSrfTop)) return nullptr ; PtrOwner pSrfBot( pSrfTop->Clone()) ; if ( IsNull( pSrfBot)) return nullptr ; pSrfBot->Translate( -dDimV * vtNorm) ; pSrfBot->Invert() ; PtrOwner pSrfTopR( GetSurfTriMeshRuled( pCrvRb, pCrvR, ISurfTriMesh::RLT_MINDIST, dLinTol)) ; if ( IsNull( pSrfTopR)) return nullptr ; PtrOwner pSrfBotR( pSrfTopR->Clone()) ; if ( IsNull( pSrfBotR)) return nullptr ; pSrfBotR->Mirror( ptCen, vtNorm) ; PtrOwner pSrfTopL( GetSurfTriMeshRuled( pCrvL, pCrvLb, ISurfTriMesh::RLT_MINDIST, dLinTol)) ; if ( IsNull( pSrfTopL)) return nullptr ; PtrOwner pSrfBotL( pSrfTopL->Clone()) ; if ( IsNull( pSrfBotL)) return nullptr ; pSrfBotL->Mirror( ptCen, vtNorm) ; PtrOwner pSrfRgt( GetSurfTriMeshByExtrusion( pCrvRb, ( -dDimV + 2 * dBevelV) * vtNorm, false, dLinTol)) ; if ( IsNull( pSrfRgt)) return nullptr ; pSrfRgt->Invert() ; PtrOwner pSrfLft( GetSurfTriMeshByExtrusion( pCrvLb, ( -dDimV + 2 * dBevelV) * vtNorm, false, dLinTol)) ; if ( IsNull( pSrfLft)) return nullptr ; // unisco le parti int nBuckets = max( 4 * ( pSrfRgt->GetVertexSize() + pSrfLft->GetVertexSize()), 1000) ; StmFromTriangleSoup stmSoup ; if ( ! stmSoup.Start( nBuckets)) return nullptr ; stmSoup.AddSurfTriMesh( *pSrfTop) ; stmSoup.AddSurfTriMesh( *pSrfTopR) ; stmSoup.AddSurfTriMesh( *pSrfTopL) ; stmSoup.AddSurfTriMesh( *pSrfRgt) ; stmSoup.AddSurfTriMesh( *pSrfLft) ; stmSoup.AddSurfTriMesh( *pSrfBotR) ; stmSoup.AddSurfTriMesh( *pSrfBotL) ; stmSoup.AddSurfTriMesh( *pSrfBot) ; PtrOwner pSTM ; // se guida aperta e tappi piatti if ( ! bGuideClosed && nCapType == RSCAP_FLAT) { // completo unione e recupero la superficie risultante if ( ! stmSoup.End()) return nullptr ; pSTM.Set( stmSoup.GetSurf()) ; // preparo seconda zuppa di triangoli per inserire i tappi StmFromTriangleSoup stmCapSoup ; if ( ! stmCapSoup.Start( nBuckets)) return nullptr ; // 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, 50 * EPS_SMALL)) return nullptr ; if ( ! vPL[1].IsClosedAndFlat( plEnds, dArea, 50 * EPS_SMALL)) return nullptr ; // calcolo il cap sull'inizio PtrOwner pSci( CreateSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByFlatContour( vPL[0])) return nullptr ; pSci->Invert() ; // calcolo il cap sulla fine PtrOwner pSce( CreateSurfTriMesh()) ; if ( IsNull( pSce) || ! pSce->CreateByFlatContour( vPL[1])) return nullptr ; pSce->Invert() ; // cucio i tappi all'estrusione if ( ! pSTM->DoSewing( *pSci) || ! pSTM->DoSewing( *pSce)) return nullptr ; } // se altrimenti guida aperta e tappi arrotondati else if ( ! bGuideClosed && ( nCapType == RSCAP_ROUND || nCapType == RSCAP_BEVEL)) { // step di rotazione per rispettare il tipo o la tolleranza double dStepRotDeg = ( nCapType == RSCAP_BEVEL ? ANG_STRAIGHT / 4 : sqrt( 8 * dLinTol / dDimH) * RADTODEG) ; // aggiungo il cap sull'inizio Point3d ptStart ; pGuide->GetStartPoint( ptStart) ; Vector3d vtStart ; pGuide->GetStartDir( vtStart) ; vtStart.Rotate( vtNorm, 0, 1) ; PolyLine PLStart ; PLStart.AddUPoint( 0, ptStart) ; PLStart.AddUPoint( 1, ptStart + ( dDimH / 2 - dBevelH) * vtStart) ; PLStart.AddUPoint( 2, ptStart + dDimH / 2 * vtStart - dBevelV * vtNorm) ; PLStart.AddUPoint( 3, ptStart + dDimH / 2 * vtStart - ( dDimV - dBevelV) * vtNorm) ; PLStart.AddUPoint( 4, ptStart + ( dDimH / 2 - dBevelH) * vtStart - dDimV * vtNorm) ; PLStart.AddUPoint( 5, ptStart - dDimV * vtNorm) ; PtrOwner pSci( CreateSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByScrewing( PLStart, ptStart, vtNorm, ANG_STRAIGHT, dStepRotDeg, 0)) return nullptr ; pSci->Invert() ; stmSoup.AddSurfTriMesh( *pSci) ; // aggiungo il cap sulla fine Point3d ptEnd ; pGuide->GetEndPoint( ptEnd) ; Vector3d vtEnd ; pGuide->GetEndDir( vtEnd) ; vtEnd.Rotate( vtNorm, 0, -1) ; PolyLine PLEnd ; PLEnd.AddUPoint( 0, ptEnd) ; PLEnd.AddUPoint( 1, ptEnd + ( dDimH / 2 - dBevelH) * vtEnd) ; PLEnd.AddUPoint( 2, ptEnd + dDimH / 2 * vtEnd - dBevelV * vtNorm) ; PLEnd.AddUPoint( 3, ptEnd + dDimH / 2 * vtEnd - ( dDimV - dBevelV) * vtNorm) ; PLEnd.AddUPoint( 4, ptEnd + ( dDimH / 2 - dBevelH) * vtEnd - dDimV * vtNorm) ; PLEnd.AddUPoint( 5, ptEnd - dDimV * vtNorm) ; PtrOwner pSce( CreateSurfTriMesh()) ; if ( IsNull( pSce) || ! pSce->CreateByScrewing( PLEnd, ptEnd, vtNorm, ANG_STRAIGHT, dStepRotDeg, 0)) return nullptr ; pSce->Invert() ; stmSoup.AddSurfTriMesh( *pSce) ; // completo unione e recupero la superficie risultante if ( ! stmSoup.End()) return nullptr ; pSTM.Set( stmSoup.GetSurf()) ; } else { // completo unione e recupero la superficie risultante if ( ! stmSoup.End()) return nullptr ; pSTM.Set( stmSoup.GetSurf()) ; } // 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) ; } //------------------------------------------------------------------------------- ISurfTriMesh* GetSurfTriMeshSwept( const ICurve* pSect, const ICurve* pGuide, bool bCapEnds, double dLinTol) { // verifica parametri if ( pSect == nullptr || pGuide == nullptr) 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 ; // determino se la sezione è chiusa bool bSectClosed = PL.IsClosed() ; // verifico che la linea guida sia piana Plane3d plGuide ; if ( ! pGuide->IsFlat( plGuide, false, 10 * EPS_SMALL)) return nullptr ; // 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) ; Vector3d vtNorm = plGuide.GetVersN() ; frStart.Set( ptStart, -vtStart, vtStart ^ vtNorm) ; // porto la sezione in questo riferimento e ve la appiattisco if ( ! PL.ToLoc( frStart) || ! PL.Flatten()) return nullptr ; // calcolo la superficie PtrOwner pSTM( CreateSurfTriMesh()) ; if ( IsNull( pSTM)) return nullptr ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // superficie swept PtrOwner pPrevCrv ; Point3d ptP ; bool bPoint = PL.GetFirstPoint( ptP) ; while ( bPoint) { // nuova curva 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 ; pSTM->DoSewing( *pSr) ; } // salvo la curva come prossima precedente pPrevCrv.Set( pCurrCrv) ; // prossimo punto bPoint = PL.GetNextPoint( ptP) ; } // 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( CreateSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByFlatContour( PL)) return nullptr ; pSci->ToGlob( frStart) ; 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( CreateSurfTriMesh()) ; if ( IsNull( pSce) || ! pSce->CreateByFlatContour( PL)) return nullptr ; pSce->Invert() ; pSce->ToGlob( frEnd) ; 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( CreateSurfTriMesh()) ; 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( CreateSurfTriMesh()) ; if ( IsNull( pSTM) || ! pSTM->CreateByTwoCurves( PL1, PL2, nType)) return nullptr ; // salvo tolleranza lineare usata pSTM->SetLinearTolerance( dLinTol) ; // restituisco la superficie return Release( pSTM) ; } //------------------------------------------------------------------------------- bool CalcRegionPolyLines( const CICURVEPVECTOR& vpCurve, double dLinTol, POLYLINEVECTOR& vPL, Vector3d& vtN) { // calcolo le polilinee che approssimano le curve POLYLINEVECTOR vPLtmp ; vPLtmp.resize( vpCurve.size()) ; for ( int i = 0 ; i < int( vpCurve.size()) ; ++ i) { if ( ! vpCurve[i]->ApproxWithLines( dLinTol, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, vPLtmp[i])) return false ; } // ne calcolo l'area e genero un ordine in senso decrescente typedef pair INDAREA ; // coppia indice, area typedef vector INDAREAVECTOR ; // vettore di coppie indice, area INDAREAVECTOR vArea ; vArea.reserve( vPLtmp.size()) ; Vector3d vtN0 ; for ( int i = 0 ; i < int( vPLtmp.size()) ; ++ i) { // verifico chiusura, calcolo piano medio e area Plane3d plPlane ; double dArea ; if ( ! vPLtmp[i].IsClosedAndFlat( plPlane, dArea, 50 * EPS_SMALL)) return false ; // imposto la normale del primo contorno come riferimento if ( i == 0) vtN0 = plPlane.GetVersN() ; // verifico che le normali siano molto vicine if ( ! AreSameOrOppositeVectorApprox( plPlane.GetVersN(), vtN0)) return false ; // assegno il segno all'area secondo il verso della normale if ( ( plPlane.GetVersN() * vtN0) > 0) vArea.emplace_back( i, dArea) ; else vArea.emplace_back( i, - dArea) ; } sort( vArea.begin(), vArea.end(), []( const INDAREA& a, const INDAREA& b) { return ( abs( a.second) > abs( b.second)) ; }) ; // sposto le polilinee nel vettore da restituire secondo l'ordine vPL.clear() ; vPL.resize( vPLtmp.size()) ; bool bCCW = true ; for ( int i = 0 ; i < int( vPLtmp.size()) ; ++ i) { // scambio swap( vPL[i], vPLtmp[vArea[i].first]) ; // verifico senso di rotazione del contorno esterno if ( i == 0) bCCW = ( vArea[i].second > 0) ; // aggiusto gli altri contorni else { if ( ( bCCW && vArea[i].second > 0) || ( ! bCCW && vArea[i].second < 0)) vPL[i].Invert() ; } } // restituisco la normale positiva alla regione vtN = ( bCCW ? vtN0 : - vtN0) ; return true ; }