//---------------------------------------------------------------------------- // EgalTech 2013-2013 //---------------------------------------------------------------------------- // File : CurveCompositeOffset.cpp Data : 07.12.14 Versione : 1.5l1 // Contenuto : Metodi per offset semplice della classe CCurveComposite. // // // // Modifiche : 07.12.14 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "CurveComposite.h" #include "CurveLine.h" #include "CurveArc.h" #include "GeoConst.h" #include "/EgtDev/Include/EGkCurve.h" #include "/EgtDev/Include/EGkIntersCurves.h" #include "/EgtDev/Include/EgtPointerOwner.h" using namespace std ; //---------------------------------------------------------------------------- static const int TP_IS_VERT_LINE = 1 ; //---------------------------------------------------------------------------- static bool IsVerticalLine( const ICurve* pCrv, double* pdLenZ) ; static bool VerifyAndAdjustSamePoint( ICurve* pCrv1, ICurve* pCrv2, CurveComposite& ccAux) ; static bool VerifyAndAdjustInternalAngle( ICurve* pCrv1, ICurve* pCrv2, CurveComposite& ccAux) ; static bool VerifyAndAdjustExternalAngle( ICurve* pCrv1, ICurve* pCrv2, double dDist, int nType, CurveComposite& ccAux) ; static bool AddFirstLastVerticalLines( CurveComposite& ccOffs, double dLenVertFirst, double dLenVertLast) ; static bool MediaInternalAngleDeltaZ( CurveComposite& ccOffs) ; //---------------------------------------------------------------------------- bool CurveComposite::SimpleOffsetXY( double dDist, int nType) { // creo una copia formata solo da rette e archi che giacciono nel piano XY (VtExtr è Z+) CurveComposite ccCopy ; if ( ! ccCopy.CopyFrom( *this) || ! ccCopy.ArcsBezierCurvesToArcsPerpExtr( 10 * EPS_SMALL, ANG_TOL_STD_DEG)) return false ; // determino se vietate linee verticali (perpendicolari a piano offset) bool bNoVertLine = (( nType & ICurve::OFF_NO_VERTICAL) != 0) ; // determino se richiesti angoli interni con deltaZ mediata bool bMedIntDz = (( nType & ICurve::OFF_MEDIA_INTDZ) != 0) ; // verifico se curva chiusa bool bClosed = ccCopy.IsClosed() && ( nType & ICurve::OFF_FORCE_OPEN) == 0 ; // creo la curva di offset CurveComposite ccOffs ; ccOffs.m_VtExtr = m_VtExtr ; ccOffs.m_dThick = m_dThick ; // eseguo l'offset ( estraggo entità dalla copia, le modifiche e le inserisco nell'offset) // lunghezze di eventuali linee verticali iniziale e finale double dLenVertFirst = 0 ; double dLenVertLast = 0 ; // recupero la prima curva PtrOwner pCrv1( ccCopy.RemoveFirstOrLastCurve( false)) ; if ( IsNull( pCrv1)) return false ; // se la curva è una linea verticale in Z, passo alla successiva if ( IsVerticalLine( pCrv1, &dLenVertFirst)) { if ( bNoVertLine) return false ; pCrv1.Set( ccCopy.RemoveFirstOrLastCurve( false)) ; } // offset della prima curva if ( IsNull( pCrv1)) return false ; if ( ! pCrv1->SimpleOffset( dDist, nType)) return false ; ICurve* pCrvPrev = pCrv1 ; if ( ! ccOffs.AddSimpleCurve( Release( pCrv1))) return false ; // curve successive PtrOwner pCrv2( ccCopy.RemoveFirstOrLastCurve( false)) ; while ( ! IsNull( pCrv2)) { // se la curva è una linea verticale in Z, passo alla successiva if ( IsVerticalLine( pCrv2, &dLenVertLast)) { if ( bNoVertLine) return false ; pCrv2.Set( ccCopy.RemoveFirstOrLastCurve( false)) ; continue ; } // eseguo semplice offset if ( ! pCrv2->SimpleOffset( dDist, nType)) return false ; // verifico relazione con la curva precedente e aggiungo eventuali curve intermedie CurveComposite ccTemp ; if ( VerifyAndAdjustSamePoint( pCrvPrev, pCrv2, ccTemp) || VerifyAndAdjustInternalAngle( pCrvPrev, pCrv2, ccTemp) || VerifyAndAdjustExternalAngle( pCrvPrev, pCrv2, dDist, nType, ccTemp)) { if ( ccTemp.GetCurveCount() > 0 && ! ccOffs.AddCurveByRelocate( ccTemp)) return false ; } // nessun caso è andato a buon fine, errore else return false ; // aggiorno curva precedente pCrvPrev = pCrv2 ; // inserisco nell'offset if ( ! ccOffs.AddSimpleCurve( Release( pCrv2))) return false ; // passo alla curva successiva pCrv2.Set( ccCopy.RemoveFirstOrLastCurve( false)) ; } // se originale chiuso, devo confrontare anche ultima e prima curva if ( bClosed && ccOffs.GetCurveCount() > 0) { // la curva successiva ora è la prima dell'offset ICurve* pCrvNext = ccOffs.m_CrvSmplS.front() ; // verifico relazione con la curva precedente e aggiungo eventuali curve intermedie CurveComposite ccTemp ; if ( VerifyAndAdjustSamePoint( pCrvPrev, pCrvNext, ccTemp) || VerifyAndAdjustInternalAngle( pCrvPrev, pCrvNext, ccTemp) || VerifyAndAdjustExternalAngle( pCrvPrev, pCrvNext, dDist, nType, ccTemp)) { int nCrvCount = ccTemp.GetCurveCount() ; if ( nCrvCount > 0 && ! ccOffs.AddCurveByRelocate( ccTemp)) return false ; if ( nCrvCount > 1) { ccOffs.ChangeStartPoint( double( ccOffs.m_CrvSmplS.size()) - 1) ; } } // nessun caso è andato a buon fine, errore else return false ; } // se originale aperto, devo rimettere eventuali lineee verticali iniziale e finale if ( ! bClosed && ccOffs.GetCurveCount() > 0 && ! AddFirstLastVerticalLines( ccOffs, dLenVertFirst, dLenVertLast)) return false ; // se richiesti angoli interni con Z mediata, verifico e sistemo if ( bMedIntDz && ! MediaInternalAngleDeltaZ( ccOffs)) return false ; // sposto le curve dall'offset alla composita attuale return RelocateFrom( ccOffs) ; } //---------------------------------------------------------------------------- bool IsVerticalLine( const ICurve* pCrv, double* pdLenZ) { // verifico sia una linea const CurveLine* pLine = GetBasicCurveLine( pCrv) ; if ( pLine == nullptr) return false ; // verifico sia diretta come l'asse Z if ( AreSamePointXYApprox( pLine->GetStart(), pLine->GetEnd())) { if ( pdLenZ != nullptr) *pdLenZ = pLine->GetEnd().z - pLine->GetStart().z ; return true ; } else { if ( pdLenZ != nullptr) *pdLenZ = 0 ; return false ; } } //---------------------------------------------------------------------------- bool VerifyAndAdjustSamePoint( ICurve* pCrv1, ICurve* pCrv2, CurveComposite& ccAux) { // verifica dei puntatori if ( pCrv1 == nullptr || pCrv2 == nullptr || &ccAux == nullptr) return false ; // pulisco la curva ausiliaria ccAux.Clear() ; // calcolo dei punti estremi (finale per prima curva, iniziale per seconda) Point3d ptP1, ptP2 ; if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2)) return false ; // verifica distanza tra estremi if ( ! AreSamePointXYEpsilon( ptP1, ptP2, 10 * EPS_SMALL)) return false ; // se punti quasi coincidenti in Z if ( abs( ptP1.z - ptP2.z) < 10 * EPS_SMALL) { // se coincidono esattamente, va bene così if ( AreSamePointExact( ptP1, ptP2)) return true ; // sono in tolleranza, ma devo ricongiungere gli estremi Point3d ptMid = 0.5 * ( ptP1 + ptP2) ; return ( pCrv1->ModifyEnd( ptMid) && pCrv2->ModifyStart( ptMid)) ; } // devo allineare i punti in XY e aggiungere un tratto verticale else { // facio coincidere gli estremi in XY Point3d ptP1a = 0.5 * ( ptP1 + ptP2) ; ptP1a.z = ptP1.z ; Point3d ptP2a = 0.5 * ( ptP1 + ptP2) ; ptP2a.z = ptP2.z ; if ( ! pCrv1->ModifyEnd( ptP1a) || ! pCrv2->ModifyStart( ptP2a)) return false ; // aggiungo una retta in Z PtrOwner pCrv( CreateBasicCurveLine()) ; if ( IsNull( pCrv) || ! pCrv->Set( ptP1a, ptP2a)) return false ; return ccAux.AddCurve( Release( pCrv)) ; } } //---------------------------------------------------------------------------- bool VerifyAndAdjustInternalAngle( ICurve* pCrv1, ICurve* pCrv2, CurveComposite& ccAux) { // verifica dei puntatori if ( pCrv1 == nullptr || pCrv2 == nullptr || &ccAux == nullptr) return false ; // pulisco la curva ausiliaria ccAux.Clear() ; // calcolo l'intersezione tra le due curve IntersCurveCurve intCC( *pCrv1, *pCrv2) ; if ( intCC.GetIntersCount() == 0) return false ; // prendo l'intersezione più vicina al punto medio tra gli estremi delle curve Point3d ptP1, ptP2 ; if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2)) return false ; Point3d ptMid = 0.5 * ( ptP1 + ptP2) ; Point3d ptNew1, ptNew2 ; if ( ! intCC.GetIntersPointNearTo( 0, ptMid, ptNew1) || ! intCC.GetIntersPointNearTo( 1, ptMid, ptNew2)) return false ; // se punti coincidenti in Z if ( abs( ptNew1.z - ptNew2.z) < EPS_SMALL) { // modifico le due curve sul punto medio Point3d ptNew = 0.5 * ( ptNew1 + ptNew2) ; return ( pCrv1->ModifyEnd( ptNew) && pCrv2->ModifyStart( ptNew)) ; } // altrimenti else { // modifico le due curve sui rispettivi punti di intersezione if ( ! pCrv1->ModifyEnd( ptNew1) || ! pCrv2->ModifyStart( ptNew2)) return false ; // aggiungo una retta in Z PtrOwner pCrv( CreateBasicCurveLine()) ; if ( IsNull( pCrv) || ! pCrv->Set( ptNew1, ptNew2)) return false ; pCrv->SetTempProp( TP_IS_VERT_LINE) ; return ccAux.AddCurve( Release( pCrv)) ; } } //---------------------------------------------------------------------------- bool VerifyAndAdjustExternalAngle( ICurve* pCrv1, ICurve* pCrv2, double dDist, int nType, CurveComposite& ccAux) { // verifica dei puntatori if ( pCrv1 == nullptr || pCrv2 == nullptr || &ccAux == nullptr) return false ; // pulisco la curva ausiliaria ccAux.Clear() ; // elimino dal tipo le parti estranee all'angolo esterno nType &= ( ICurve::OFF_FILLET | ICurve::OFF_CHAMFER | ICurve::OFF_EXTEND) ; // calcolo direzioni tangenti sull'estremo in comune Vector3d vtDir1, vtDir2 ; if ( ! pCrv1->GetEndDir( vtDir1) || ! pCrv2->GetStartDir( vtDir2)) return false ; // verifico se presente componente in Z bool bDZ1 = abs( vtDir1.z) > EPS_ANG_ZERO ; bool bDZ2 = abs( vtDir2.z) > EPS_ANG_ZERO ; // le porto nel piano XY vtDir1.z = 0 ; vtDir2.z = 0 ; if ( ! vtDir1.Normalize() || ! vtDir2.Normalize()) return false ; // calcolo l'angolo di rotazione dalla prima direzione alla seconda double dAngDeg ; if ( ! vtDir1.GetAngleXY( vtDir2, dAngDeg)) return false ; // se vicino all'angolo piatto, si devono ricalcolare usando le curve originali e spostandosi un poco if ( abs( dAngDeg) > ( ANG_STRAIGHT - EPS_ANG_SMALL)) { // ritorno alle curve originali PtrOwner pCrvOri1( pCrv1->Clone()) ; PtrOwner pCrvOri2( pCrv2->Clone()) ; if ( IsNull( pCrvOri1) || ! pCrvOri1->SimpleOffset( - dDist, nType)) return false ; if ( IsNull( pCrvOri2) || ! pCrvOri2->SimpleOffset( - dDist, nType)) return false ; // eseguo calcolo spostato su curve originali double dU1 = 1 ; double dU2 = 0 ; Point3d ptDummy ; Vector3d vtDir1b ; Vector3d vtDir2b ; if ( ! MoveParamToAvoidTg( dU1, ICurve::FROM_MINUS, *pCrvOri1) || ! pCrvOri1->GetPointTang( dU1, ICurve::FROM_MINUS, ptDummy, vtDir1b) || ! MoveParamToAvoidTg( dU2, ICurve::FROM_PLUS, *pCrvOri2) || ! pCrvOri2->GetPointTang( dU2, ICurve::FROM_PLUS, ptDummy, vtDir2b)) return false ; if ( ! vtDir1b.GetAngleXY( vtDir2b, dAngDeg)) return false ; } // verifico sia angolo esterno (accetto se entità quasi esattamente sovrapposto) if ( abs( dAngDeg) < ( ANG_STRAIGHT - 10 * EPS_ANG_ZERO) && ( ( dDist < 0 && dAngDeg > 0) || ( dDist > 0 && dAngDeg < 0))) return false ; // se l'angolo esterno supera il retto, offset extend diventa offset chamfer if ( nType == ICurve::OFF_EXTEND && abs( dAngDeg) > ANG_RIGHT + EPS_ANG_SMALL) nType = ICurve::OFF_CHAMFER ; // se angolo esterno molto piccolo, semplifico tutto const double SMALL_EXT_ANG = 1.0 ; bool bAngSmall = ( abs( dAngDeg) < SMALL_EXT_ANG) ; if ( bAngSmall) nType = ICurve::OFF_EXTEND ; // congiungo le due curve switch ( nType) { case ICurve::OFF_FILLET : { Point3d ptP1, ptP2 ; if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2)) return false ; double dAngStart ; vtDir1.ToSpherical( nullptr, nullptr, &dAngStart) ; PtrOwner pCrv( CreateBasicCurveArc()) ; if ( IsNull( pCrv) || ! pCrv->Set2PD( ptP1, ptP2, dAngStart)) return false ; // restituisco la curva return ccAux.AddCurve( Release( pCrv)) ; } break ; case ICurve::OFF_CHAMFER : { // lunghezza aggiuntiva in tangenza double dLen = abs( dDist) * tan( abs( dAngDeg) / 4 * DEGTORAD) ; // punti di costruzione smusso Point3d ptP1, ptP1a, ptP2a, ptP2 ; if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2)) return false ; ptP1a = ptP1 + vtDir1 * dLen ; ptP2a = ptP2 - vtDir2 * dLen ; // se sull'angolo c'era un dislivello (linea verticale eliminata) double dDeltaZ = ptP2.z - ptP1.z ; if ( abs( dDeltaZ) > EPS_SMALL) { ptP1a.z += dDeltaZ / 4 ; ptP2a.z -= dDeltaZ / 4 ; bDZ1 = true ; bDZ2 = true ; } // se prima c'è linea senza DZ posso allungarla if ( pCrv1->GetType() == CRV_LINE && ! bDZ1) pCrv1->ModifyEnd( ptP1a) ; // altrimenti, devo aggiungere una nuova linea else { PtrOwner pCrv( CreateBasicCurveLine()) ; if ( IsNull( pCrv) || ! pCrv->Set( ptP1, ptP1a)) return false ; if ( ! ccAux.AddCurve( Release( pCrv))) return false ; } // tratto intermedio { PtrOwner pCrv( CreateBasicCurveLine()) ; if ( IsNull( pCrv) || ! pCrv->Set( ptP1a, ptP2a)) return false ; if ( ! ccAux.AddCurve( Release( pCrv))) return false ; } // se dopo c'è linea senza DZ posso allungarla if ( pCrv2->GetType() == CRV_LINE && ! bDZ2) pCrv2->ModifyStart( ptP2a) ; // altrimenti, devo aggiungere una nuova linea else { PtrOwner pCrv( CreateBasicCurveLine()) ; if ( IsNull( pCrv) || ! pCrv->Set( ptP2a, ptP2)) return false ; if ( ! ccAux.AddCurve( Release( pCrv))) return false ; } return true ; } break ; case ICurve::OFF_EXTEND : { // lunghezza aggiuntiva in tangenza double dLen = abs( dDist) * tan( abs( dAngDeg) / 2 * DEGTORAD) ; // punti di costruzione estensione Point3d ptP1, ptPc, ptP2 ; if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2)) return false ; ptPc = ptP1 + vtDir1 * dLen ; // se sull'angolo c'era un dislivello (linea verticale eliminata) double dDeltaZ = ptP2.z - ptP1.z ; if ( abs( dDeltaZ) > EPS_SMALL) { ptPc.z += dDeltaZ / 2 ; bDZ1 = true ; bDZ2 = true ; } // se prima c'è linea senza DZ posso allungarla o angolo molto piccolo if ( ( pCrv1->GetType() == CRV_LINE && ! bDZ1) || bAngSmall) pCrv1->ModifyEnd( ptPc) ; // altrimenti, devo aggiungere una nuova linea else { PtrOwner pCrv( CreateBasicCurveLine()) ; if ( IsNull( pCrv) || ! pCrv->Set( ptP1, ptPc)) return false ; if ( ! ccAux.AddCurve( Release( pCrv))) return false ; } // se dopo c'è linea senza DZ posso allungarla o angolo molto piccolo if ( ( pCrv2->GetType() == CRV_LINE && ! bDZ2) || bAngSmall) pCrv2->ModifyStart( ptPc) ; // altrimenti, devo aggiungere una nuova linea else { PtrOwner pCrv( CreateBasicCurveLine()) ; if ( IsNull( pCrv) || ! pCrv->Set( ptPc, ptP2)) return false ; if ( ! ccAux.AddCurve( Release( pCrv))) return false ; } return true ; } } return false ; } //---------------------------------------------------------------------------- bool AddFirstLastVerticalLines( CurveComposite& ccOffs, double dLenVertFirst, double dLenVertLast) { // se richiesto inserimento prima retta verticale if ( abs( dLenVertFirst) > EPS_SMALL) { Point3d ptP2 ; if ( ! ccOffs.GetStartPoint( ptP2)) return false ; Point3d ptP1 = ptP2 ; ptP1.z -= dLenVertFirst ; PtrOwner pCrv( CreateBasicCurveLine()) ; if ( IsNull( pCrv) || ! pCrv->Set( ptP1, ptP2)) return false ; if ( ! ccOffs.AddCurve( Release( pCrv), false)) return false ; } // se richiesto inserimento ultima retta verticale if ( abs( dLenVertLast) > EPS_SMALL) { Point3d ptP1 ; if ( ! ccOffs.GetEndPoint( ptP1)) return false ; Point3d ptP2 = ptP1 ; ptP2.z += dLenVertLast ; PtrOwner pCrv( CreateBasicCurveLine()) ; if ( IsNull( pCrv) || ! pCrv->Set( ptP1, ptP2)) return false ; if ( ! ccOffs.AddCurve( Release( pCrv))) return false ; } return true ; } //---------------------------------------------------------------------------- bool MediaInternalAngleDeltaZ( CurveComposite& ccOffs) { // definisco una composita temporanea CurveComposite ccTemp ; // ciclo sulle curve bool bModifyNext = false ; Point3d ptMid ; ICurve* pCrv = ccOffs.RemoveFirstOrLastCurve( false) ; while ( pCrv != nullptr) { // verifico se curva di raccordo in Z di angolo interno if ( pCrv->GetTempProp() == TP_IS_VERT_LINE) { // ne recupero il punto medio e la cancello pCrv->GetMidPoint( ptMid) ; delete pCrv ; // modifico il finale della curva precedente if ( ! ccTemp.ModifyEnd( ptMid)) return false ; // imposto flag per modifica iniziale della curva successiva bModifyNext = true ; } // altrimenti else { // se da modificare if ( bModifyNext && ! pCrv->ModifyStart( ptMid)) { delete pCrv ; return false ; } bModifyNext = false ; // la sposto nella composita di offset if ( ! ccTemp.AddCurve( pCrv)) return false ; } // passo alla curva successiva pCrv = ccOffs.RemoveFirstOrLastCurve( false) ; } // se modifica in sospeso, allora curva originale chiusa e modifico inizio if ( bModifyNext && ! ccTemp.ModifyStart( ptMid)) return false ; // riporto le curve nella composita di offset return ccOffs.RelocateFrom( ccTemp) ; }