//---------------------------------------------------------------------------- // EgalTech 2016-2016 //---------------------------------------------------------------------------- // File : Operation.cpp Data : 29.04.16 Versione : 1.6p4 // Contenuto : Implementazione gestione base operazioni. // // // // Modifiche : 29.04.16 DS Creazione modulo. // // //---------------------------------------------------------------------------- //--------------------------- Include ---------------------------------------- #include "stdafx.h" #include "DllMain.h" #include "Operation.h" #include "OperationConst.h" #include "MachMgr.h" #include "OutputConst.h" #include "/EgtDev/Include/EGkAngle.h" #include "/EgtDev/Include/EGkGeoPoint3d.h" #include "/EgtDev/Include/EGkCurveLine.h" #include "/EgtDev/Include/EGkCurveArc.h" #include "/EgtDev/Include/EGkArcSpecial.h" #include "/EgtDev/Include/EGkCurveComposite.h" #include "/EgtDev/Include/EGkDistPointCurve.h" #include "/EgtDev/Include/EGkIntersCurves.h" #include "/EgtDev/Include/EGkIntersLineSurfTm.h" #include "/EgtDev/Include/EGkGeomDB.h" #include "/EgtDev/Include/EGkCDBoxPolyhedron.h" #include "/EgtDev/Include/EXeConst.h" #include "/EgtDev/Include/EgtPointerOwner.h" using namespace std ; //---------------------------------------------------------------------------- bool Operation::SetOwner( int nId, IGeomDB* pGDB) { m_nOwnerId = nId ; m_pGeomDB = pGDB ; return ( m_nOwnerId != GDB_ID_NULL && m_pGeomDB != nullptr) ; } //---------------------------------------------------------------------------- int Operation::GetOwner( void) const { return m_nOwnerId ; } //---------------------------------------------------------------------------- IGeomDB* Operation::GetGeomDB( void) const { return m_pGeomDB ; } //---------------------------------------------------------------------------- bool Operation::Init( MachMgr* pMchMgr) { m_pMchMgr = pMchMgr ; if ( m_pMchMgr == nullptr) return false ; if ( m_pMchMgr->GetGeomDB() != m_pGeomDB) return false ; return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- Operation::Operation( void) : m_nOwnerId( GDB_ID_NULL), m_pGeomDB( nullptr), m_pMchMgr( nullptr), m_nPhase( 1), m_nPathId( GDB_ID_NULL), m_bCurr( false), m_ptCurr(), m_vtTool(), m_vtCorr(), m_vtAux(), m_dFeed( 0), m_nFlag( 0) { } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP, const Vector3d& vtDir, double& dElev) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // inizializzo elevazione dElev = INFINITO ; // ciclo sui grezzi bool bFound = false ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { // intersezione del raggio dal punto alla trimesh del grezzo const double RAY_LEN = 10000 ; int nStmId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; ISurfTriMesh* pStm = GetSurfTriMesh( m_pGeomDB->GetGeoObj( nStmId)) ; if ( pStm != nullptr) { bFound = true ; // recupero il riferimento della trimesh Frame3d frStm ; m_pGeomDB->GetGlobFrame( nStmId, frStm) ; // porto il raggio in questo riferimento Point3d ptPL = ptP ; ptPL.ToLoc( frStm) ; Vector3d vtDirL = vtDir ; vtDirL.ToLoc( frStm) ; ILSIVECTOR vInfo ; if ( IntersLineSurfTm( ptPL, vtDirL, RAY_LEN, *pStm, vInfo)) { for ( const auto& Info : vInfo) { // se tratto di intersezione coincidente, considero la posizione più lontana, ovvero 2 if ( Info.nILTT == ILTT_SEGM || Info.nILTT == ILTT_SEGM_ON_EDGE) { // se prosegue un tratto precedente, non devo controllare sia minimo if ( fabs( dElev - Info.dU) < EPS_SMALL) dElev = Info.dU2 ; else dElev = min( dElev, Info.dU2) ; } // altrimenti intersezione puntuale, verifico che esca (coseno >= 0) else if ( Info.dCosDN > - COS_ORTO_ANG_ZERO) { dElev = min( dElev, Info.dU) ; } } } } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // se non trovate intersezioni, elevazione nulla if ( dElev > INFINITO - 1) dElev = 0 ; return bFound ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP1, const Point3d& ptP2, const Vector3d& vtDir, double& dElev) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // inizializzo elevazione dElev = INFINITO ; // ciclo sui grezzi bool bFound = false ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { // intersezione del raggio dal punto alla trimesh del grezzo const double RAY_LEN = 10000 ; int nStmId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; ISurfTriMesh* pStm = GetSurfTriMesh( m_pGeomDB->GetGeoObj( nStmId)) ; if ( pStm != nullptr) { bFound = true ; // recupero il riferimento della trimesh Frame3d frStm ; m_pGeomDB->GetGlobFrame( nStmId, frStm) ; // porto il segmento e la direzione in questo riferimento Point3d ptP1L = ptP1 ; ptP1L.ToLoc( frStm) ; Point3d ptP2L = ptP2 ; ptP2L.ToLoc( frStm) ; Vector3d vtDirL = vtDir ; vtDirL.ToLoc( frStm) ; ILSIVECTOR vInfo ; // inizializzo elevazione della superficie double dElevS = 0 ; // faccio test con un insieme di punti ( !!! sostituire con intersezione tra rettangolo e trimesh !!!) const double STEP = 50 ; int nStep = max( (int) ceil( ApproxDist( ptP1L, ptP2L) / STEP), 3) ; for ( int i = 0 ; i <= nStep ; ++ i) { // calcolo punto di test double dFraz = i / (double) nStep ; Point3d ptPL = Media( ptP1L, ptP2L, dFraz) ; // inizializzo elevazione del punto double dElevP = INFINITO ; // calcolo elevazione sul punto if ( IntersLineSurfTm( ptPL, vtDirL, RAY_LEN, *pStm, vInfo)) { for ( const auto& Info : vInfo) { // se tratto di intersezione coincidente, considero la posizione più lontana, ovvero 2 if ( Info.nILTT == ILTT_SEGM || Info.nILTT == ILTT_SEGM_ON_EDGE) { // se prosegue un tratto precedente, non devo controllare sia minimo if ( fabs( dElevP - Info.dU) < EPS_SMALL) dElevP = Info.dU2 ; else dElevP = min( dElevP, Info.dU2) ; } // altrimenti intersezione puntuale, verifico che esca (coseno >= 0) else if ( Info.dCosDN > - COS_ORTO_ANG_ZERO) { dElevP = min( dElevP, Info.dU) ; } } } // se elevazione calcolata, aggiorno quella della superficie if ( dElevP < INFINITO - 1) dElevS = max( dElevS, dElevP) ; } // aggiorno elevazione complessiva if ( dElevS > 0) dElev = min( dElev, dElevS) ; } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // se non trovate intersezioni, elevazione nulla if ( dElev > INFINITO - 1) dElev = 0 ; return bFound ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP1, const Point3d& ptP2, const Point3d& ptP3, const Vector3d& vtDir, double& dElev) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // approssimo l'arco con due linee double dElev1, dElev2 ; if ( ! GetElevation( nPhase, ptP1, ptP2, vtDir, dElev1)) return false ; if ( ! GetElevation( nPhase, ptP2, ptP3, vtDir, dElev2)) return false ; dElev = max( dElev1, dElev2) ; return true ; } //---------------------------------------------------------------------------- bool Operation::GetElevation( int nPhase, const Point3d& ptP, const Vector3d& vtTool, double dRad, const Vector3d& vtDir, double& dElev) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // considero il punto centrale if ( ! GetElevation( nPhase, ptP, vtDir, dElev)) return false ; // considero 8 direzioni sulla circonferenza const int N_STEP = 8 ; Vector3d vtRad = FromUprightOrtho( vtTool) * dRad ; for ( int i = 0 ; i < N_STEP ; ++ i) { // esploro 4 posizioni sulle direzioni for ( int j = 1 ; j <= 8 ; j *= 2) { double dElevT = 0 ; if ( ! GetElevation( nPhase, ptP + vtRad / j, vtDir, dElevT)) return false ; dElev = max( dElev, dElevT) ; } // passo alla direzione successiva vtRad.Rotate( vtTool, ANG_FULL / N_STEP) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::GetDistanceFromRawSide( int nPhase, const Point3d& ptP, const Vector3d& vtDir, double& dDist) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero il grezzo in cui è incluso il punto BBox3d b3Pnt( ptP) ; b3Pnt.Expand( 10, 10, 0) ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { // pre-filtro con il box BBox3d b3Raw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; if ( m_pGeomDB->GetGlobalBBox( nRawSolidId, b3Raw) && b3Pnt.OverlapsXY( b3Raw)) { // porto il punto nel riferimento del grezzo (a Z=0 perchè ivi è il contorno) Frame3d frRaw ; m_pGeomDB->GetGroupGlobFrame( nRawId, frRaw) ; Point3d ptPL = ptP ; ptPL.ToLoc( frRaw) ; ptPL.z = 0 ; // verifica con il contorno int nOutCrvId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_OUTLINE) ; const ICurve* pCurve = GetCurve( m_pGeomDB->GetGeoObj( nOutCrvId)) ; if ( pCurve != nullptr) { int nSide ; double dDist ; DistPointCurve distPC( ptPL, *pCurve) ; if ( distPC.GetDist( dDist) && dDist < 100 * EPS_SMALL || (distPC.GetSideAtMinDistPoint( 0, Z_AX, nSide) && nSide != MDS_RIGHT)) break ; } } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // se il punto non è interno ad alcun grezzo, distanza nulla if ( nRawId == GDB_ID_NULL) { dDist = 0 ; return true ; } // recupero il contorno del grezzo int nOutId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_OUTLINE) ; ICurve* pOut = ::GetCurve( m_pGeomDB->GetGeoObj( nOutId)) ; if ( pOut == nullptr) return false ; // recupero il riferimento del contorno e verifico che abbia Z verticale Frame3d frStm ; if ( ! m_pGeomDB->GetGlobFrame( nOutId, frStm) || ( frStm.GetZType() != Frame3d::TOP && frStm.GetZType() != Frame3d::BOTTOM)) return false ; // interseco il raggio di test (portato nel riferimento del contorno) con il contorno del grezzo const double RAY_LEN = 10000 ; PtrOwner pRay( CreateCurveLine()) ; if ( IsNull( pRay) || ! pRay->SetPVL( ptP, vtDir, RAY_LEN)) return false ; pRay->ToLoc( frStm) ; IntersCurveCurve intCC( *pRay, *pOut) ; IntCrvCrvInfo aInfo ; if ( intCC.GetIntCrvCrvInfo( 0, aInfo)) dDist = aInfo.IciA[0].dU * RAY_LEN ; else dDist = 0 ; return true ; } //---------------------------------------------------------------------------- bool Operation::GetMinDistanceFromRawSide( int nPhase, const Point3d& ptP, double& dDist, Vector3d& vtDir) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero il grezzo in cui è incluso il punto BBox3d b3Pnt( ptP) ; b3Pnt.Expand( 10, 10, 0) ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { // pre-filtro con il box BBox3d b3Raw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; if ( m_pGeomDB->GetGlobalBBox( nRawSolidId, b3Raw) && b3Pnt.OverlapsXY( b3Raw)) { // porto il punto nel riferimento del grezzo (a Z=0 perchè ivi è il contorno) Frame3d frRaw ; m_pGeomDB->GetGroupGlobFrame( nRawId, frRaw) ; Point3d ptPL = ptP ; ptPL.ToLoc( frRaw) ; ptPL.z = 0 ; // verifica con il contorno int nOutCrvId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_OUTLINE) ; const ICurve* pCurve = GetCurve( m_pGeomDB->GetGeoObj( nOutCrvId)) ; if ( pCurve != nullptr) { int nSide ; DistPointCurve distPC( ptPL, *pCurve) ; // se punto praticamente sul bordo del grezzo, prendo la normale esterna a questo bordo if ( distPC.GetDist( dDist) && dDist < 100 * EPS_SMALL) { double dU ; int nFlag ; Point3d ptML ; Vector3d vtTgL ; if ( distPC.GetParamAtMinDistPoint( 0, dU, nFlag) && pCurve->GetPointTang( dU, ICurve::FROM_MINUS, ptML, vtTgL)) { vtDir = vtTgL ; vtDir.Rotate( Z_AX, - ANG_RIGHT) ; vtDir.ToGlob( frRaw) ; vtDir.Normalize() ; break ; } } // altrimenti se punto interno, prendo la direzione dal punto al bordo else if (distPC.GetSideAtMinDistPoint( 0, Z_AX, nSide) && nSide != MDS_RIGHT) { int nFlag ; Point3d ptML ; if ( distPC.GetMinDistPoint( 0, ptML, nFlag)) { vtDir = ptML - ptPL ; vtDir.ToGlob( frRaw) ; vtDir.z = 0 ; vtDir.Normalize() ; break ; } } } } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // se il punto non è interno ad alcun grezzo, distanza nulla if ( nRawId == GDB_ID_NULL) dDist = 0 ; return true ; } //---------------------------------------------------------------------------- bool Operation::GetDistanceFromRawBottom( int nPhase, int nPathId, double dToler, double& dRbDist) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero distanza da fondo dei grezzi interessati dal percorso dRbDist = 0 ; BBox3d b3Compo ; if ( ! m_pGeomDB->GetGlobalBBox( nPathId, b3Compo)) return false ; b3Compo.Expand( dToler, dToler, 0) ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { BBox3d b3Raw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; if ( m_pGeomDB->GetGlobalBBox( nRawSolidId, b3Raw) && b3Compo.OverlapsXY( b3Raw)) { double dDist = b3Compo.GetMax().z - b3Raw.GetMin().z ; if ( dDist > dRbDist) dRbDist = dDist ; } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::GetRawGlobBox( int nPhase, int nPathId, double dToler, BBox3d& b3Raw) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // inizializzo box b3Raw.Reset() ; // ricerco grezzi interessati dal percorso BBox3d b3Compo ; if ( ! m_pGeomDB->GetGlobalBBox( nPathId, b3Compo)) return false ; return GetRawGlobBox( nPhase, b3Compo, dToler, b3Raw) ; } //---------------------------------------------------------------------------- bool Operation::GetRawGlobBox( int nPhase, const BBox3d& b3Test, double dToler, BBox3d& b3Raw) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // inizializzo box b3Raw.Reset() ; // allargo box di test BBox3d b3Compo = b3Test ; b3Compo.Expand( dToler, dToler, 0) ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { // verifico che il grezzo compaia nella fase if ( m_pMchMgr->VerifyRawPartPhase( nRawId, nPhase)) { BBox3d b3OneRaw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; if ( m_pGeomDB->GetGlobalBBox( nRawSolidId, b3OneRaw) && b3Compo.OverlapsXY( b3OneRaw)) { b3Raw.Add( b3OneRaw) ; } } // passo al grezzo successivo nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Operation::AdjustCurveFromSurf( ICurveComposite* pCrvCompo, int nToolDir, int nFaceUse, double dToolThick) { // copia della curva originale PtrOwner pCopy( pCrvCompo->Clone()) ; // se richiesto contorno if ( nFaceUse == FACE_CONT) { // cerco l'estremo più alto e lo imposto come inizio double dU = 0 ; double dUmax = 0 ; double dZmax = - INFINITO ; Point3d ptP ; while ( pCrvCompo->GetPointD1D2( dU, ICurve::FROM_MINUS, ptP)) { if ( ptP.z > dZmax + EPS_SMALL) { dZmax = ptP.z ; dUmax = dU ; } dU += 1 ; } if ( dU != 0) pCrvCompo->ChangeStartPoint( dUmax) ; } // altrimenti else { // ingombro BBox3d b3Box ; pCrvCompo->GetLocalBBox( b3Box) ; double dMin = b3Box.GetMin().z ; double dMax = b3Box.GetMax().z ; if ( nFaceUse == FACE_FRONT || nFaceUse == FACE_BACK) { dMin = b3Box.GetMin().y ; dMax = b3Box.GetMax().y ; } else if ( nFaceUse == FACE_RIGHT || nFaceUse == FACE_LEFT) { dMin = b3Box.GetMin().x ; dMax = b3Box.GetMax().x ; } double dTol = min( ( dMax - dMin) / 8., 20.) ; // cerco il punto medio delle curve più vicino all'estremo desiderato double dRef = dMin ; if ( nFaceUse == FACE_DOWN || nFaceUse == FACE_FRONT || nFaceUse == FACE_LEFT) dRef = dMax ; const ICurve* pCrv = pCrvCompo->GetFirstCurve() ; while ( pCrv != nullptr) { Point3d ptMid ; pCrv->GetMidPoint( ptMid) ; switch ( nFaceUse) { case FACE_DOWN : dRef = min( dRef, ptMid.z) ; break ; case FACE_TOP : dRef = max( dRef, ptMid.z) ; break ; case FACE_FRONT : dRef = min( dRef, ptMid.y) ; break ; case FACE_BACK : dRef = max( dRef, ptMid.y) ; break ; case FACE_LEFT : dRef = min( dRef, ptMid.x) ; break ; case FACE_RIGHT : dRef = max( dRef, ptMid.x) ; break ; } pCrv = pCrvCompo->GetNextCurve() ; } // elimino le linee troppo discoste dal riferimento dall'inizio e poi dalla fine pCrv = pCrvCompo->GetFirstCurve() ; while ( pCrv != nullptr) { Point3d ptMid ; pCrv->GetMidPoint( ptMid) ; if ( ( nFaceUse == FACE_DOWN && ptMid.z > dRef + dTol) || ( nFaceUse == FACE_TOP && ptMid.z < dRef - dTol) || ( nFaceUse == FACE_FRONT && ptMid.y > dRef + dTol) || ( nFaceUse == FACE_BACK && ptMid.y < dRef - dTol) || ( nFaceUse == FACE_LEFT && ptMid.x > dRef + dTol) || ( nFaceUse == FACE_RIGHT && ptMid.x < dRef - dTol)) { ICurve* pErase = pCrvCompo->RemoveFirstOrLastCurve( false) ; delete( pErase) ; pCrv = pCrvCompo->GetFirstCurve() ; } else break ; } pCrv = pCrvCompo->GetLastCurve() ; while ( pCrv != nullptr) { Point3d ptMid ; pCrv->GetMidPoint( ptMid) ; if ( ( nFaceUse == FACE_DOWN && ptMid.z > dRef + dTol) || ( nFaceUse == FACE_TOP && ptMid.z < dRef - dTol) || ( nFaceUse == FACE_FRONT && ptMid.y > dRef + dTol) || ( nFaceUse == FACE_BACK && ptMid.y < dRef - dTol) || ( nFaceUse == FACE_LEFT && ptMid.x > dRef + dTol) || ( nFaceUse == FACE_RIGHT && ptMid.x < dRef - dTol)) { ICurve* pErase = pCrvCompo->RemoveFirstOrLastCurve( true) ; delete( pErase) ; pCrv = pCrvCompo->GetLastCurve() ; } else break ; } } // determino il versore estrusione (utensile) if ( nToolDir == TOOL_ORTHO) pCrvCompo->SetThickness( 0) ; else if ( nToolDir == TOOL_ORTUP) { Vector3d vtN ; pCrvCompo->GetExtrusion( vtN) ; if ( vtN.z < - EPS_SMALL) { pCrvCompo->Translate( vtN * dToolThick) ; pCrvCompo->SetExtrusion( - vtN) ; } pCrvCompo->SetThickness( 0) ; } else { // nToolDir == TOOL_PARAL // sistemo versore Vector3d vtN ; pCrvCompo->GetExtrusion( vtN) ; Point3d ptStart, ptEnd ; pCrvCompo->GetStartPoint( ptStart) ; pCrvCompo->GetEndPoint( ptEnd) ; Vector3d vtT = ptEnd - ptStart ; Vector3d vtExtr = vtN ^ vtT ; if ( ! vtExtr.Normalize()) return false ; pCrvCompo->SetExtrusion( vtExtr) ; // elimino eventuali parti iniziali e/o finali dirette circa come il versore const double MAX_ALL_COS = cos( 25 * DEGTORAD) ; const ICurve* pCrv = pCrvCompo->GetFirstCurve() ; while ( pCrv != nullptr) { Vector3d vtDir ; pCrv->GetStartDir( vtDir) ; if ( abs( vtDir * vtExtr) > MAX_ALL_COS) { ICurve* pErase = pCrvCompo->RemoveFirstOrLastCurve( false) ; delete( pErase) ; pCrv = pCrvCompo->GetFirstCurve() ; } else if ( pCrvCompo->IsClosed()) { pCrvCompo->ChangeStartPoint( 1) ; pCrv = pCrvCompo->GetFirstCurve() ; } else break ; } pCrv = pCrvCompo->GetLastCurve() ; while ( pCrv != nullptr) { Vector3d vtDir ; pCrv->GetStartDir( vtDir) ; if ( abs( vtDir * vtExtr) > MAX_ALL_COS) { ICurve* pErase = pCrvCompo->RemoveFirstOrLastCurve( true) ; delete( pErase) ; pCrv = pCrvCompo->GetLastCurve() ; } else break ; } // determino lo spessore misurato lungo la direzione if ( ! IsNull( pCopy)) { Frame3d frOcs ; frOcs.Set( ORIG, vtExtr) ; Frame3d frExtr ; frExtr.ToLoc( frOcs) ; BBox3d b3Extr ; pCopy->GetBBox( frExtr, b3Extr) ; if ( ! b3Extr.IsEmpty()) pCrvCompo->SetThickness( b3Extr.GetMax().z - b3Extr.GetMin().z) ; } } return true ; } //---------------------------------------------------------------------------- bool Operation::ApproxWithArcsIfUseful( ICurveComposite* pCompo) { // costanti di approssimazione const double LIN_TOL_MID = 0.05 ; const double ANG_TOL_STD_DEG = 15 ; const double LIN_FEA_STD = 20 ; // recupero estrusione e spessore Vector3d vtExtr = Z_AX ; pCompo->GetExtrusion( vtExtr) ; double dThick = 0 ; pCompo->GetThickness( dThick) ; // verifico se sono tante linee double dLen = 0 ; pCompo->GetLength( dLen) ; int nCrvs = pCompo->GetCurveCount() ; if ( nCrvs < 5 || dLen > nCrvs * LIN_FEA_STD) return true ; // calcolo approssimazione con archi PolyArc PA ; if ( ! pCompo->ApproxWithArcsEx( LIN_TOL_MID, ANG_TOL_STD_DEG, LIN_FEA_STD, PA)) return false ; // sostituisco gli archi alle curve originali pCompo->Clear() ; if ( ! pCompo->FromPolyArc( PA)) return false ; // riassegno estrusione e spessore pCompo->SetExtrusion( vtExtr) ; pCompo->SetThickness( dThick) ; return true ; } //---------------------------------------------------------------------------- bool Operation::ApproxWithLines( ICurveComposite* pCompo) { // recupero estrusione e spessore Vector3d vtExtr = Z_AX ; pCompo->GetExtrusion( vtExtr) ; double dThick = 0 ; pCompo->GetThickness( dThick) ; // calcolo approssimazione lineare const double ANG_TOL_MAX_DEG = 90 ; PolyLine PL ; if ( ! pCompo->ApproxWithLines( 50 * EPS_SMALL, ANG_TOL_MAX_DEG, ICurve::APL_SPECIAL, PL)) return false ; // sostituisco le linee alle curve originali pCompo->Clear() ; pCompo->FromPolyLine( PL) ; // riassegno estrusione e spessore pCompo->SetExtrusion( vtExtr) ; pCompo->SetThickness( dThick) ; return true ; } //---------------------------------------------------------------------------- bool Operation::VerifyArcs( ICurveComposite* pCompo, double dMaxAngCen) { // verifiche sull'ampiezza dell'angolo al centro degli eventuali archi int nMaxInd = pCompo->GetCurveCount() - 1 ; for ( int i = 0 ; i <= nMaxInd ; ) { // se arco con angolo al centro oltre il limite, lo divido a metà const ICurveArc* pArc = GetCurveArc( pCompo->GetCurve( i)) ; if ( pArc != nullptr && abs( pArc->GetAngCenter()) > dMaxAngCen) { pCompo->AddJoint( i + 0.5) ; ++ nMaxInd ; } else ++ i ; } return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Operation::CalcAndSetBBox( int nClId) { if ( m_pGeomDB == nullptr) return false ; // assegno ingombri dei vari percorsi di lavorazione e della lavorazione nel suo complesso BBox3d b3AllMch ; int nClPathId = m_pGeomDB->GetFirstInGroup( nClId) ; while ( nClPathId != GDB_ID_NULL) { BBox3d b3Grp ; for ( int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; nEntId != GDB_ID_NULL ; nEntId = m_pGeomDB->GetNext( nEntId)) { // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) continue ; // recupero il punto finale e lo uso per aggiornare l'ingombro Point3d ptP = pCamData->GetEndPoint() ; AdjustEndPointForAxesCalc( pCamData, ptP) ; b3Grp.Add( ptP) ; // se arco, determino anche alcuni punti intermedi if ( pCamData->IsArc()) { // dati dell'arco Point3d ptCen = pCamData->GetCenter() ; AdjustArcCenterForAxesCalc( pCamData, ptCen) ; double dAngCen = pCamData->GetAngCen() ; Vector3d vtN = pCamData->GetNormDir() ; double dDeltaN = pCamData->GetDeltaN() ; // ricavo i punti const double ANG_STEP = 15.0 ; int nStep = int( abs( dAngCen) / ANG_STEP) ; if ( nStep > 1) { double dCoeff = 1. / nStep ; for ( int i = 1 ; i < nStep ; ++ i) { Point3d ptT = ptP ; ptT.Rotate( ptCen, vtN, dAngCen * ( dCoeff - 1.)) ; ptT.Translate( vtN * ( dDeltaN * ( dCoeff - 1.))) ; b3Grp.Add( ptT) ; } } } } m_pGeomDB->SetInfo( nClPathId, KEY_PMIN, b3Grp.GetMin()) ; m_pGeomDB->SetInfo( nClPathId, KEY_PMAX, b3Grp.GetMax()) ; b3AllMch.Add( b3Grp) ; nClPathId = m_pGeomDB->GetNext( nClPathId) ; } m_pGeomDB->SetInfo( nClId, KEY_MMIN, b3AllMch.GetMin()) ; m_pGeomDB->SetInfo( nClId, KEY_MMAX, b3AllMch.GetMax()) ; return true ; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Operation::GetInitialAxesValues( DBLVECTOR& vAxVal) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero gruppo per geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ; if ( nClId == GDB_ID_NULL) return false ; // recupero il primo percorso CL int nClPathId = m_pGeomDB->GetFirstGroupInGroup( nClId) ; // restituisco i valori iniziali degli assi di questo percorso di lavorazione return GetClPathInitialAxesValues( nClPathId, vAxVal) ; } //---------------------------------------------------------------------------- bool Operation::GetClPathInitialAxesValues( int nClPathId, DBLVECTOR& vAxVal) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero la prima entità di questo percorso int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) return false ; // assegno i valori degli assi vAxVal = pCamData->GetAxesVal() ; return ( vAxVal.size() > 0) ; } //---------------------------------------------------------------------------- bool Operation::GetFinalAxesValues( DBLVECTOR& vAxVal) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero gruppo per geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ; if ( nClId == GDB_ID_NULL) return false ; // recupero l'ultimo percorso CL int nClPathId = m_pGeomDB->GetLastGroupInGroup( nClId) ; // restituisco i valori finali degli assi di questo percorso di lavorazione return GetClPathFinalAxesValues( nClPathId, vAxVal) ; } //---------------------------------------------------------------------------- bool Operation::GetClPathFinalAxesValues( int nClPathId, DBLVECTOR& vAxVal) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero l'ultima entità di questo percorso int nEntId = m_pGeomDB->GetLastInGroup( nClPathId) ; // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) return false ; // assegno i valori degli assi vAxVal = pCamData->GetAxesVal() ; return ( vAxVal.size() > 0) ; } //---------------------------------------------------------------------------- string Operation::ExtractInfo( const string& sNotes, const string& sKey) { string sInfo ; STRVECTOR vsCmd ; Tokenize( sNotes, ";", vsCmd) ; for each (const auto& sCmd in vsCmd) { // se per assi rotanti if ( sCmd.find( sKey) != string::npos) { sInfo = sCmd ; ReplaceString( sInfo, sKey, "") ; break ; } } return sInfo ; } //---------------------------------------------------------------------------- string Operation::ExtractHint( const string& sNotes) { return ExtractInfo( sNotes, "Hint:") ; } //---------------------------------------------------------------------------- bool Operation::CalculateAxesValues( const string& sHint, bool bSolChExact) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero l'operazione precedente non vuota o che richiede home precedente int nPrevOpId = m_pMchMgr->GetPrevActiveOperation( m_nOwnerId) ; Operation* pPrevOp = GetOperation( m_pGeomDB->GetUserObj( nPrevOpId)) ; while ( pPrevOp != nullptr) { if ( ! pPrevOp->IsEmpty() || pPrevOp->NeedPrevHome()) break ; else { nPrevOpId = m_pMchMgr->GetPrevActiveOperation( nPrevOpId) ; pPrevOp = GetOperation( m_pGeomDB->GetUserObj( nPrevOpId)) ; } } // recupero l'utensile precedente string sPrevTool ; if ( pPrevOp != nullptr && ! pPrevOp->IsEmpty()) sPrevTool = pPrevOp->GetToolName() ; // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // rimuovo posizionamento home da lavorazione precedente se non richiesto (non può più essere l'ultima) if ( pPrevOp != nullptr && ! NeedPrevHome()) pPrevOp->RemoveHome() ; // recupero il numero di assi lineari e rotanti attivi int nLinAxes = m_pMchMgr->GetCurrLinAxes() ; int nRotAxes = m_pMchMgr->GetCurrRotAxes() ; // recupero gli angoli home DBLVECTOR vAxRotHome( nRotAxes, 0.) ; DBLVECTOR vAxHome ; m_pMchMgr->GetAllCurrAxesHomePos( vAxHome) ; for ( int i = 0 ; i < nRotAxes ; ++ i) vAxRotHome[i] = vAxHome[nLinAxes + i] ; // Assegno gli angoli iniziali DBLVECTOR vAxRotPrec( nRotAxes, 0.) ; DBLVECTOR vAxVal ; // se utensile non cambiato, uso gli angoli finali della lavorazione precedente if ( ! sPrevTool.empty() && ! ToolChangeNeeded( *pPrevOp, *this) && pPrevOp->GetFinalAxesValues( vAxVal)) { for ( int i = 0 ; i < nRotAxes ; ++ i) vAxRotPrec[i] = vAxVal[nLinAxes + i] ; } // altrimenti uso gli angoli home else { vAxRotPrec = vAxRotHome ; } // se ci sono suggerimenti validi per gli assi rotanti, li applico if ( ! sHint.empty()) { STRVECTOR vsTok ; Tokenize( sHint, ",", vsTok) ; for each (const auto& sTok in vsTok) { string szKey, szVal ; Split( sTok, "=", true, szKey, szVal) ; for ( int i = 0 ; i < nRotAxes ; ++ i) { string sAxToken ; if ( m_pMchMgr->GetCurrMachine()->GetCurrAxisToken(nLinAxes + i, sAxToken) && szKey == sAxToken) { double dVal ; if ( FromString( szVal, dVal)) vAxRotPrec[i] = dVal ; break ; } } } } // recupero peso primo asse rotante di testa double dRot1W = m_pMchMgr->GetCalcRot1W() ; // recupero la minima differenza angolare da posizione precedente per stare vivino a posizione home double dAngDeltaMinForHome = m_pMchMgr->GetAngDeltaMinForHome() ; // applico eventuali blocchi di assi rotanti m_pMchMgr->ApplyRotAxisBlock() ; // applico il criterio di scelta della soluzione quando molteplici m_pMchMgr->SetCalcSolCh( GetSolCh(), bSolChExact) ; // recupero gruppo della geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ; if ( nClId == GDB_ID_NULL) return false ; // calcolo il valore degli assi macchina di tutti i movimenti bool bOk = true ; int nClPathId = m_pGeomDB->GetFirstGroupInGroup( nClId) ; while ( nClPathId != GDB_ID_NULL) { DBLVECTOR vAxRotPrecOri = vAxRotPrec ; int nOutStrC = 0 ; if ( ! CalculateClPathAxesValues( nClPathId, nLinAxes, nRotAxes, dRot1W, dAngDeltaMinForHome, vAxRotHome, vAxRotPrec, nOutStrC)) { // se attivata scelta angolo iniziale vicino ad home e extra corsa C, provo a rifarlo disattivando if ( dAngDeltaMinForHome < ANG_FULL && nOutStrC != 0) CalculateClPathAxesValues( nClPathId, nLinAxes, nRotAxes, dRot1W, INFINITO, vAxRotHome, vAxRotPrec, nOutStrC) ; // se extracorsa dell'asse C, provo a precaricarlo al contrario if ( nOutStrC != 0) { vAxRotPrec[0] = vAxRotPrecOri[0] + ( nOutStrC > 0 ? - ANG_FULL : ANG_FULL) ; vAxRotPrec[1] = vAxRotPrecOri[1] ; if ( ! CalculateClPathAxesValues( nClPathId, nLinAxes, nRotAxes, dRot1W, INFINITO, vAxRotHome, vAxRotPrec, nOutStrC)) bOk = false ; } else bOk = false ; } nClPathId = m_pGeomDB->GetNextGroup( nClPathId) ; } return bOk ; } //---------------------------------------------------------------------------- bool Operation::CalculateClPathAxesValues( int nClPathId, int nLinAxes, int nRotAxes, double dRot1W, double dAngDeltaMinForHome, const DBLVECTOR& vAxRotHome, DBLVECTOR& vAxRotPrec, int& nOutStrC) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero il numero degli assi lineari e rotanti // predispongo variabile per valori assi DBLVECTOR vAxVal ; vAxVal.reserve( 8) ; // variabili per coordinate lineari precedenti double dXprec, dYprec, dZprec ; // ciclo su tutte le entità del percorso CL nOutStrC = 0 ; bool bOk = true ; bool bFirst = true ; for ( int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; nEntId != GDB_ID_NULL ; nEntId = m_pGeomDB->GetNext( nEntId)) { // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) continue ; // verifico che il primo movimento non sia un arco if ( bFirst && pCamData->IsArc()) { pCamData->ResetAxes() ; LOG_INFO( GetEMkLogger(), "Error : first move in ClPath is an arc") return false ; } // calcolo degli assi rotanti della macchina int nRStat ; DBLVECTOR vAng1, vAng2 ; bool bROk = m_pMchMgr->GetCalcAngles( pCamData->GetToolDir(), pCamData->GetAuxDir(), nRStat, vAng1, vAng2) ; if ( ! bROk || nRStat == 0) { pCamData->SetAxes( CamData::AS_DIR_ERR, vAxVal) ; LOG_INFO( GetEMkLogger(), "Error : tool direction unreachable") return false ; } if ( abs( nRStat) == 1) { // se primo movimento if ( bFirst) { // porto gli angoli ai valori più vicini ai precedenti con offset di uno o più giri for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { m_pMchMgr->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng1[i]) ; if ( abs( vAng1[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) m_pMchMgr->GetNearestAngleInStroke( i, vAxRotHome[i], vAng1[i]) ; } // se sol.ne indeterminata (sempre il primo asse libero), assegno il precedente, limitandolo alla corsa if ( nRStat < 0 && vAng1.size() >= 1) { for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { // ignoro gli assi bloccati if ( m_pMchMgr->IsKinematicRotAxisBlocked( i)) continue ; // assegno il precedente ed esco vAng1[i] = vAxRotPrec[i] ; m_pMchMgr->LimitAngleToStroke( i, vAng1[i]) ; break ; } } } // per movimenti successivi else { // scelgo gli angoli più vicini, per continuità non applico offset per stare nelle corse for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { vAng1[i] = AngleNearAngle( vAng1[i], vAxRotPrec[i]) ; if ( abs( vAng1[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) vAng1[i] = AngleNearAngle( vAng1[i], vAxRotHome[i]) ; } // se sol.ne indeterminata (sempre il primo asse libero), assegno il precedente if ( nRStat < 0 && vAng1.size() >= 1) { for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { // ignoro gli assi bloccati if ( m_pMchMgr->IsKinematicRotAxisBlocked( i)) continue ; // assegno il precedente ed esco vAng1[i] = vAxRotPrec[i] ; break ; } } } } if ( abs( nRStat) == 2) { bool bAng1 = true ; bool bAng2 = true ; // se primo movimento if ( bFirst) { // porto gli angoli ai valori più vicini ai precedenti con offset di uno o più giri for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { m_pMchMgr->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng1[i]) ; if ( abs( vAng1[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) m_pMchMgr->GetNearestAngleInStroke( i, vAxRotHome[i], vAng1[i]) ; m_pMchMgr->GetNearestAngleInStroke( i, vAxRotPrec[i], vAng2[i]) ; if ( abs( vAng2[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) m_pMchMgr->GetNearestAngleInStroke( i, vAxRotHome[i], vAng2[i]) ; } // se sol.ne indeterminata (sempre il primo asse libero), assegno il precedente, limitandolo alla corsa if ( nRStat < 0 && vAng1.size() >= 1) { for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { // ignoro gli assi bloccati if ( m_pMchMgr->IsKinematicRotAxisBlocked( i)) continue ; // assegno il precedente ed esco vAng1[i] = vAxRotPrec[i] ; m_pMchMgr->LimitAngleToStroke( i, vAng1[i]) ; vAng2[i] = vAng1[i] ; break ; } } } else { // scelgo gli angoli più vicini, per continuità non applico offset per stare nelle corse for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { vAng1[i] = AngleNearAngle( vAng1[i], vAxRotPrec[i]) ; if ( abs( vAng1[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) vAng1[i] = AngleNearAngle( vAng1[i], vAxRotHome[i]) ; vAng2[i] = AngleNearAngle( vAng2[i], vAxRotPrec[i]) ; if ( abs( vAng2[i] - vAxRotPrec[i]) > dAngDeltaMinForHome) vAng2[i] = AngleNearAngle( vAng2[i], vAxRotHome[i]) ; } // se sol.ne indeterminata (sempre il primo asse libero), assegno il precedente if ( nRStat < 0 && vAng1.size() >= 1) { for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { // ignoro gli assi bloccati if ( m_pMchMgr->IsKinematicRotAxisBlocked( i)) continue ; // assegno il precedente ed esco vAng1[i] = vAxRotPrec[i] ; vAng2[i] = vAng1[i] ; break ; } } // verifico che le soluzioni siano nelle corse for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { if ( ! m_pMchMgr->VerifyAngleOutstroke( i, vAng1[i])) bAng1 = false ; if ( ! m_pMchMgr->VerifyAngleOutstroke( i, vAng2[i])) bAng2 = false ; } } // scelgo la soluzione più vicina alla precedente double dDelta1 = 0 ; double dDelta2 = 0 ; for ( int i = 0 ; i < int( vAng1.size()) ; ++ i) { // ignoro gli assi bloccati if ( m_pMchMgr->IsKinematicRotAxisBlocked( i)) continue ; // calcolo i delta asse con eventuale peso bool bFirst = ( i == 0) ; dDelta1 += fabs( vAng1[i] - vAxRotPrec[i]) * ( bFirst ? dRot1W : 1) ; dDelta2 += fabs( vAng2[i] - vAxRotPrec[i]) * ( bFirst ? dRot1W : 1) ; } if ( bAng2 && ( dDelta2 < dDelta1 - EPS_ANG_SMALL || ! bAng1)) vAng1 = vAng2 ; } // ricavo posizione con eventuali modifiche dipendenti dalla lavorazione Point3d ptP = pCamData->GetEndPoint() ; AdjustEndPointForAxesCalc( pCamData, ptP) ; // calcolo gli assi lineari della macchina int nLStat ; double dX, dY, dZ ; bool bLOk = m_pMchMgr->GetCalcPositions( ptP, vAng1, nLStat, dX, dY, dZ) ; if ( ! bLOk || nLStat != 0) { bOk = false ; pCamData->SetAxes( CamData::AS_ERR, vAxVal) ; continue ; } // assegno valori assi vAxVal.clear() ; vAxVal.emplace_back( dX) ; vAxVal.emplace_back( dY) ; vAxVal.emplace_back( dZ) ; for ( auto dAng : vAng1) vAxVal.emplace_back( dAng) ; // verifico i limiti di corsa degli assi int nStat ; bool bOsOk = m_pMchMgr->VerifyOutstroke( dX, dY, dZ, vAng1, false, nStat) ; if ( ! bOsOk || nStat != 0) { bOk = false ; pCamData->SetAxes( CamData::AS_OUTSTROKE, vAxVal) ; if ( nOutStrC == 0) { if ( (nStat & 64) != 0) nOutStrC = - 1 ; else if ( (nStat & 128) != 0) nOutStrC = 1 ; } string sInfo = "Outstroke : " + m_pMchMgr->GetOutstrokeInfo() ; LOG_INFO( GetEMkLogger(), sInfo.c_str()) // imposto stato bFirst = false ; // memorizzo i valori degli assi lineari come nuovi precedenti dXprec = dX ; dYprec = dY ; dZprec = dZ ; // memorizzo i valori degli angoli come nuovi precedenti vAxRotPrec = vAng1 ; continue ; } // salvo i valori degli assi pCamData->SetAxes( CamData::AS_OK, vAxVal) ; // calcolo e salvo il valore dell'asse ausiliario di testa riportato sul grezzo Vector3d vtBackAux ; m_pMchMgr->GetCurrMachine()->GetBackAuxDirFromAngles( vAng1, vtBackAux) ; pCamData->SetBackAuxDir( vtBackAux) ; // se arco devo calcolarne il centro in assi macchina if ( pCamData->IsArc()) { // devo lavorare con arco schiacciato nel suo piano Point3d ptCen = pCamData->GetCenter() ; AdjustArcCenterForAxesCalc( pCamData, ptCen) ; double dAngCen = pCamData->GetAngCen() ; Vector3d vtN = pCamData->GetNormDir() ; double dDeltaN = pCamData->GetDeltaN() ; // ricavo punto medio nel piano dell'arco Point3d ptMid = ptP - dDeltaN * vtN ; ptMid.Rotate( ptCen, vtN, - dAngCen / 2) ; // determino i valori degli assi al punto medio DBLVECTOR vAngMid( vAng1.size()) ; for ( size_t i = 0 ; i < vAng1.size() ; ++ i) vAngMid[i] = ( vAxRotPrec[i] + vAng1[i]) / 2 ; int nLmidStat ; double dXmid, dYmid, dZmid ; bool bLmidOk = m_pMchMgr->GetCalcPositions( ptMid, vAngMid, nLmidStat, dXmid, dYmid, dZmid) ; if ( ! bLmidOk || nLmidStat != 0) { bOk = false ; pCamData->SetAxes( CamData::AS_ERR, vAxVal) ; continue ; } // ricavo punto finale nel piano dell'arco Point3d ptEnd = ptP - dDeltaN * vtN ; // determino i valori degli assi al punto finale DBLVECTOR vAngEnd( vAng1.size()) ; for ( size_t i = 0 ; i < vAng1.size() ; ++ i) vAngEnd[i] = vAng1[i] ; int nLendStat ; double dXend, dYend, dZend ; bool bLendOk = m_pMchMgr->GetCalcPositions( ptEnd, vAngEnd, nLendStat, dXend, dYend, dZend) ; if ( ! bLendOk || nLendStat != 0) { bOk = false ; pCamData->SetAxes( CamData::AS_ERR, vAxVal) ; continue ; } // punti espressi in assi macchina Point3d ptP1M( dXprec, dYprec, dZprec) ; Point3d ptP2M( dXmid, dYmid, dZmid) ; Point3d ptP3M( dXend, dYend, dZend) ; // se coincidono if ( AreSamePointApprox( ptP1M, ptP2M) && AreSamePointApprox( ptP1M, ptP3M)) { // lo considero un arco di raggio nullo pCamData->SetAxesCen( ptP2M) ; pCamData->SetAxesRad( 0) ; pCamData->SetAxesAngCen( 0) ; pCamData->SetAxesNormDir( V_NULL) ; // il movimento diventa lineare pCamData->SetMoveType( 1) ; // non è necessario verificare i limiti di corsa degli assi } // altrimenti calcolo l'arco cui appartengono else { // calcolo arco per i tre punti espressi in assi macchina PtrOwner pCurve( GetArc3P( ptP1M, ptP2M, ptP3M, false)) ; if ( IsNull( pCurve)) { LOG_INFO( GetEMkLogger(), "Error : arc on machine not calculable") return false ; } // se realmente arco if ( pCurve->GetType() == CRV_ARC) { ICurveArc* pArc = GetCurveArc( pCurve) ; // verifico il piano if ( pArc->GetNormVersor() * pCamData->GetNormDir() < 0) pArc->InvertN() ; // assegno il centro e il raggio di questo arco ai dati cam pCamData->SetAxesCen( pArc->GetCenter()) ; pCamData->SetAxesRad( pArc->GetRadius()) ; pCamData->SetAxesAngCen( pArc->GetAngCenter()) ; pCamData->SetAxesNormDir( pArc->GetNormVersor()) ; // aggiorno il tipo di arco pCamData->SetMoveType( pArc->GetAngCenter() > 0 ? 3 : 2) ; // verifico i limiti di corsa dei punti lungo l'arco const int NUM_VERIF_STEP = 16 ; Point3d ptCen = pCamData->GetAxesCen() ; double dAngCenStep = pCamData->GetAxesAngCen() / NUM_VERIF_STEP ; Vector3d vtN = pCamData->GetAxesNormDir() ; Vector3d vtCurr = Point3d( dXprec, dYprec, dZprec) - ptCen ; for ( int i = 1 ; i < NUM_VERIF_STEP ; ++ i) { vtCurr.Rotate( vtN, dAngCenStep) ; double dCoeff = double( i) / NUM_VERIF_STEP ; Point3d ptCurr = ptCen + vtCurr + vtN * dDeltaN * dCoeff ; DBLVECTOR vAng( vAng1.size()) ; for ( size_t i = 0 ; i < vAng1.size() ; ++ i) vAng[i] = vAxRotPrec[i] * ( 1 - dCoeff) + vAng1[i] * dCoeff ; int nStat ; bool bOsOk = m_pMchMgr->VerifyOutstroke( ptCurr.x, ptCurr.y, ptCurr.z, vAng, false, nStat) ; if ( ! bOsOk || nStat != 0) { bOk = false ; pCamData->SetAxes( CamData::AS_OUTSTROKE, vAxVal) ; string sInfo = "Outstroke : " + m_pMchMgr->GetOutstrokeInfo() ; LOG_INFO( GetEMkLogger(), sInfo.c_str()) continue ; } } } // altrimenti linea else { ICurveLine* pLine = GetCurveLine( pCurve) ; // lo considero un arco di raggio infinito pCamData->SetAxesCen( ORIG) ; pCamData->SetAxesRad( 0) ; pCamData->SetAxesAngCen( 0) ; pCamData->SetAxesNormDir( V_NULL) ; // il movimento diventa lineare pCamData->SetMoveType( 1) ; // non è necessario verificare i limiti di corsa degli assi } } } bFirst = false ; // memorizzo i valori degli assi lineari come nuovi precedenti dXprec = dX ; dYprec = dY ; dZprec = dZ ; // memorizzo i valori degli angoli come nuovi precedenti vAxRotPrec = vAng1 ; } return bOk ; } //---------------------------------------------------------------------------- bool Operation::AdjustStartEndMovements( bool bVerifyPreviousLink) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero gruppo della geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( m_nOwnerId, MCH_CL) ; if ( nClId == GDB_ID_NULL) return false ; // recupero ultima operazione precedente non vuota o che richiede home precedente int nPrevOpId = m_pMchMgr->GetPrevActiveOperation( m_nOwnerId) ; Operation* pPrevOp = GetOperation( m_pGeomDB->GetUserObj( nPrevOpId)) ; while ( pPrevOp != nullptr) { if ( ! pPrevOp->IsEmpty() || pPrevOp->NeedPrevHome()) break ; else { nPrevOpId = m_pMchMgr->GetPrevActiveOperation( nPrevOpId) ; pPrevOp = GetOperation( m_pGeomDB->GetUserObj( nPrevOpId)) ; } } // recupero l'utensile precedente e i dati della sua testa string sPrevTool ; if ( pPrevOp != nullptr && ! pPrevOp->IsEmpty()) sPrevTool = pPrevOp->GetToolName() ; // determino posizione precedente assi DBLVECTOR vAxVal ; bool bMaxZ = false ; // se primo utensile o richiesto, uso la posizione home if ( sPrevTool.empty() || NeedPrevHome()) { // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // recupero posizione home if ( ! m_pMchMgr->GetAllCurrAxesHomePos( vAxVal)) return false ; // si parte da Z massima bMaxZ = true ; } // se utensile non cambiato o su diversa uscita della stessa testa, uso posizione finale della lavorazione precedente else if ( ! ToolChangeNeeded( *pPrevOp, *this)) { // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // cancello risalita lavorazione precedente pPrevOp->RemoveRise() ; // recupero posizione finale lavorazione precedente if ( ! pPrevOp->GetFinalAxesValues( vAxVal)) return false ; // se richiesta verifica collegamento con lavorazione precedente if ( bVerifyPreviousLink) { // recupero posizione iniziale lavorazione DBLVECTOR vAxIni ; if ( ! GetInitialAxesValues( vAxIni)) return false ; // aggiungo eventuale delta Z double dDeltaZ = 0 ; // determino delta Z rispetto a posizione finale if ( vAxVal.size() >= 4 && vAxIni.size() >= 4) { // verifico se movimento assi rotanti richiede una risalita parziale if ( ! CalcDeltaZForHeadRotation( vAxVal, vAxIni, dDeltaZ)) return false ; } // confronto con eventuale risalita di inizio lavorazione dDeltaZ = max( dDeltaZ, ( vAxIni[2] - vAxVal[2])) ; // se necessaria, aggiungo risalita parziale if ( dDeltaZ > 100 * EPS_SMALL) { if ( ! pPrevOp->AddRise( vAxVal, dDeltaZ)) return false ; // aggiorno quota in Z della posizione iniziale ( anche se verrà aggiornata solo in seguito) vAxIni[2] = vAxVal[2] ; } // verifico se la testa interferisce con i pezzi o i bloccaggi sulla tavola if ( ! TestCollisionAvoid( vAxVal, vAxIni)) { // riprovo con risalita parziale double dHomeZ ; if ( ! m_pMchMgr->GetCurrAxisHomePos( 2, dHomeZ)) return false ; bool bPartRise = false ; double dSafeZ = dHomeZ ; double dUnsafeZ = max( vAxVal[2], vAxIni[2]) ; DBLVECTOR vAxVal2 = vAxVal ; DBLVECTOR vAxIni2 = vAxIni ; for ( int i = 1 ; i <= 4 ; ++ i) { double dTestSafeZ = ( dUnsafeZ + dSafeZ) / 2 ; vAxVal2[2] = dTestSafeZ ; vAxIni2[2] = dTestSafeZ ; if ( TestCollisionAvoid( vAxVal2, vAxIni2)) { bPartRise = true ; dSafeZ = dTestSafeZ ; } else { dUnsafeZ = dTestSafeZ ; } } if ( ! bPartRise) { // cancello eventuale risalita parziale della lavorazione precedente pPrevOp->RemoveRise() ; // aggiungo risalita a Zmax if ( ! pPrevOp->AddRise( vAxVal)) return false ; // si parte da Z massima bMaxZ = true ; } else { // cancello eventuale risalita parziale della lavorazione precedente pPrevOp->RemoveRise() ; // recupero le nuove quote finali (per calcolare il corretto delta) pPrevOp->GetFinalAxesValues( vAxVal) ; // aggiungo risalita a Zsafe if ( ! pPrevOp->AddRise( vAxVal, dSafeZ - vAxVal[2])) return false ; // aggiorno quota iniziale in Z vAxIni[2] = dSafeZ ; } } } } // altrimenti aggiungo uscita per cambio utensile alla lavorazione precedente e parto da questa else { // imposto l'utensile precedente per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( pPrevOp->GetToolName(), pPrevOp->GetHeadName(), pPrevOp->GetExitNbr())) return false ; // ricalcolo risalita di lavorazione precedente (se già calcolata) if ( pPrevOp->RemoveRise()) { if ( ! pPrevOp->AddRise( vAxVal)) return false ; } else { // recupero posizione home if ( ! m_pMchMgr->GetAllCurrAxesHomePos( vAxVal)) return false ; } // imposto l'utensile per i calcoli macchina if ( ! m_pMchMgr->SetCalcTool( GetToolName(), GetHeadName(), GetExitNbr())) return false ; // si parte da Z massima bMaxZ = true ; } // aggiusto l'inizio di ogni percorso di lavoro bool bOk = true ; int nPrevClPathId = GDB_ID_NULL ; int nClPathId = m_pGeomDB->GetFirstGroupInGroup( nClId) ; while ( bOk && nClPathId != GDB_ID_NULL) { // se richiesta verifica collegamento con lavorazione precedente, sistemo inizio if ( bVerifyPreviousLink) { if ( ! AdjustOneStartMovement( nClPathId, nPrevClPathId, vAxVal, bMaxZ)) bOk = false ; } bMaxZ = false ; // recupero nuovi finali RemoveRise( nClPathId) ; if ( ! GetClPathFinalAxesValues( nClPathId, vAxVal)) bOk = false ; // passo al successivo nPrevClPathId = nClPathId ; nClPathId = m_pGeomDB->GetNextGroup( nClPathId) ; } // aggiungo risalita finale bOk = bOk && AddRise( vAxVal) ; // se ultima operazione o la successiva lo richiede, vado in home int nNextOpId = m_pMchMgr->GetNextActiveOperation( m_nOwnerId) ; Operation* pNextOp = GetOperation( m_pGeomDB->GetUserObj( nNextOpId)) ; if ( pNextOp == nullptr || pNextOp->NeedPrevHome()) bOk = bOk && AddHome() ; return bOk ; } //---------------------------------------------------------------------------- bool Operation::AdjustOneStartMovement( int nClPathId, int nPrevClPathId, const DBLVECTOR& vAxPrev, bool bMaxZ) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // elimino eventuali CLIMB già presenti RemoveClimb( nClPathId) ; // recupero la prima entità di questo percorso int nEntId = m_pGeomDB->GetFirstInGroup( nClPathId) ; // recupero i dati Cam dell'entità CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) return false ; // recupero i valori degli assi DBLVECTOR vAxCurr = pCamData->GetAxesVal() ; // se provengo da Z massima if ( bMaxZ) { // copio l'entità if ( m_pGeomDB->Copy( nEntId, GDB_ID_NULL, nEntId, GDB_AFTER) == GDB_ID_NULL) return false ; // modifico quella originale (è la prima del percorso) m_pGeomDB->SetName( nEntId, MCH_CL_CLIMB) ; DBLVECTOR vAxNew = vAxCurr ; vAxNew[2] = vAxPrev[2] ; // eventuali aggiustamenti speciali dipendenti dalla macchina Vector3d vtTool = pCamData->GetToolDir() ; DBLVECTOR vAxTmp = vAxNew ; bool bModif ; if ( ! SpecialMoveZup( vtTool, vAxTmp, bModif)) return false ; if ( bModif) { pCamData->SetToolDir( vtTool) ; vAxNew = vAxTmp ; } // applico le modifiche (non forzo emissione della Z che è già massima) pCamData->SetAxes( CamData::AS_OK, vAxNew) ; pCamData->ChangeAxesMask( CamData::MSK_L1 | CamData::MSK_L2 | CamData::MSK_R1 | CamData::MSK_R2 | CamData::MSK_R3) ; pCamData->SetFlag( 2) ; } // verifico se la testa interferisce con i pezzi o i bloccaggi sulla tavola else { // determino la Z più alta tra le due posizioni double dTopZ = max( vAxPrev[2], vAxCurr[2]) ; // per il test uso posizioni temporanee con questa Z DBLVECTOR vAxPrevTmp = vAxPrev ; vAxPrevTmp[2] = dTopZ ; DBLVECTOR vAxCurrTmp = vAxCurr ; vAxCurrTmp[2] = dTopZ ; // se interferisce if ( ! TestCollisionAvoid( vAxPrevTmp, vAxCurrTmp)) { // recupero HomeZ double dHomeZ ; if ( ! m_pMchMgr->GetCurrAxisHomePos( 2, dHomeZ)) return false ; // riprovo con una Z intermedia double dSafeZ = dHomeZ ; double dUnsafeZ = dTopZ ; for ( int i = 1 ; i <= 4 ; ++ i) { double dTestSafeZ = ( dUnsafeZ + dSafeZ) / 2 ; vAxPrevTmp[2] = dTestSafeZ ; vAxCurrTmp[2] = dTestSafeZ ; if ( TestCollisionAvoid( vAxPrevTmp, vAxCurrTmp)) dSafeZ = dTestSafeZ ; else dUnsafeZ = dTestSafeZ ; } // risalita sopra fine percorso precedente DBLVECTOR vAxNew1 ; if ( ! AddRise( vAxNew1, dSafeZ - vAxPrev[2], nPrevClPathId)) return false ; // aggiungo posizione elevata prima dell'inizio del percorso corrente if ( m_pGeomDB->Copy( nEntId, GDB_ID_NULL, nEntId, GDB_AFTER) == GDB_ID_NULL) return false ; // modifico l'entità originale (è la prima del percorso) m_pGeomDB->SetName( nEntId, MCH_CL_CLIMB) ; DBLVECTOR vAxNew = vAxCurr ; vAxNew[2] = vAxNew1[2] ; // eventuali aggiustamenti speciali dipendenti dalla macchina Vector3d vtTool = pCamData->GetToolDir() ; DBLVECTOR vAxTmp = vAxNew ; bool bModif ; if ( ! SpecialMoveZup( vtTool, vAxTmp, bModif)) return false ; if ( bModif) { pCamData->SetToolDir( vtTool) ; vAxNew = vAxTmp ; } // applico le modifiche (non forzo emissione della Z che è già alta) pCamData->SetAxes( CamData::AS_OK, vAxNew) ; pCamData->ChangeAxesMask( CamData::MSK_L1 | CamData::MSK_L2 | CamData::MSK_R1 | CamData::MSK_R2 | CamData::MSK_R3) ; pCamData->SetFlag( 2) ; } // altrimenti else { // se Z pressochè uguali non devo fare alcunché if ( fabs( vAxCurr[2] - vAxPrev[2]) <= 100 * EPS_SMALL) return true ; // se Z corrente maggiore della precedente else if ( vAxCurr[2] > vAxPrev[2]) { // devo essere sulla stessa lavorazione if ( nPrevClPathId == GDB_ID_NULL) return false ; // aggiungo risalita alla fine del precedente percorso DBLVECTOR vAxNew ; if ( ! AddRise( vAxNew, vAxCurr[2] - vAxPrev[2], nPrevClPathId)) return false ; } // altrimenti Z corrente minore della precedente else { // aggiungo posizione elevata prima dell'inizio del percorso corrente if ( m_pGeomDB->Copy( nEntId, GDB_ID_NULL, nEntId, GDB_AFTER) == GDB_ID_NULL) return false ; // modifico l'entità originale (è la prima del percorso) m_pGeomDB->SetName( nEntId, MCH_CL_CLIMB) ; DBLVECTOR vAxNew = vAxCurr ; vAxNew[2] = vAxPrev[2] ; // eventuali aggiustamenti speciali dipendenti dalla macchina Vector3d vtTool = pCamData->GetToolDir() ; DBLVECTOR vAxTmp = vAxNew ; bool bModif ; if ( ! SpecialMoveZup( vtTool, vAxTmp, bModif)) return false ; if ( bModif) { pCamData->SetToolDir( vtTool) ; vAxNew = vAxTmp ; } // applico le modifiche (non forzo emissione della Z che è già alta) pCamData->SetAxes( CamData::AS_OK, vAxNew) ; pCamData->ChangeAxesMask( CamData::MSK_L1 | CamData::MSK_L2 | CamData::MSK_R1 | CamData::MSK_R2 | CamData::MSK_R3) ; pCamData->SetFlag( 2) ; } } } return true ; } //---------------------------------------------------------------------------- bool Operation::ToolChangeNeeded( const Operation& Op1, const Operation& Op2) { // se non cambia l'utensile, non cambia la testa fisica if ( EqualNoCase( Op1.GetToolName(), Op2.GetToolName())) return false ; // se non hanno TcPos e stanno sulla stessa testa con uscita diversa, non cambia la testa fisica if ( Op1.GetToolTcPos().empty() && Op2.GetToolTcPos().empty() && EqualNoCase( Op1.GetHeadName(), Op2.GetHeadName()) && Op1.GetExitNbr() != Op2.GetExitNbr()) return false ; // se hanno lo stesso TcPos e stanno sulla stessa testa con uscita diversa, non cambia la testa fisica if ( EqualNoCase( Op1.GetToolTcPos(), Op2.GetToolTcPos()) && EqualNoCase( Op1.GetHeadName(), Op2.GetHeadName()) && Op1.GetExitNbr() != Op2.GetExitNbr()) return false ; // altrimenti necessario un cambio return true ; } //---------------------------------------------------------------------------- bool Operation::RemoveClimb( int nClPathId) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // elimino tutte le entità CLIMB all'inizio del percorso int nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_CLIMB) ; while ( nId != GDB_ID_NULL) { m_pGeomDB->Erase( nId) ; nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_CLIMB) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::AddRise( DBLVECTOR& vAxVal, double dDelta, int nClPathId) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero gruppo per geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ; if ( nClId == GDB_ID_NULL) return false ; // se percorso CL specificato, verifico appartenga al gruppo sopra ricavato if ( nClPathId != GDB_ID_NULL) { if ( m_pGeomDB->GetParentId( nClPathId) != nClId) return false ; } // altrimenti, recupero l'ultimo percorso CL della operazione else { nClPathId = m_pGeomDB->GetLastGroupInGroup( nClId) ; if ( nClPathId == GDB_ID_NULL) return false ; } // recupero l'ultima entità del percorso int nEntId = m_pGeomDB->GetLastInGroup( nClPathId) ; if ( nEntId == GDB_ID_NULL) return false ; // ne recupero i dati Cam CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) return false ; // creo oggetto punto per DB geometrico PtrOwner pGP( CreateGeoPoint3d()) ; if ( IsNull( pGP)) return false ; // assegno le coordinate del punto pGP->Set( pCamData->GetEndPoint()) ; // inserisco l'oggetto nel DB geometrico int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nClPathId, Release( pGP)) ; if ( nId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nId, MCH_CL_RISE) ; // creo oggetto dati Cam da associare al punto PtrOwner pCam( pCamData->Clone()) ; if ( IsNull( pCam)) return false ; // recupero i valori degli assi vAxVal = pCam->GetAxesVal() ; if ( vAxVal.size() < 3) return false ; // flag per tipo di movimento in rapido int nFlag = 0 ; // se delta positivo lo uso come incremento di Z if ( dDelta > 0) { vAxVal[2] += dDelta ; nFlag = 0 ; // movimento standard } // altrimenti uso la Z home else { // recupero posizione home DBLVECTOR vAxHome ; m_pMchMgr->GetAllCurrAxesHomePos( vAxHome) ; vAxVal[2] = vAxHome[2] ; nFlag = 3 ; // movimento a Zmax } // eventuale aggiustamenti speciali dipendenti dalla macchina Vector3d vtTool = pCam->GetToolDir() ; DBLVECTOR vAxTmp = vAxVal ; bool bModif ; if ( ! SpecialMoveZup( vtTool, vAxTmp, bModif)) return false ; if ( bModif) { pCam->SetToolDir( vtTool) ; vAxVal = vAxTmp ; } // verifico extra-corsa dell'asse Z double dL1 = vAxVal[0] ; double dL2 = vAxVal[1] ; double dL3 = vAxVal[2] ; DBLVECTOR vAng ; for ( int i = 0 ; i < int( vAxVal.size()) - 3 ; ++ i) vAng.emplace_back( vAxVal[i+3]) ; int nStat ; bool bOk = ( m_pMchMgr->VerifyOutstroke( dL1, dL2, dL3, vAng, false, nStat) && nStat == 0) ; // assegno i dati pCam->SetAxes( (bOk ? CamData::AS_OK : CamData::AS_OUTSTROKE), vAxVal) ; pCam->SetMoveType( 0) ; pCam->SetFeed( 0) ; pCam->SetFlag( nFlag) ; // associo questo oggetto a quello geometrico m_pGeomDB->SetUserObj( nId, Release( pCam)) ; return bOk ; } //---------------------------------------------------------------------------- bool Operation::RemoveRise( int nClPathId) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero gruppo per geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ; if ( nClId == GDB_ID_NULL) return false ; // se percorso CL specificato, verifico appartenga al gruppo sopra ricavato if ( nClPathId != GDB_ID_NULL) { if ( m_pGeomDB->GetParentId( nClPathId) != nClId) return false ; } // altrimenti, recupero l'ultimo percorso CL della operazione else { nClPathId = m_pGeomDB->GetLastGroupInGroup( nClId) ; if ( nClPathId == GDB_ID_NULL) return false ; } // elimino tutte le entità RISE alla fine del percorso int nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_RISE) ; while ( nId != GDB_ID_NULL) { m_pGeomDB->Erase( nId) ; nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_RISE) ; } // elimino a maggior ragione eventuali entità HOME nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_HOME) ; while ( nId != GDB_ID_NULL) { m_pGeomDB->Erase( nId) ; nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_HOME) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::AddHome( void) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero gruppo per geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ; if ( nClId == GDB_ID_NULL) return false ; // recupero l'ultimo percorso int nLastPxClId = m_pGeomDB->GetLastGroupInGroup( nClId) ; // recupero l'ultima entità del percorso di nome RISE int nEntId = m_pGeomDB->GetLastNameInGroup( nLastPxClId, MCH_CL_RISE) ; if ( nEntId == GDB_ID_NULL) return false ; // ne recupero i dati Cam CamData* pCamData = GetCamData( m_pGeomDB->GetUserObj( nEntId)) ; if ( pCamData == nullptr) return false ; // creo oggetto punto per DB geometrico PtrOwner pGP( CreateGeoPoint3d()) ; if ( IsNull( pGP)) return false ; // assegno le coordinate del punto pGP->Set( pCamData->GetEndPoint()) ; // inserisco l'oggetto nel DB geometrico int nId = m_pGeomDB->AddGeoObj( GDB_ID_NULL, nLastPxClId, Release( pGP)) ; if ( nId == GDB_ID_NULL) return false ; m_pGeomDB->SetName( nId, MCH_CL_HOME) ; // creo oggetto dati Cam PtrOwner pCam( pCamData->Clone()) ; if ( IsNull( pCam)) return false ; // flag per tipo di movimento in rapido (rapido a Home) int nFlag = 4 ; // recupero coordinate home DBLVECTOR vAxHome ; m_pMchMgr->GetAllCurrAxesHomePos( vAxHome) ; // assegno i dati pCam->SetAxes( CamData::AS_OK, vAxHome) ; pCam->SetMoveType( 0) ; pCam->SetFeed( 0) ; pCam->SetFlag( nFlag) ; // associo questo oggetto a quello geometrico m_pGeomDB->SetUserObj( nId, Release( pCam)) ; return true ; } //---------------------------------------------------------------------------- bool Operation::RemoveHome( void) { if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero gruppo per geometria di lavorazione (Cutter Location) int nClId = m_pGeomDB->GetFirstNameInGroup( GetOwner(), MCH_CL) ; if ( nClId == GDB_ID_NULL) return false ; // recupero l'ultimo percorso CL int nClPathId = m_pGeomDB->GetLastGroupInGroup( nClId) ; // elimino tutte le entità HOME alla fine del percorso int nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_HOME) ; while ( nId != GDB_ID_NULL) { m_pGeomDB->Erase( nId) ; nId = m_pGeomDB->GetFirstNameInGroup( nClPathId, MCH_CL_HOME) ; } return true ; } //---------------------------------------------------------------------------- bool Operation::CalcDeltaZForHeadRotation( const DBLVECTOR& vAxStart, const DBLVECTOR& vAxEnd, double& dDeltaZ) { if ( m_pMchMgr == nullptr) return false ; // il numero di assi deve essere costante if ( vAxStart.size() != vAxEnd.size()) return false ; // se ci sono solo assi lineari, pongo delta Z nullo if ( vAxStart.size() <= 3) { dDeltaZ = 0 ; return true ; } // determino gli estremi del movimento rotatorio double dR1Start = ( vAxStart.size() >= 4 ? vAxStart[3] : 0) ; double dR2Start = ( vAxStart.size() >= 5 ? vAxStart[4] : 0) ; double dR3Start = ( vAxStart.size() >= 6 ? vAxStart[5] : 0) ; double dR1End = ( vAxEnd.size() >= 4 ? vAxEnd[3] : 0) ; double dR2End = ( vAxEnd.size() >= 5 ? vAxEnd[4] : 0) ; double dR3End = ( vAxEnd.size() >= 6 ? vAxEnd[5] : 0) ; // determino le variazioni di ciascun asse double dR1Delta = dR1End - dR1Start ; double dR2Delta = dR2End - dR2Start ; double dR3Delta = dR3End - dR3Start ; // divido il movimento in step di massimo 5 gradi int nStep = int( max( abs( dR1Delta), max( abs( dR2Delta), abs( dR3Delta))) / 5.0) + 1 ; double dR1Step = dR1Delta / nStep ; double dR2Step = dR2Delta / nStep ; double dR3Step = dR3Delta / nStep ; // calcolo double dL1 = vAxStart[0] ; double dL2 = vAxStart[1] ; double dL3 = vAxStart[2] ; double dZStart, dZmin ; DBLVECTOR vAng( 3) ; for ( int i = 0 ; i <= nStep ; ++ i) { vAng[0] = dR1Start + i * dR1Step ; vAng[1] = dR2Start + i * dR2Step ; vAng[2] = dR3Start + i * dR2Step ; Point3d ptTip ; if ( ! m_pMchMgr->GetCalcTipFromPositions( dL1, dL2, dL3, vAng, true, true, ptTip)) return false ; if ( i == 0) { dZStart = ptTip.z ; dZmin = dZStart ; } else if ( ptTip.z < dZmin) dZmin = ptTip.z ; } dDeltaZ = dZStart - dZmin ; return true ; } //---------------------------------------------------------------------------- bool Operation::TestCollisionAvoid( const DBLVECTOR& vAxStart, const DBLVECTOR& vAxEnd) { // Recupero macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // Recupero tavola corrente string sTable ; if ( ! pMch->GetCurrTable( sTable)) return false ; // Recupero assi correnti STRVECTOR vAxName ; pMch->GetAllCurrAxesName( vAxName) ; // il numero di assi deve essere costante if ( vAxName.size() != vAxStart.size() || vAxName.size() != vAxEnd.size()) return false ; // Se testa con Info ZMAXONROT != 0 e movimento assi rotanti -> collisione int nHeadId = pMch->GetCurrHead() ; DBLVECTOR vdVal ; if ( m_pGeomDB->GetInfo( nHeadId, MCH_ZMAXONROT, vdVal) && vdVal.size() >= 1 && lround(vdVal[0]) > 0) { double dAngTol = 100 * EPS_ANG_SMALL ; if ( vdVal.size() >= 2) dAngTol = max( vdVal[1], dAngTol) ; int nLinAxes = m_pMchMgr->GetCurrLinAxes() ; int nRotAxes = m_pMchMgr->GetCurrRotAxes() ; for ( int i = nLinAxes ; i < nLinAxes + nRotAxes ; ++ i) { if ( int( vAxName.size()) > i && abs( vAxEnd[i] - vAxStart[i]) > dAngTol) return false ; } } // Porto la macchina in home pMch->ResetAllAxesPos() ; // Elenco grezzi attivi INTVECTOR vRawId ; int nRawId = m_pMchMgr->GetFirstRawPart() ; while ( nRawId != GDB_ID_NULL) { if ( m_pMchMgr->VerifyRawPartPhase( nRawId, m_nPhase)) vRawId.emplace_back( nRawId) ; nRawId = m_pMchMgr->GetNextRawPart( nRawId) ; } // Aggancio pezzi attivi alla tavola corrente for ( const auto nRawId : vRawId) pMch->LinkRawPartToGroup( nRawId, sTable) ; // Elenco bloccaggi attivi INTVECTOR vFxtId ; int nFxtId = m_pMchMgr->GetFirstFixture() ; while ( nFxtId != GDB_ID_NULL) { vFxtId.emplace_back( nFxtId) ; nFxtId = m_pMchMgr->GetNextFixture( nFxtId) ; } // Aggancio bloccaggi alla tavola corrente for ( const auto nFxtId : vFxtId) pMch->LinkFixtureToGroup( nFxtId, sTable) ; // distanza di sicurezza double dSafeDist = m_pMchMgr->GetCurrMachiningsMgr()->GetSafeZ() - 100 * EPS_SMALL ; // Vado nelle posizioni da controllare const int COLL_STEP = 16 ; bool bCollide = false ; for ( int i = 0 ; i < COLL_STEP && ! bCollide ; ++ i) { // Imposto la posizione double dCoeff = double( i) / COLL_STEP ; for ( size_t j = 0 ; j < vAxStart.size() ; ++ j) { double dPos = ( 1 - dCoeff) * vAxStart[j] + dCoeff * vAxEnd[j] ; pMch->SetAxisPos( vAxName[j], dPos) ; } // Determino sottobox e box della testa e degli altri oggetti da considerare per la collisione BOXIVECTOR vbiSH ; BBox3d b3Head ; INTVECTOR vCollId ; pMch->GetCurrHeadCollGroups( vCollId) ; for each ( int nCId in vCollId) { int nHId = m_pGeomDB->GetFirstInGroup( nCId) ; while ( nHId != GDB_ID_NULL) { BBox3d b3Tmp ; m_pGeomDB->GetGlobalBBox( nHId, b3Tmp, BBF_ONLY_VISIBLE) ; if ( ! b3Tmp.IsEmpty()) { b3Tmp.Expand( dSafeDist) ; b3Head.Add( b3Tmp) ; vbiSH.emplace_back( b3Tmp, nHId) ; } nHId = m_pGeomDB->GetNext( nHId) ; } } // Li confronto con i grezzi for ( const auto nRawId : vRawId) { // verifico i box BBox3d b3Raw ; int nRawSolidId = m_pGeomDB->GetFirstNameInGroup( nRawId, MACH_RAW_SOLID) ; if ( m_pGeomDB->GetGlobalBBox( nRawSolidId, b3Raw) && b3Head.Overlaps( b3Raw)) { // riferimento del grezzo Frame3d frSolid ; m_pGeomDB->GetGlobFrame( nRawSolidId, frSolid) ; // verifico i sottobox for ( const auto biSH : vbiSH) { if ( biSH.first.Overlaps( b3Raw)) { // sottobox testa nel riferimento del solido BBox3d b3Hsol ; m_pGeomDB->GetRefBBox( biSH.second, frSolid, b3Hsol) ; b3Hsol.Expand( dSafeDist) ; // solido const ISurfTriMesh* pStm = GetSurfTriMesh( m_pGeomDB->GetGeoObj( nRawSolidId)) ; // verifica di collisione tra sottobox e solido if ( pStm == nullptr || CDBoxPolyhedron( b3Hsol, *pStm)) { bCollide = true ; break ; } } } if ( bCollide) break ; } } // Se non trovata collisione, li confronto con i bloccaggi if ( ! bCollide) { for ( const auto nFxtId : vFxtId) { // verifico i box BBox3d b3Fxt ; int nFlag = BBF_ONLY_VISIBLE | BBF_IGNORE_TEXT | BBF_IGNORE_DIM ; if ( m_pGeomDB->GetGlobalBBox( nFxtId, b3Fxt, nFlag) && b3Head.Overlaps( b3Fxt)) { // verifico i sottobox for ( const auto biSH : vbiSH) { if ( biSH.first.Overlaps( b3Fxt)) { bCollide = true ; break ; } } if ( bCollide) break ; } } } } // Riporto la macchina in home pMch->ResetAllAxesPos() ; // Sgancio pezzi, grezzi e sottopezzi dalla tavola corrente pMch->UnlinkAllPartsFromGroups() ; pMch->UnlinkAllRawPartsFromGroups() ; pMch->UnlinkAllFixturesFromGroups() ; return ( ! bCollide) ; } //---------------------------------------------------------------------------- bool Operation::SpecialMoveZup( Vector3d& vtTool, DBLVECTOR& vAx, bool& bModif) { // recupero la macchina corrente Machine* pMch = ( m_pMchMgr != nullptr ? m_pMchMgr->GetCurrMachine() : nullptr) ; if ( pMch == nullptr) return false ; // costanti static const string EMC_VAR = "EMC" ; // tabella variabili locali per calcolo static const string EVAR_ERROR = ".ERR" ; // OUT (int) codice di errore ( 0 = ok) static const string EVAR_MODIF = ".MODIF" ; // OUT (bool) flag di modifica effettuata static const string ON_SPECIAL_MOVEZUP = "OnSpecialMoveZup" ; // eseguo l'azione if ( pMch->LuaExistsFunction( ON_SPECIAL_MOVEZUP)) { bool bOk = true ; int nErr = 99 ; // imposto valori parametri bOk = bOk && pMch->LuaCreateGlobTable( EMC_VAR) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TOOL, GetToolName()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_HEAD, GetHeadName()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_EXIT, GetExitNbr()) ; bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TCPOS, GetToolTcPos()) ; // direzione utensile bOk = bOk && pMch->LuaSetGlobVar( EMC_VAR + GVAR_TDIR, vtTool) ; // valore degli assi int nNumAxes = int( vAx.size()) ; for ( int i = 1 ; i <= nNumAxes ; ++ i) bOk = bOk && pMch->LuaSetGlobVar( GetGlobVarAxisValue( i, EMC_VAR), vAx[i-1]) ; // eseguo bOk = bOk && pMch->LuaCallFunction( ON_SPECIAL_MOVEZUP, false) ; // recupero valori parametri obbligatori bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + EVAR_ERROR, nErr) ; bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + EVAR_MODIF, bModif) ; bOk = bOk && pMch->LuaGetGlobVar( EMC_VAR + GVAR_TDIR, vtTool) ; for ( int i = 1 ; i <= nNumAxes ; ++ i) bOk = bOk && pMch->LuaGetGlobVar( GetGlobVarAxisValue( i, EMC_VAR), vAx[i-1]) ; // reset bOk = bOk && pMch->LuaResetGlobVar( EMC_VAR) ; // segnalo errori if ( nErr != 0) { bOk = false ; string sOut = " Error in " + ON_SPECIAL_MOVEZUP + " (" + ToString( nErr) + ")" ; LOG_INFO( GetEMkLogger(), sOut.c_str()) } return bOk ; } else { bModif = false ; return true ; } } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- bool Operation::GetAggrBottomData( const string& sHead, AggrBottom& agbData) { // inizializzo la struttura agbData.Clear() ; // verifiche if ( m_pMchMgr == nullptr || m_pGeomDB == nullptr) return false ; // recupero identificativo della testa int nHeadId = m_pMchMgr->GetHeadId( sHead) ; if ( nHeadId == GDB_ID_NULL) return false ; // leggo i dati m_pGeomDB->GetInfo( nHeadId, MCH_AGB_TYPE, agbData.nType) ; m_pGeomDB->GetInfo( nHeadId, MCH_AGB_DMAX, agbData.dDMax) ; m_pGeomDB->GetInfo( nHeadId, MCH_AGB_ENCH, agbData.dEncH) ; m_pGeomDB->GetInfo( nHeadId, MCH_AGB_ENCV, agbData.dEncV) ; return true ; }