//---------------------------------------------------------------------------- // EgalTech 2015-2015 //---------------------------------------------------------------------------- // File : StmFromCurves.cpp Data : 01.02.15 Versione : 1.6b1 // Contenuto : Implementazione di funzioni per creazione di superfici Stm // a partire da curve, con diversi metodi. // // // Modifiche : 01.02.15 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "GeoConst.h" #include "CurveLine.h" #include "CurveArc.h" #include "CurveComposite.h" #include "SurfTriMesh.h" #include "Voronoi.h" #include "/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/EgtPointerOwner.h" #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( 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 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() ; // 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 ; // 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 int nBuckets = max( 2 * ( pSrfRgt->GetVertexSize() + pSrfLft->GetVertexSize()), 1000) ; StmFromTriangleSoup stmSoup ; if ( ! stmSoup.Start( nBuckets)) return nullptr ; stmSoup.AddSurfTriMesh( *pSrfTop) ; stmSoup.AddSurfTriMesh( *pSrfRgt) ; stmSoup.AddSurfTriMesh( *pSrfLft) ; 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()) ; // 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( CreateBasicSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByFlatContour( vPL[0])) return nullptr ; pSci->Invert() ; // calcolo il cap sulla fine PtrOwner pSce( CreateBasicSurfTriMesh()) ; 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 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) ; // se l'offset interno alla guida è chiuso if ( pCrvL->IsClosed()) { // calcolo l'angolo di rotazione per screwing faccia Top e Bottom Point3d ptRight ; pCrvR->GetEndPoint( ptRight) ; Point3d ptLeft ; pCrvR->GetStartPoint( ptLeft) ; Point3d ptJunction ; pCrvL->GetStartPoint( ptJunction) ; Point3d ptCenter = Media( ptRight, ptLeft) ; Vector3d vtRight = ptRight - ptCenter ; Vector3d vtLeft = ptLeft - ptCenter ; double dAng = ANG_STRAIGHT ; vtRight.GetAngle( vtLeft, dAng) ; vtRight.Normalize() ; PolyLine plLoop ; // creo il loop defininendo i punti plLoop.AddUPoint( 0, ptRight) ; // primo punto double dAngStep = ceil( dAng / dStepRotDeg) ; // aggiusto lo step for ( int i = 1 ; i < dAngStep ; ++ i) { Point3d ptRot = ptRight ; ptRot.Rotate( ptCenter, vtNorm, i * ( dAng / dAngStep)) ; plLoop.AddUPoint( i, ptRot) ; // punto intermedio sulla circonferenza } plLoop.AddUPoint( dAngStep ++, ptLeft) ; // ultimo punto plLoop.AddUPoint( dAngStep ++, ptJunction) ; // punto centrale sull'offset chiuso plLoop.AddUPoint( dAngStep, ptRight) ; // polyLine chiusa // superificie Top PtrOwner pStmTop( CreateSurfTriMesh()) ; if ( IsNull( pStmTop) || ! pStmTop->CreateByFlatContour( plLoop)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmTop) ; // superificie Bottom PtrOwner pStmBottom( CloneSurfTriMesh( pStmTop)) ; pStmBottom->Translate( - dDimV * vtNorm) ; pStmBottom->Invert() ; stmSoup.AddSurfTriMesh( *pStmBottom) ; // superificie perpendicolare // la PolyLine che utilizzo la posso ricavare da quella calcolata sopra plLoop.EraseLastUPoint() ; // apro il loop plLoop.EraseLastUPoint() ; // tolgo il punto di contatto sull'offset PtrOwner pStmPerp( CreateSurfTriMesh()) ; if ( IsNull( pStmPerp) || ! pStmPerp->CreateByExtrusion( plLoop, - vtNorm * dDimV) || ! pStmPerp->Invert()) return nullptr ; stmSoup.AddSurfTriMesh( *pStmPerp) ; } // se l'offset interno della guida è aperto... else { // aggiungo il cap sull'inizio Point3d ptStart ; pGuide->GetStartPoint( ptStart) ; // calcolo l'angolo di rotazione per screwing faccia Top e Bottom Point3d ptSLeft ; pCrvL->GetStartPoint( ptSLeft) ; Point3d ptSRight ; pCrvR->GetStartPoint( ptSRight) ; Vector3d vtLeft = ptSLeft - ptStart ; Vector3d vtRight = ptSRight - ptStart ; double dAng = ANG_STRAIGHT ; vtLeft.GetAngle( vtRight, dAng) ; vtLeft.Normalize() ; PolyLine plLoop ; // creo il loop defininendo i punti plLoop.AddUPoint( 0, ptSLeft) ; // primo punto double dAngStep = ceil( dAng / dStepRotDeg) ; for ( int i = 1 ; i < dAngStep ; ++ i) { Point3d ptRot = ptSLeft ; ptRot.Rotate( ptStart, vtNorm, i * ( dAng / dAngStep)) ; plLoop.AddUPoint( i, ptRot) ; } plLoop.AddUPoint( dAngStep, ptSRight) ; // ultimo punto plLoop.AddUPoint( dAngStep + 1, ptSLeft) ; // polyline chiusa // creo la superficie Top PtrOwner pStmTop_start( CreateSurfTriMesh()) ; if ( IsNull( pStmTop_start) || ! pStmTop_start->CreateByFlatContour( plLoop)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmTop_start) ; // superificie Bottom PtrOwner pStmBottom_start( CloneSurfTriMesh( pStmTop_start)) ; pStmBottom_start->Translate( - dDimV * vtNorm) ; pStmBottom_start->Invert() ; stmSoup.AddSurfTriMesh( *pStmBottom_start) ; // superificie perpendicolare // la PolyLine che utilizzo la posso ricavare da quella calcolata sopra plLoop.EraseLastUPoint() ; // apro il loop PtrOwner pStmPerp_start( CreateSurfTriMesh()) ; if ( IsNull( pStmPerp_start) || ! pStmPerp_start->CreateByExtrusion( plLoop, - vtNorm * dDimV) || ! pStmPerp_start->Invert()) return nullptr ; stmSoup.AddSurfTriMesh( *pStmPerp_start) ; // aggiungo il cap sulla fine Point3d ptEnd ; pGuide->GetEndPoint( ptEnd) ; // calcolo l'angolo di rotazione per screwing faccia Top e Bottom pCrvL->GetEndPoint( ptSLeft) ; pCrvR->GetEndPoint( ptSRight) ; vtLeft = ptSLeft - ptEnd ; vtRight = ptSRight - ptEnd ; dAng = ANG_STRAIGHT ; vtRight.GetAngle( vtLeft, dAng) ; vtRight.Normalize() ; plLoop.Clear() ; // creo il loop defininendo i punti plLoop.AddUPoint( 0, ptSRight) ; dAngStep = ceil( dAng / dStepRotDeg) ; // primo punto for ( int i = 1 ; i < dAngStep ; ++ i) { Point3d ptRot = ptSRight ; ptRot.Rotate( ptEnd, vtNorm, i * ( dAng / dAngStep)) ; plLoop.AddUPoint( i, ptRot) ; } plLoop.AddUPoint( dAngStep, ptSLeft) ; // ultimo punto plLoop.AddUPoint( dAngStep + 1, ptSRight) ; // polyline chiusa // creo la superficie Top PtrOwner pStmTop_end( CreateSurfTriMesh()) ; if ( IsNull( pStmTop_end) || ! pStmTop_end->CreateByFlatContour( plLoop)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmTop_end) ; // creo la superificie Bottom PtrOwner pStmBottom_end( CloneSurfTriMesh( pStmTop_end)) ; pStmBottom_end->Translate( - dDimV * vtNorm) ; pStmBottom_end->Invert() ; stmSoup.AddSurfTriMesh( *pStmBottom_end) ; // creo la superificie perpendicolare alla guida plLoop.EraseLastUPoint() ; // apro il loop PtrOwner pStmPerp_end( CreateSurfTriMesh()) ; if ( IsNull( pStmPerp_end) || ! pStmPerp_end->CreateByExtrusion( plLoop, - vtNorm * dDimV) || ! pStmPerp_end->Invert()) return nullptr ; stmSoup.AddSurfTriMesh( *pStmPerp_end) ; } // 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) ; } //------------------------------------------------------------------------------- static ISurfTriMesh* GetSurfTriMeshBeveledRectSwept( double dDimH, double dDimV, double dBevelH, double dBevelV, const ICurve* pGuide, int nCapType, double dLinTol) { // metodo di calcolo impostato da USE_VORONOI // 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() ; // 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} ; bool bOk = true ; if ( ! USE_VORONOI) { 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 ; } } } } else { // se Voronoi non è possibile calcolare gli offset di una stessa curva in parallelo for ( int i = 0 ; i < NUM_OFFS && bOk ; ++ i) bOk = vOffsCrv[i].Make( pGuide, vDist[i], ICurve::OFF_FILLET) ; } 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()) ; // 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( CreateBasicSurfTriMesh()) ; if ( IsNull( pSci) || ! pSci->CreateByFlatContour( vPL[0])) return nullptr ; pSci->Invert() ; // calcolo il cap sulla fine PtrOwner pSce( CreateBasicSurfTriMesh()) ; 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) ; // se l'offset interno della guida è chiuso... if ( pCrvL->IsClosed()) { // calcolo l'angolo di rotazione per screwing faccia Top e Bottom Point3d ptRight ; pCrvR->GetEndPoint( ptRight) ; Point3d ptLeft ; pCrvR->GetStartPoint( ptLeft) ; Point3d ptJunction ; pCrvL->GetStartPoint( ptJunction) ; Point3d ptCenter = Media( ptRight, ptLeft) ; Vector3d vtRight = ptRight - ptCenter ; Vector3d vtLeft = ptLeft - ptCenter ; double dAng = ANG_STRAIGHT ; vtRight.GetAngle( vtLeft, dAng) ; vtRight.Normalize() ; PolyLine plLoop ; // creo il loop defininendo i punti plLoop.AddUPoint( 0, ptRight) ; // primo punto double dAngStep = ceil( dAng / dStepRotDeg) ; // aggiusto lo step for ( int i = 1 ; i < dAngStep ; ++ i) { Point3d ptRot = ptRight ; ptRot.Rotate( ptCenter, vtNorm, i * ( dAng / dAngStep)) ; plLoop.AddUPoint( i, ptRot) ; // punto intermedio sulla circonferenza } plLoop.AddUPoint( dAngStep ++, ptLeft) ; // ultimo punto plLoop.AddUPoint( dAngStep ++, ptJunction) ; // punto centrale sull'offset chiuso plLoop.AddUPoint( dAngStep, ptRight) ; // polyLine chiusa // superificie Top PtrOwner pStmTop( CreateSurfTriMesh()) ; if ( IsNull( pStmTop) || ! pStmTop->CreateByFlatContour( plLoop)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmTop) ; // superificie Bottom PtrOwner pStmBottom( CloneSurfTriMesh( pStmTop)) ; if ( IsNull( pStmBottom) || ! pStmBottom->Mirror( ptCen, vtNorm)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmBottom) ; // calcolo l'angolo di rotazione per la faccia Top del bevel // NB. Questo angolo va ricalcolato, il bevel è inclinato rispetto alla normale della guida ptCenter.Translate( - dBevelV * vtNorm) ; Point3d ptbRight ; pCrvRb->GetEndPoint( ptbRight) ; Point3d ptbLeft ; pCrvRb->GetStartPoint( ptbLeft) ; Vector3d vtbLeft = ptbLeft - ptCenter ; Vector3d vtbRight = ptbRight - ptCenter ; dAng = ANG_STRAIGHT ; vtbRight.GetAngle( vtbLeft, dAng) ; vtbRight.Normalize() ; // la PolyLine che utilizzo la posso ricavare da quella calcolata sopra plLoop.EraseLastUPoint() ; // apro il loop plLoop.EraseLastUPoint() ; // tolgo il punto di contatto sull'offset // creo il loop defininendo i punti PolyLine plLoopB ; plLoopB.AddUPoint( 0, ptbRight) ; dAngStep = ceil( dAng / dStepRotDeg) ; for ( int i = 1 ; i < dAngStep ; ++ i) { Point3d ptRot = ptbRight ; ptRot.Rotate( ptCenter, vtNorm, i * ( dAng / dAngStep)) ; plLoopB.AddUPoint( i, ptRot) ; } plLoopB.AddUPoint( dAngStep, ptbLeft) ; // creo la superficie Top Bevel PtrOwner pStmbTop_start( CreateSurfTriMesh()) ; if ( IsNull( pStmbTop_start) || ! pStmbTop_start->CreateByTwoCurves( plLoop, plLoopB, ISurfTriMesh::RLT_MINDIST) || ! pStmbTop_start->Invert()) return nullptr ; stmSoup.AddSurfTriMesh( *pStmbTop_start) ; // creo la superificie Bottom Bevel PtrOwner pStmbBottom_start( CloneSurfTriMesh( pStmbTop_start)) ; if ( IsNull( pStmbBottom_start) || ! pStmbBottom_start->Mirror( ptCen, vtNorm)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmbBottom_start) ; // creo la superficie perpendicolare alla guida PolyLine plLoopB1 = plLoopB ; plLoopB1.Mirror( ptCen, vtNorm) ; PtrOwner pStmPerp( CreateSurfTriMesh()) ; if ( IsNull( pStmPerp) || ! pStmPerp->CreateByTwoCurves( plLoopB, plLoopB1, ISurfTriMesh::RLT_MINDIST) || ! pStmPerp->Invert()) return nullptr ; stmSoup.AddSurfTriMesh( *pStmPerp) ; } // se l'offset interno della guida è aperto... else { // aggiungo il cap sull'inizio Point3d ptStart ; pGuide->GetStartPoint( ptStart) ; // calcolo l'angolo di rotazione per screwing faccia Top e Bottom Point3d ptSLeft ; pCrvL->GetStartPoint( ptSLeft) ; Point3d ptSRight ; pCrvR->GetStartPoint( ptSRight) ; Vector3d vtLeft = ptSLeft - ptStart ; Vector3d vtRight = ptSRight - ptStart ; double dAng = ANG_STRAIGHT ; vtLeft.GetAngle( vtRight, dAng) ; vtLeft.Normalize() ; PolyLine plLoop ; // creo il loop defininendo i punti plLoop.AddUPoint( 0, ptSLeft) ; // primo punto double dAngStep = ceil( dAng / dStepRotDeg) ; for ( int i = 1 ; i < dAngStep ; ++ i) { Point3d ptRot = ptSLeft ; ptRot.Rotate( ptStart, vtNorm, i * ( dAng / dAngStep)) ; plLoop.AddUPoint( i, ptRot) ; } plLoop.AddUPoint( dAngStep, ptSRight) ; // ultimo punto plLoop.AddUPoint( dAngStep + 1, ptSLeft) ; // polyline chiusa // creo la superficie Top PtrOwner pStmTop_start( CreateSurfTriMesh()) ; if ( IsNull( pStmTop_start) || ! pStmTop_start->CreateByFlatContour( plLoop)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmTop_start) ; // creo la superificie Bottom PtrOwner pStmBottom_start( CloneSurfTriMesh( pStmTop_start)) ; if ( IsNull( pStmBottom_start) || ! pStmBottom_start->Mirror( ptCen, vtNorm)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmBottom_start) ; // calcolo l'angolo di rotazione per la faccia Top del bevel ptStart.Translate( - dBevelV * vtNorm) ; Point3d ptSbLeft ; pCrvLb->GetStartPoint( ptSbLeft) ; Point3d ptSbRight ; pCrvRb->GetStartPoint( ptSbRight) ; Vector3d vtbLeft = ptSbLeft - ptStart ; Vector3d vtbRight = ptSbRight - ptStart ; dAng = ANG_STRAIGHT ; vtbLeft.GetAngle( vtbRight, dAng) ; vtbLeft.Normalize() ; plLoop.EraseLastUPoint() ; // apro il loop // creo il loop defininendo i punti PolyLine plLoopB ; plLoopB.AddUPoint( 0, ptSbLeft) ; dAngStep = ceil( dAng / dStepRotDeg) ; // primo punto for ( int i = 1 ; i < dAngStep ; ++ i) { Point3d ptRot = ptSbLeft ; ptRot.Rotate( ptStart, vtNorm, i * ( dAng / dAngStep)) ; plLoopB.AddUPoint( i, ptRot) ; } plLoopB.AddUPoint( dAngStep, ptSbRight) ; // ultimo punto // creo la superficie Top Bevel PtrOwner pStmbTop_start( CreateSurfTriMesh()) ; if ( IsNull( pStmbTop_start) || ! pStmbTop_start->CreateByTwoCurves( plLoop, plLoopB, ISurfTriMesh::RLT_MINDIST) || ! pStmbTop_start->Invert()) return nullptr ; stmSoup.AddSurfTriMesh( *pStmbTop_start) ; // creo la superificie Bottom Bevel PtrOwner pStmbBottom_start( CloneSurfTriMesh( pStmbTop_start)) ; if ( IsNull( pStmbBottom_start) || ! pStmbBottom_start->Mirror( ptCen, vtNorm)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmbBottom_start) ; // creo la superficie perpendicolare alla guida PolyLine plLoopB1 = plLoopB ; plLoopB1.Mirror( ptCen, vtNorm) ; PtrOwner pStmPerp_start( CreateSurfTriMesh()) ; if ( IsNull( pStmPerp_start) || ! pStmPerp_start->CreateByTwoCurves( plLoopB, plLoopB1, ISurfTriMesh::RLT_MINDIST) || ! pStmPerp_start->Invert()) return nullptr ; stmSoup.AddSurfTriMesh( *pStmPerp_start) ; // aggiungo il cap sulla fine Point3d ptEnd ; pGuide->GetEndPoint( ptEnd) ; // calcolo l'angolo di rotazione per screwing faccia Top e Bottom pCrvL->GetEndPoint( ptSLeft) ; pCrvR->GetEndPoint( ptSRight) ; vtLeft = ptSLeft - ptEnd ; vtRight = ptSRight - ptEnd ; dAng = ANG_STRAIGHT ; vtRight.GetAngle( vtLeft, dAng) ; vtRight.Normalize() ; plLoop.Clear() ; // creo il loop defininendo i punti plLoop.AddUPoint( 0, ptSRight) ; dAngStep = ceil( dAng / dStepRotDeg) ; // primo punto for ( int i = 1 ; i < dAngStep ; ++ i) { Point3d ptRot = ptSRight ; ptRot.Rotate( ptEnd, vtNorm, i * ( dAng / dAngStep)) ; plLoop.AddUPoint( i, ptRot) ; } plLoop.AddUPoint( dAngStep, ptSLeft) ; // ultimo punto plLoop.AddUPoint( dAngStep + 1, ptSRight) ; // polyline chiusa // creo la superficie Top PtrOwner pStmTop_end( CreateSurfTriMesh()) ; if ( IsNull( pStmTop_end) || ! pStmTop_end->CreateByFlatContour( plLoop)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmTop_end) ; // creo la superificie Bottom PtrOwner pStmBottom_end( CreateSurfTriMesh()) ; if ( IsNull( pStmBottom_end) || ! pStmBottom_end->CopyFrom( pStmTop_end) || ! pStmBottom_end->Mirror( ptCen, vtNorm)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmBottom_end) ; // calcolo l'angolo di rotazione per la faccia Top del bevel ptEnd.Translate( - dBevelV * vtNorm) ; pCrvLb->GetEndPoint( ptSbLeft) ; pCrvRb->GetEndPoint( ptSbRight) ; vtbLeft = ptSbLeft - ptEnd ; vtbRight = ptSbRight - ptEnd ; dAng = ANG_STRAIGHT ; vtbRight.GetAngle( vtbLeft, dAng) ; vtbRight.Normalize() ; plLoop.EraseLastUPoint() ; // apro il loop // creo il loop defininendo i punti plLoopB.Clear() ; plLoopB.AddUPoint( 0, ptSbRight) ; dAngStep = ceil( dAng / dStepRotDeg) ; // primo punto for ( int i = 1 ; i < dAngStep ; ++ i) { Point3d ptRot = ptSbRight ; ptRot.Rotate( ptEnd, vtNorm, i * ( dAng / dAngStep)) ; plLoopB.AddUPoint( i, ptRot) ; } plLoopB.AddUPoint( dAngStep, ptSbLeft) ; // ultimo punto // creo la superficie Top Bevel PtrOwner pStmbTop_end( CreateSurfTriMesh()) ; if ( IsNull( pStmbTop_end) || ! pStmbTop_end->CreateByTwoCurves( plLoop, plLoopB, ISurfTriMesh::RLT_MINDIST) || ! pStmbTop_end->Invert()) return nullptr ; stmSoup.AddSurfTriMesh( *pStmbTop_end) ; // creo la superificie Bottom Bevel PtrOwner pStmbBottom_end( CloneSurfTriMesh( pStmbTop_end)) ; if ( IsNull( pStmbBottom_end) || ! pStmbBottom_end->Mirror( ptCen, vtNorm)) return nullptr ; stmSoup.AddSurfTriMesh( *pStmbBottom_end) ; // creo la superficie perpendicolare alla guida plLoopB1 = plLoopB ; plLoopB1.Mirror( ptCen, vtNorm) ; PtrOwner pStmPerp_end( CreateSurfTriMesh()) ; if ( IsNull( pStmPerp_end) || ! pStmPerp_end->CreateByTwoCurves( plLoopB, plLoopB1, ISurfTriMesh::RLT_MINDIST) || ! pStmPerp_end->Invert()) return nullptr ; stmSoup.AddSurfTriMesh( *pStmPerp_end) ; } // 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) ; } //------------------------------------------------------------------------------- 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.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 Plane3d plGuide ; if ( pGuide->IsFlat( plGuide, bIsLine, 10 * EPS_SMALL)) 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( CreateSurfTriMesh()) ; if ( ! pSci->CreateByRegion( vPLi)) 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( CreateSurfTriMesh()) ; if ( ! pSce->CreateByRegion( vPLe)) 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) ; } //------------------------------------------------------------------------------- bool CalcRegionPolyLines( const CICURVEPVECTOR& vpCurve, double dLinTol, POLYLINEVECTOR& vPL, Vector3d& vtN) { // se non ho curve, non faccio nulla if ( int( vpCurve.size()) == 0) return true ; // calcolo le polilinee che approssimano le curve 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 false ; } // ricavo versore normale Plane3d plPlane ; double dArea ; if ( ! vPL[0].IsClosedAndFlat( plPlane, dArea, 50 * EPS_SMALL)) return false ; vtN = plPlane.GetVersN() ; typedef std::pair INDAREA ; std::vector m_vArea ; // calcolo piano medio e area delle curve m_vArea.reserve( vPL.size()) ; for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { // calcolo piano medio e area Plane3d plPlane ; double dArea ; if ( ! vPL[i].IsClosedAndFlat( plPlane, dArea)) return false ; // verifico che le normali siano molto vicine if ( ! AreSameOrOppositeVectorApprox( plPlane.GetVersN(), vtN)) return false ; // assegno il segno all'area secondo il verso della normale if ( ( plPlane.GetVersN() * vtN) > 0) m_vArea.emplace_back( i, dArea) ; else m_vArea.emplace_back( i, - dArea) ; } // ordino in senso decrescente sull'area sort( m_vArea.begin(), m_vArea.end(), []( const INDAREA& a, const INDAREA& b) { return ( abs( a.second) > abs( b.second)) ; }) ; // dalle PolyLine passo alle curve nel piano XY ( prendo la prima come riferimento, trascuro le Z delle successive) Frame3d frRef ; frRef.Set( ORIG, vtN) ; if ( ! frRef.IsValid()) return false ; ICRVCOMPOPOVECTOR vCrvCompo( int( vPL.size())) ; for ( int i = 0 ; i < int( vPL.size()) ; ++ i) { vCrvCompo[i].Set( CreateCurveComposite()) ; vCrvCompo[i]->FromPolyLine( vPL[i]) ; vCrvCompo[i]->ToLoc( frRef) ; } // creo una matrice di interi ; ogni riga corrisponde ad un chunk, dove in posizione 0 c'è il loop esterno e nelle // successive i loop interni INTMATRIX vnPLIndMat ; // vettore di indici per ordinare le PolyLine INTVECTOR vPL_IndOrder ; vPL_IndOrder.resize( int( vPL.size())) ; for ( int i = 0 ; i < int( m_vArea.size()) ; ++ i) vPL_IndOrder[i] = m_vArea[i].first ; // aggiungo le diverse curve bool bFirstCrv ; Plane3d plExtLoop ; double dAreaExtLoop = 0. ; do { bFirstCrv = true ; for ( int i = 0 ; i < int( m_vArea.size()) ; ++ i) { // recupero indice di percorso e verifico sia valido int j = m_vArea[i].first ; if ( j < 0) continue ; // lo inserisco come esterno... if ( bFirstCrv) { vnPLIndMat.push_back({ j}) ; m_vArea[i].first = -1 ; dAreaExtLoop = m_vArea[i].second ; // inverto se necessario if ( m_vArea[i].second < EPS_SMALL) { vPL[j].Invert() ; vCrvCompo[j]->Invert() ; dAreaExtLoop *= -1 ; } bFirstCrv = false ; } // ... altrimenti verifico se il loop è interno o no else { // il loop è interno se è sia interno al loop esterno della riga di vnPLIndMat e allo stesso tempo // esterno a tutti i loop già inseriti nella riga attuale. // verifica rispetto loop esterno IntersCurveCurve ccInt( *vCrvCompo[vnPLIndMat.back().front()], *vCrvCompo[j]) ; CRVCVECTOR ccClass ; if ( ccInt.GetCrossOrOverlapIntersCount() > 0 || ! ccInt.GetCurveClassification( 1, EPS_SMALL, ccClass) || ccClass.empty() || ccClass[0].nClass != CRVC_IN) continue ; // verifica rispetto ai loop interni bool bOk = true ; for ( int k = 1 ; k < int( vnPLIndMat.back().size()) ; ++ k) { IntersCurveCurve ccInt2( *vCrvCompo[vnPLIndMat.back()[k]], *vCrvCompo[j]) ; CRVCVECTOR ccClass2 ; if ( ccInt2.GetCrossOrOverlapIntersCount() > 0 || ! ccInt2.GetCurveClassification( 1, EPS_SMALL, ccClass2) || ccClass2.empty() || ccClass2[0].nClass != CRVC_IN) { bOk = false ; break ; } } if ( bOk) { // inserisco nella matrice vnPLIndMat.back().push_back( j) ; m_vArea[i].first = -1 ; // inverto se necessario if ( m_vArea[i].second * dAreaExtLoop > 0.) { vPL[j].Invert() ; vCrvCompo[j]->Invert() ; } } } } } while ( ! bFirstCrv) ; // ordino le PolyLine per area POLYLINEVECTOR vPL_tmp ; for ( int i = 0 ; i < int( vPL_IndOrder.size()) ; ++ i) vPL_tmp.push_back( vPL[ vPL_IndOrder[i]]) ; swap( vPL, vPL_tmp) ; return true ; }