-- FeatureLib.lua by Egalware s.r.l. 2024/04/02 -- Libreria lettura o calcolo dati e proprietà della feature -- 2024/04/02 PRIMA VERSIONE CALCOLO LAVORAZIONI CON STRATEGIE -- Tabella per definizione modulo local FeatureLib = {} -- Carico i dati globali local BeamData = require( 'BeamData') -- carico librerie local BeamLib = require( 'BeamLib') local FaceData = require( 'FaceData') local ID = require( 'Identity') ------------------------------------------------------------------------------------------------------------- function FeatureLib.GetProcFromTrimesh( idTrimesh, Part, ProcToCopyFrom) local Proc = {} -- eventuale copia dati da ProcToCopyFrom in arrivo if type( ProcToCopyFrom) == "table" then Proc = BeamLib.TableCopyDeep( ProcToCopyFrom) end -- dati specifici della Proc Proc.id = idTrimesh Proc.nFct = EgtSurfTmFacetCount( Proc.id) or 0 Proc.b3Box = EgtGetBBoxGlob( idTrimesh or GDB_ID.NULL, GDB_BB.STANDARD) Proc.AffectedFaces = BeamLib.GetAffectedFaces( Proc, Part) Proc.AdjacencyMatrix = FaceData.GetAdjacencyMatrix( Proc) Proc.Faces = FaceData.GetFacesInfo( Proc, Part) -- TODO servono anche altri dati raccolti nel collect?? return Proc end --------------------------------------------------------------------- -- recupero topologia della feature function FeatureLib.NeedTopologyFeature( Proc) -- features tipo taglio if ID.IsCut( Proc) then return true elseif ID.IsHeadCut( Proc) then return true elseif ID.IsTailCut( Proc) then return true elseif ID.IsDoubleCut( Proc) then return true elseif ID.IsSawCut( Proc) then return true -- features tipo taglio longitudinale elseif ID.IsLongitudinalCut( Proc) then return true elseif ID.IsDoubleLongitudinalCut( Proc) then return true elseif ID.IsChamfer( Proc) then return true -- features tipo LapJoint elseif ID.IsSlot( Proc) then return true elseif ID.IsFrontSlot( Proc) then return true elseif ID.IsBirdsMouth( Proc) then return true elseif ID.IsLapJoint( Proc) then return true elseif ID.IsNotchRabbet( Proc) then return true elseif ID.IsNotch( Proc) then return true elseif ID.IsPocket( Proc) then return true -- calcolo topologia da geometria SOLO se non raggiata elseif ID.IsMortise( Proc) and Proc.nFct < 6 then return true -- calcolo topologia da geometria SOLO se non raggiata elseif ID.IsFrontMortise( Proc) and Proc.nFct < 6 then return true elseif ID.IsBlockHaus( Proc) then return true end -- se feature non tra quelle sopra, riconoscimento topologico non necessario return false end --------------------------------------------------------------------- -- restituisce true se Proc ha tutti gli angoli concavi (bAllConcave) e, nei casi in cui ha senso, se questi sono esattamente 90 deg (bAllRight) local function AreAllAnglesConcaveOrRight( vAdj) -- se la feature ha una sola faccia, esco subito if #vAdj <= 1 then return end local bAllConcave, bAllRight = true, true local nFct = #( vAdj or {}) for i = 1, nFct do for j = 1, nFct do -- se trovo un angolo convesso restituisco falso e esco subito if vAdj[i][j] and vAdj[i][j] > 0 then bAllConcave = false bAllRight = false break elseif vAdj[i][j] and vAdj[i][j] ~= 0 and vAdj[i][j] + 90 > 500 * GEO.EPS_ANG_SMALL then bAllRight = false end end end -- se 1 faccia oppure 2 facce con angolo convesso non ha senso ritornare valori per bAllRight if nFct < 2 or ( nFct == 2 and vAdj[1][2] > 0) then return bAllConcave else return bAllConcave, bAllRight end end --------------------------------------------------------------------- -- restituisce true se almeno una delle dimensioni della feature è maggiore o uguale ad una delle dimensioni principali del pezzo (tolleranza 1 mm) local function IsAnyDimensionLongAsPart( Proc, Part) local bResult = false if Proc.b3Box:getDimX() > Part.b3Part:getDimX() - 1000 * GEO.EPS_SMALL or Proc.b3Box:getDimY() > Part.b3Part:getDimY() - 1000 * GEO.EPS_SMALL or Proc.b3Box:getDimZ() > Part.b3Part:getDimZ() - 1000 * GEO.EPS_SMALL then bResult = true end return bResult end ------------------------------------------------------------------------------------------------------------- -- restituisce vero se la feature con box b3Proc taglia l'intera sezione della barra local function IsFeatureCuttingEntireSection( b3Proc, Part) return ( b3Proc:getDimY() > ( Part.b3Part:getDimY() - 500 * GEO.EPS_SMALL) and b3Proc:getDimZ() > ( Part.b3Part:getDimZ() - 500 * GEO.EPS_SMALL)) end ------------------------------------------------------------------------------------------------------------- -- restituisce vero se la feature con box b3Proc taglia l'intera lunghezza della barra local function IsFeatureCuttingEntireLength( b3Proc, Part) return ( ( b3Proc:getDimY() > ( Part.b3Part:getDimY() - 500 * GEO.EPS_SMALL) and b3Proc:getDimX() > ( Part.b3Part:getDimX() - 500 * GEO.EPS_SMALL)) or ( b3Proc:getDimZ() > ( Part.b3Part:getDimZ() - 500 * GEO.EPS_SMALL) and b3Proc:getDimX() > ( Part.b3Part:getDimX() - 500 * GEO.EPS_SMALL))) end --------------------------------------------------------------------- -- restituisce una stringa con il nome esteso della topologia della feature -- *famiglia - numero di facce - passante* local function GetTopologyName( sFamily, nNumberOfFaces, bIsThrough) return sFamily .. '-' .. tostring( nNumberOfFaces) .. '-' .. EgtIf( bIsThrough, 'Through', 'Blind') end --------------------------------------------------------------------- -- recupera topologia feature function FeatureLib.ClassifyTopology( Proc, Part) local FeatureTopology = {} if not Proc.AffectedFaces then Proc.AffectedFaces = BeamLib.GetAffectedFaces( Proc, Part) end local bIsFeatureCuttingEntireSection = IsFeatureCuttingEntireSection( Proc.b3Box, Part) local bIsFeatureCuttingEntireLength = IsFeatureCuttingEntireLength( Proc.b3Box, Part) local bIsAnyDimensionLongAsPart = IsAnyDimensionLongAsPart( Proc, Part) local vAdj = Proc.AdjacencyMatrix local bAllAnglesConcave, bAllRightAngles = AreAllAnglesConcaveOrRight( vAdj) local vTriangularFaces = FaceData.GetTriangularFaces( Proc) local vFacesByAdjNumber = FaceData.GetFacesByAdjacencyNumber( Proc) local sFamily local bIsThrough if Proc.nFct == 1 and ( bIsFeatureCuttingEntireSection or bIsFeatureCuttingEntireLength) then sFamily = 'Cut' bIsThrough = true elseif Proc.nFct == 1 then sFamily = 'Bevel' bIsThrough = true elseif Proc.nFct == 2 and bAllAnglesConcave and #vTriangularFaces == 1 then sFamily = 'Bevel' bIsThrough = false elseif Proc.nFct == 2 and bAllAnglesConcave and ( Proc.AffectedFaces.bLeft or Proc.AffectedFaces.bRight) and ( Proc.AffectedFaces.bFront or Proc.AffectedFaces.bBack) then sFamily = 'Rabbet' bIsThrough = true elseif Proc.nFct == 2 and bAllAnglesConcave then sFamily = 'VGroove' bIsThrough = true elseif Proc.nFct == 2 and not bAllAnglesConcave and bIsAnyDimensionLongAsPart then sFamily = 'DoubleBevel' bIsThrough = true elseif Proc.nFct == 3 and bAllAnglesConcave and #vFacesByAdjNumber[2] == 1 and #vTriangularFaces == 2 then sFamily = 'Bevel' bIsThrough = false elseif Proc.nFct == 3 and bAllAnglesConcave and #vFacesByAdjNumber[2] == 1 and bIsAnyDimensionLongAsPart then sFamily = 'Groove' bIsThrough = true elseif Proc.nFct == 3 and bAllAnglesConcave and #vFacesByAdjNumber[2] == 3 then sFamily = 'Groove' bIsThrough = false elseif Proc.nFct == 4 and #vFacesByAdjNumber[2] == 4 and #vTriangularFaces == 2 then sFamily = 'DoubleBevel' bIsThrough = false elseif Proc.nFct == 4 and bAllAnglesConcave and #vFacesByAdjNumber[3] == 2 then sFamily = 'Groove' bIsThrough = false elseif Proc.nFct == 4 and bAllAnglesConcave and #vFacesByAdjNumber[2] == 4 and bIsAnyDimensionLongAsPart then sFamily = 'Tunnel' bIsThrough = true elseif Proc.nFct >= 4 and #vFacesByAdjNumber[1] == 2 and bIsAnyDimensionLongAsPart then sFamily = 'Strip' bIsThrough = true elseif Proc.nFct == 5 and bAllAnglesConcave and #vFacesByAdjNumber[4] == 1 then sFamily = 'Pocket' bIsThrough = false elseif Proc.nFct == 6 and #vFacesByAdjNumber[2] == 4 and #vFacesByAdjNumber[3] == 2 and #vTriangularFaces == 4 then sFamily = 'DoubleBevel' bIsThrough = false end if sFamily then FeatureTopology.sFamily = sFamily FeatureTopology.bIsThrough = bIsThrough FeatureTopology.bAllRightAngles = bAllRightAngles FeatureTopology.sName = GetTopologyName( sFamily, Proc.nFct, bIsThrough) FeatureTopology.AdjacencyMatrix = vAdj -- feature che necessita di essere catalogata, ma non si capisce come else FeatureTopology.sFamily = 'NOT_IMPLEMENTED' FeatureTopology.sName = FeatureTopology.sFamily end return FeatureTopology end --------------------------------------------------------------------- -- recupera classificazione feature (da info BTL, non geometrica) function FeatureLib.GetTopologyFromFeature( Proc, Part) local Topology = {} Topology.sFamily = 'FEATURE' -- per feature 'Mortasa' si setta topologia da info BTL se raggiata if ID.IsMortise( Proc) or ID.IsFrontMortise( Proc) then Topology.sName = 'Pocket-Round' if Proc.FeatureInfo.bIsFrontMortise then Topology.sName = Topology.sName .. '-Front' elseif Proc.FeatureInfo.bIsMortiseThrough then Topology.sName = Topology.sName .. '-Through' end else Topology.sName = 'FEATURE' end return Topology end ------------------------------------------------------------------------------------------------------------- function FeatureLib.GetAdditionalInfo( Proc, Part) Proc.FeatureInfo = {} -- se foro if ID.IsDrilling( Proc) then Proc.FeatureInfo = FeatureLib.GetDrillingData( Proc) -- se tenone o tenone a coda di rondine elseif ID.IsTenon( Proc) or ID.IsDovetailTenon( Proc) then Proc.FeatureInfo = FeatureLib.GetTenonData( Proc) -- se mortasa a coda di rondine o mortasa frontale a coda di rondine elseif ID.IsDovetailMortise( Proc) or ID.IsFrontDovetailMortise( Proc) then Proc.FeatureInfo = FeatureLib.GetDTMortiseData( Proc) -- se mortasa a coda di rondine o mortasa frontale a coda di rondine elseif ID.IsMortise( Proc) or ID.IsFrontMortise( Proc) then Proc.FeatureInfo = FeatureLib.GetMortiseData( Proc, Part) end return Proc end ------------------------------------------------------------------------------------------------------------- -- Recupero dati foro e adattamento se speciale function FeatureLib.GetDrillingData( Proc) local AuxId = EgtGetInfo( Proc.id, 'AUXID', 'i') local FeatureExtraInfo = {} -- verifico se foro da adattare if EgtExistsInfo( Proc.id, 'DiamUser') then if AuxId then AuxId = AuxId + Proc.id end if AuxId and EgtGetType( AuxId) == GDB_TY.CRV_ARC and BeamData.USER_HOLE_DIAM and BeamData.USER_HOLE_DIAM > 1 then EgtModifyArcRadius( AuxId, BeamData.USER_HOLE_DIAM / 2) end end FeatureExtraInfo.dDrillDiam = EgtGetInfo( Proc.id, 'P12', 'd') or 0 FeatureExtraInfo.dDrillLen = abs( EgtCurveThickness( Proc.id + AuxId)) or 0 FeatureExtraInfo.nDrillFcs = EgtGetInfo( Proc.id, 'FCS', 'i') or 0 FeatureExtraInfo.nDrillFce = EgtGetInfo( Proc.id, 'FCE', 'i') or 0 return FeatureExtraInfo end ------------------------------------------------------------------------------------------------------------- -- Recupero dati tenone e tenone a coda di rondine function FeatureLib.GetTenonData( Proc) local FeatureExtraInfo = {} -- recupero e verifico l'entità curva local idAux = EgtGetInfo( Proc.id, 'AUXID', 'i') if idAux then idAux = idAux + Proc.id end -- recupero i dati della curva local vtN = EgtCurveExtrusion( idAux, GDB_RT.GLOB) local ptBC = EgtGP( idAux, GDB_RT.GLOB) -- determino altezza del tenone local frTen = Frame3d( ptBC, vtN) local b3Ten = EgtGetBBoxRef( Proc.id, GDB_BB.STANDARD, frTen) local dTenH = b3Ten:getDimZ() -- assegno centro e normale della faccia top local ptC = ptBC + vtN * dTenH -- calcolo distanza massima della curva dal punto più lontano della base tenone (facet 0) local dMaxDist for i = 0, Proc.nFct - 1 do local ptFC, vtFN = EgtSurfTmFacetCenter( Proc.id, i, GDB_ID.ROOT) if not AreSameVectorApprox( vtFN, vtN) or abs( ( ptFC - ptBC) * vtN) > 100 * GEO.EPS_SMALL then break end local nLoopId, nLoopCnt = EgtExtractSurfTmFacetLoops( Proc.id, i, EgtGetParent( Proc.id)) if nLoopId then local dUmin, dUmax = EgtCurveDomain( nLoopId) for dU = dUmin, dUmax do local ptP = EgtUP( nLoopId, dU, GDB_ID.ROOT) local ptNear = EgtNP( idAux, ptP, GDB_ID.ROOT) local dDist = dist( ptP, ptNear) if not dMaxDist or dDist > dMaxDist then dMaxDist = dDist end end for j = 1, nLoopCnt do EgtErase( nLoopId + j - 1) end end end if not dMaxDist then local b3TenonAux = EgtGetBBoxRef( idAux, GDB_BB.STANDARD, frTen) dMaxDist = 2 * ( b3Ten:getRadius() - b3TenonAux:getRadius()) end FeatureExtraInfo.dTenonLength = dTenH FeatureExtraInfo.dTenonMaxDist = dMaxDist FeatureExtraInfo.vtTenonN = vtN FeatureExtraInfo.ptTenonCenter = ptC FeatureExtraInfo.idAddAuxGeom = idAux return FeatureExtraInfo end ------------------------------------------------------------------------------------------------------------- -- Recupero dati fmortasa a coda di rondine function FeatureLib.GetDTMortiseData( Proc) local FeatureExtraInfo = {} local idAux = EgtGetInfo( Proc.id, 'AUXID', 'i') if idAux then idAux = idAux + Proc.id end local vtMortiseN = EgtCurveExtrusion( idAux, GDB_RT.GLOB) -- ne determino l'asse local vtAx = EgtEV( idAux, GDB_RT.GLOB) - EgtSV( idAux, GDB_RT.GLOB) vtAx:normalize() -- determino l'altezza della mortasa (0=faccia di fondo) local rFrameDtMortise, dMortiseLength, dMortiseWidth = EgtSurfTmFacetMinAreaRectangle( Proc.id, 0, GDB_RT.GLOB) if abs( rFrameDtMortise:getVersY() * vtAx) > abs( rFrameDtMortise:getVersX() * vtAx) then rFrameDtMortise:rotate( rFrameDtMortise:getOrigin(), rFrameDtMortise:getVersZ(), 90) dMortiseLength, dMortiseWidth = dMortiseWidth, dMortiseLength end local b3DtMortise = EgtGetBBoxRef( Proc.id, GDB_BB.STANDARD, rFrameDtMortise) local dMortiseDepth = b3DtMortise:getDimZ() -- recupero il raggio minimo della mortasa local dMortiseMinRadius = 1000 local nSt, nEnd = EgtCurveDomain( idAux) for i = nSt, nEnd - 1 do local dRad = EgtCurveCompoRadius( idAux, i) if dRad > 0 and dRad < dMortiseMinRadius then dMortiseMinRadius = dRad end end -- distanza massima all'imbocco ortogonale all'asse local vtDiff = EgtEP( idAux, GDB_RT.GLOB) - EgtSP( idAux, GDB_RT.GLOB) local vtOrtDiff = vtDiff - vtDiff * vtAx * vtAx local dMortiseMaxDist = min( vtOrtDiff:len(), dMortiseWidth) FeatureExtraInfo.bIsFrontMortise = Proc.nPrc == 56 FeatureExtraInfo.dMortiseLength = dMortiseLength FeatureExtraInfo.dMortiseWidth = dMortiseWidth FeatureExtraInfo.dMortiseMaxDist = dMortiseMaxDist FeatureExtraInfo.dMortiseDepth = dMortiseDepth FeatureExtraInfo.dMortiseMinRadius = dMortiseMinRadius FeatureExtraInfo.vtMortiseN = vtMortiseN FeatureExtraInfo.idAddAuxGeom = idAux return FeatureExtraInfo end ------------------------------------------------------------------------------------------------------------- -- Recupero dati mortasa function FeatureLib.GetMortiseData( Proc, Part) local FeatureExtraInfo = {} local idAux = EgtGetInfo( Proc.id, 'AUXID', 'i') if idAux then idAux = idAux + Proc.id end local vtMortiseN = EgtCurveExtrusion( idAux, GDB_RT.GLOB) -- recupero i dati della faccia di fondo local frMortise, dMortiseLength, dMortiseWidth = EgtSurfTmFacetMinAreaRectangle( Proc.id, 0, GDB_ID.ROOT) -- se curva di contorno aperta la rendo chiusa local _, bCurveModified = BeamLib.ConvertToClosedCurve( Proc, idAux) -- Confronto le direzioni dei 2 versori : se diverse la faccia 0 non è il fondo => mortasa passante local bMortiseThrough = not AreSameVectorApprox( vtMortiseN, frMortise:getVersZ()) -- in caso sia passante si ricalcola tutto sul contorno della tasca if bMortiseThrough then -- creo superficie chiusa sul contorno local nFlat = EgtSurfTmByFlatContour( EgtGetParent( idAux), idAux, 0.05) if nFlat then frMortise, dMortiseLength, dMortiseWidth = EgtSurfTmFacetMinAreaRectangle( nFlat, 0, GDB_ID.ROOT) -- verifico se copiare la geometria lungo l'asse Z local b3Aux = EgtGetBBoxRef( idAux, GDB_BB.STANDARD, frMortise) local bxMax = b3Aux:getMax() local b3Mor = EgtGetBBoxRef( Proc.id, GDB_BB.STANDARD, frMortise) local bxMin = b3Mor:getMin() local dMove = bxMin:getZ() - bxMax:getZ() -- se il percorso ausiliario è esterno al grezzo, lo riavvicino if abs( dMove) > GEO.EPS_SMALL then idAux = EgtCopyGlob( idAux, BeamLib.GetAddGroup( Part.id)) local vtMove = Vector3d( 0, 0, dMove) vtMove:toGlob( frMortise) EgtMove( idAux, vtMove, GDB_RT.GLOB) EgtMove( nFlat, vtMove, GDB_RT.GLOB) frMortise, dMortiseLength, dMortiseWidth = EgtSurfTmFacetMinAreaRectangle( nFlat, 0, GDB_ID.ROOT) end -- cancello la superficie piana utilizzata per ricalcolare dati EgtErase( nFlat) end end -- determino altezza della mortasa local b3Mortise = EgtGetBBoxRef( Proc.id, GDB_BB.STANDARD, frMortise) local dMortiseDepth = b3Mortise:getDimZ() -- recupero il raggio minimo della mortasa local dMortiseMinRadius = 1000 local nSt, nEnd = EgtCurveDomain( idAux) for i = nSt, nEnd - 1 do local dRad = EgtCurveCompoRadius( idAux, i) if dRad > 0 and dRad < dMortiseMinRadius then dMortiseMinRadius = dRad end end -- se la mortasa passante il contorno è sulla faccia della trave e il riconoscimento lati aperti non è corretto if not bCurveModified and not bMortiseThrough then BeamLib.SetOpenSide( idAux, Part.b3Part) end FeatureExtraInfo.bIsFrontMortise = Proc.nPrc == 51 FeatureExtraInfo.bIsMortiseThrough = bMortiseThrough FeatureExtraInfo.bIsMortiseOpen = bCurveModified FeatureExtraInfo.dMortiseLength = dMortiseLength FeatureExtraInfo.dMortiseWidth = dMortiseWidth FeatureExtraInfo.dMortiseDepth = dMortiseDepth FeatureExtraInfo.dMortiseMinRadius = dMortiseMinRadius FeatureExtraInfo.vtMortiseN = vtMortiseN FeatureExtraInfo.idAddAuxGeom = idAux -- aggiustamento affected faces in caso di feature frontale if FeatureExtraInfo.bIsFrontMortise and FeatureExtraInfo.vtMortiseN:getX() < 0 then Proc.AffectedFaces.bLeft = true elseif FeatureExtraInfo.bIsFrontMortise and FeatureExtraInfo.vtMortiseN:getX() > 0 then Proc.AffectedFaces.bRight = true end return FeatureExtraInfo end ------------------------------------------------------------------------------------------------------------- -- funzione che restituisce indice di completamento in base alla percentuale di volume lavorato function FeatureLib.GetFeatureCompletionIndex( dCompletionPercentage) -- indice di completamento local nCompletionIndex = 0 -- nullo if dCompletionPercentage < 5 then nCompletionIndex = 0 -- Low elseif dCompletionPercentage < 50 then nCompletionIndex = 1 -- Medium elseif dCompletionPercentage < 80 then nCompletionIndex = 2 -- High / Complete else nCompletionIndex = 5 end return nCompletionIndex end ------------------------------------------------------------------------------------------------------------- -- funzione che restituisce qualità della lavorazione in base agli utensili utilizzati function FeatureLib.GetFeatureQuality( sTypeTools) local nQuality = 5 local TypeTools = EgtSplitString( sTypeTools) -- indice in base a utensile for i=1, #TypeTools do if TypeTools[i] == 'Blade' then nQuality = min( nQuality, 5) elseif TypeTools[i] == 'Mill' then nQuality = min( nQuality, 4) elseif TypeTools[i] == 'Chainsaw' then nQuality = min( nQuality, 2) else nQuality = min( nQuality, 1) end end -- se si utilizzano più utensili si perde in qualità if #TypeTools > 1 then nQuality = max( nQuality - 1, 0.5) end return nQuality end ------------------------------------------------------------------------------------------------------------- function FeatureLib.GetStrategyQuality( Machinings) local nQuality = 0 local dQualityNumerator = 0 local dQualityDenominator = 0 for i = 1, #Machinings do local Machining = Machinings[i] local dWeightedQuality = TOOLS[Machining.nToolIndex].nQuality * Machining.dLengthToMachine if Machining.bIsApplicable then dQualityNumerator = dQualityNumerator + dWeightedQuality dQualityDenominator = dQualityDenominator + Machining.dLengthToMachine end end nQuality = dQualityNumerator / dQualityDenominator return nQuality end ------------------------------------------------------------------------------------------------------------- function FeatureLib.GetStrategyTimeToMachine( Machinings) local dTimeToMachine = 0 for i = 1, #Machinings do local Machining = Machinings[i] if Machining.bIsApplicable then dTimeToMachine = dTimeToMachine + Machining.dTimeToMachine end end return dTimeToMachine end ------------------------------------------------------------------------------------------------------------- function FeatureLib.GetStrategyResultNotApplicable( sInfo) local Result = {} if not type( sInfo) == "string" then sInfo = '' end Result.dTimeToMachine = 0 Result.dMRR = 0 Result.dCompletionPercentage = 0 Result.sStatus = 'Not-Applicable' Result.nCompletionIndex = 0 Result.nQuality = 0 Result.sInfo = sInfo return Result end ------------------------------------------------------------------------------------------------------------- -- TODO rivedere affidabilità del calcolo del composite rating -- funzione che calcola il 'CompositeRating' di ogni strategia function FeatureLib.CalculateCompositeRating( StrategyResult) -- se ho tutti i dati che mi servono calcolo il rating della strategia applicato alla feature if StrategyResult and StrategyResult.nQuality and StrategyResult.nCompletionIndex and StrategyResult.dMRR then -- indice bonta lavorazione feature in rotazione è opzionale, se non settato viene messo a 3 if not StrategyResult.nFeatureRotationIndex then StrategyResult.nFeatureRotationIndex = 3 end StrategyResult.dCompositeRating = ceil( StrategyResult.nQuality * StrategyResult.nCompletionIndex * StrategyResult.dMRR * StrategyResult.nFeatureRotationIndex) else StrategyResult.dCompositeRating = 0 end return StrategyResult end ------------------------------------------------------------------------------------------------------------- function FeatureLib.IsMachiningLong( dMachiningLengthOnX, Part, OptionalParameters) local bIsMachiningLong -- parametri opzionali if not OptionalParameters then OptionalParameters = {} end local dMaxSegmentLength = OptionalParameters.dMaxSegmentLength or BeamData.LONGCUT_MAXLEN bIsMachiningLong = ( dMachiningLengthOnX > dMaxSegmentLength + 10 * GEO.EPS_SMALL) or ( dMachiningLengthOnX > 0.7 * Part.b3Part:getDimX() + 10 * GEO.EPS_SMALL) return bIsMachiningLong end ------------------------------------------------------------------------------------------------------------- function FeatureLib.GetFeatureSplittingPoints( Proc, Part, OptionalParameters) local FeatureSplittingPoints = {} local bFeatureStartsOnEdgeLeft = false local bFeatureStartsOnEdgeRight = false local dSplitXLeft = Proc.b3Box:getMin():getX() local dSplitXRight = Proc.b3Box:getMax():getX() local dFeatureCentralLength = Proc.b3Box:getDimX() -- parametri opzionali if not OptionalParameters then OptionalParameters = {} end local dMaxSegmentLength = OptionalParameters.dMaxSegmentLength or BeamData.LONGCUT_MAXLEN local dMaxSegmentLengthOnEdges = OptionalParameters.dMaxSegmentLengthOnEdges or BeamData.LONGCUT_ENDLEN local dMinSegmentLength = OptionalParameters.dMinSegmentLength or dMaxSegmentLengthOnEdges / 2 local dToolOverlapBetweenSegments = OptionalParameters.dToolOverlapBetweenSegments or BeamData.MILL_OVERLAP -- verifica spezzatura necessaria if not FeatureLib.IsMachiningLong( Proc.b3Box:getDimX(), Part) then return {} end -- verifica se necessari spezzoni differenti sugli estremi if Proc.b3Box:getMin():getX() < Part.b3Part:getMin():getX() + dMaxSegmentLengthOnEdges - 10 * GEO.EPS_SMALL then bFeatureStartsOnEdgeLeft = true end if Proc.b3Box:getMax():getX() > Part.b3Part:getMax():getX() - dMaxSegmentLengthOnEdges + 10 * GEO.EPS_SMALL then bFeatureStartsOnEdgeRight = true end -- calcolo punto estremo sinistro local ptSplitXLeft if bFeatureStartsOnEdgeLeft then -- decido punto spezzatura verso la coda if Proc.b3Box:getDimX() > dMaxSegmentLengthOnEdges * 2 then dSplitXLeft = max( Part.b3Part:getMin():getX() + dMaxSegmentLengthOnEdges, Proc.b3Box:getMin():getX() + dMinSegmentLength) else -- se pezzo abbastanza piccolo, spezzo in mezzo al 'pezzo + grezzo restante' if Part.dRestLength + Part.b3Part:getDimX() < BeamData.dMinRaw * 1.5 then dSplitXLeft = Part.b3Part:getMax():getX() - ( ( Part.dRestLength + Part.b3Part:getDimX()) / 2) else dSplitXLeft = max( Proc.b3Box:getMin():getX() + ( BeamData.dMinRaw)/2 + 150, Part.b3Part:getMax():getX() - dMaxSegmentLengthOnEdges) end end dFeatureCentralLength = abs( dSplitXRight - dSplitXLeft) ptSplitXLeft = Point3d( dSplitXLeft, 0, 0) end -- calcolo punto estremo destro local ptSplitXRight if bFeatureStartsOnEdgeRight then dSplitXRight = min( ( Proc.b3Box:getMax():getX() - dMinSegmentLength), Part.b3Part:getMax():getX() - dMaxSegmentLengthOnEdges) if dSplitXRight - dSplitXLeft < 500 * GEO.EPS_SMALL then dSplitXRight = dSplitXLeft - dToolOverlapBetweenSegments dFeatureCentralLength = 0 bFeatureStartsOnEdgeLeft = false else dFeatureCentralLength = dSplitXRight - dSplitXLeft end ptSplitXRight = Point3d( dSplitXRight, 0, 0) end -- aggiungo eventuale punto estremo destro if bFeatureStartsOnEdgeRight then table.insert( FeatureSplittingPoints, ptSplitXRight) end -- aggiungo punti centrali della feature if dFeatureCentralLength > 0 then local nSplitParts = max( ceil( dFeatureCentralLength / dMaxSegmentLength + 10 * GEO.EPS_SMALL), 1) local dSplitPartsLen = dFeatureCentralLength / nSplitParts for i = 1, ( nSplitParts - 1) do local ptOn local dCurrentPointX = dSplitXRight - i * dSplitPartsLen ptOn = Point3d( dCurrentPointX, 0, 0) table.insert( FeatureSplittingPoints, ptOn) end end -- aggiungo eventuale punto estemo sinistro if bFeatureStartsOnEdgeLeft then table.insert( FeatureSplittingPoints, ptSplitXLeft) end return FeatureSplittingPoints end ------------------------------------------------------------------------------------------------------------- function FeatureLib.GetFeatureVolume( Proc, Part) local dProcVolume = 0 local idAddGroup = BeamLib.GetAddGroup( Part.id) if not idAddGroup then -- TODO gestire meglio questo errore. Non conviene creare e verificare all'inizio se il gruppo esiste? EgtOutLog( 'Error : missing AddGroup') return 0 end local idProcCopy = EgtCopyGlob( Proc.id, idAddGroup) or GDB_ID.NULL local b3PartCopy = BBox3d( Part.b3Part) b3PartCopy:expand( -100 * GEO.EPS_SMALL) local idPartCopy = EgtSurfTmBBox( idAddGroup, b3PartCopy , false, GDB_RT.GLOB) EgtSurfTmSubtract( idPartCopy, idProcCopy) dProcVolume = EgtSurfVolume( idPartCopy) EgtErase( { idProcCopy, idPartCopy}) return dProcVolume end ------------------------------------------------------------------------------------------------------------- -- funzione che verifica se la feature, lavorata in questa fase, compromette lettura misura laser function FeatureLib.CalculateFeatureHindersLaserMeasure( Proc, Part) local bFeatureHindersLaserMeasure = false -- se la feature è aperta frontalmente, posteriormente e di testa, allora potrebbe impattare sulla misura laser, controllo caso per caso if Proc.AffectedFaces.bRight and Proc.AffectedFaces.bFront and Proc.AffectedFaces.bBack then bFeatureHindersLaserMeasure = true end return bFeatureHindersLaserMeasure end ------------------------------------------------------------------------------------------------------------- -- TODO funzione copiata direttamente da automatismo vecchio, da migliorare / completare function FeatureLib.CalculateFeatureNotClampableLengths( Proc, Part) local NotClampableLength = {} local dNotClampableLengthHead = 0 local dNotClampableLengthTail = 0 -- TODO al momento con meno di 3 facce si restituisce 0 if Proc.nFct < 3 then -- eventuale segnalazione ingombro di testa o coda local dMinHIng = min( 0.5 * BeamData.VICE_MINH, 0.5 * Part.b3Raw:getDimZ()) local dMinZ = max( BeamData.MIN_HEIGHT, 0.35 * Part.b3Raw:getDimZ()) -- calcolo punto massimo in Z fino a dove considerare il pinzaggio. Minimo tra pinzaggio massimo e altezza pezzo local dMaxHZ = Part.b3Raw:getMin():getZ() + min( BeamData.VICE_MAXH or BeamData.MAX_HEIGHT, Part.b3Raw:getDimZ()) -- punto massimo in Z considerando anche la Z della feature local dMaxHZFeat = min( dMaxHZ, Proc.b3Box:getMax():getZ()) -- dimensione Z del pinzaggio (differenza massima Z pinzabile e box feature) local dDeltaZClamp = ( ( dMaxHZ - Part.b3Raw:getMin():getZ()) - max( 0, dMaxHZFeat - Proc.b3Box:getMin():getZ())) -- se pinzaggio minimo è come il massimo (oppure come l'altezza massima del pezzo) significa che è verticale local bIsVertClamps = BeamData.VICE_MINH > BeamData.MAX_HEIGHT - 100 * GEO.EPS_SMALL -- condizioni per limitare pinzaggio testa/coda local bUpdateIng = true -- se dimensione del box della feature maggiore di metà pinzaggio minimo o metà spessore pezzo bUpdateIng = bUpdateIng and Proc.b3Box:getDimZ() > dMinHIng -- se la feature si trova più in basso del minimo pinzabile in Z o il 35% dello spessore pezzo bUpdateIng = bUpdateIng and Proc.b3Box:getMin():getZ() < Part.b3Raw:getMin():getZ() + dMinZ -- se feature è al di sotto del pinzaggio massimo bUpdateIng = bUpdateIng and Proc.b3Box:getMin():getZ() < dMaxHZ -- se ho le morse verticali, o se la feature è in centro o verso alto, controllo se non prendo abbastanza. if bIsVertClamps or ( Proc.b3Box:getMin():getZ() - Part.b3Raw:getMin():getZ()) > BeamData.MIN_HEIGHT then bUpdateIng = bUpdateIng and dDeltaZClamp < BeamData.VICE_MINH end if bUpdateIng then if Proc.AffectedFaces.bRight then local dOffs = Part.b3Part:getMax():getX() - Proc.b3Box:getMin():getX() -- se pinze a 45° e pinza abbastanza materiale, compenso comunque, ma solo inclinazione morse if not bIsVertClamps and dDeltaZClamp > BeamData.VICE_MINH and BeamData.VICE_MAXH then dOffs = min( dOffs, BeamData.VICE_MAXH - BeamData.VICE_MINH) end dNotClampableLengthHead = dOffs elseif Proc.AffectedFaces.bLeft then local dOffs = Proc.b3Box:getMax():getX() - Part.b3Part:getMin():getX() -- se pinze a 45° e pinza abbastanza materiale, compenso comunque, ma solo inclinazione morse if not bIsVertClamps and dDeltaZClamp > BeamData.VICE_MINH and BeamData.VICE_MAXH then dOffs = min( dOffs, BeamData.VICE_MAXH - BeamData.VICE_MINH) end dNotClampableLengthTail = dOffs elseif Proc.b3Box:getCenter():getX() > Part.b3Part:getCenter():getX() then local dOffs = Part.b3Part:getMax():getX() - Proc.b3Box:getMin():getX() local dDist = Part.b3Part:getMax():getX() - Proc.b3Box:getMax():getX() -- se pinze a 45° e pinza abbastanza materiale, compenso comunque, ma solo inclinazione morse if not bIsVertClamps and dDeltaZClamp > BeamData.VICE_MINH and BeamData.VICE_MAXH then dOffs = min( dOffs, BeamData.VICE_MAXH - BeamData.VICE_MINH) end -- dDist serve?? dNotClampableLengthHead = dOffs end end end NotClampableLength.dNotClampableLengthHead = dNotClampableLengthHead NotClampableLength.dNotClampableLengthTail = dNotClampableLengthTail return NotClampableLength end ------------------------------------------------------------------------------------------------------------- function FeatureLib.GetFeatureMaxNotClampableLengths( Proc, Part) local nPartIndex = Part.nIndexInParts local nProcIndex = Proc.nIndexInVProc local Rotations = PROCESSINGS[nPartIndex].Rotation local dMaxOnHead = 0 local dMaxOnTail = 0 for i = 1, #Part.CombinationList do for j = 1, 4 do -- controllo che la rotazione sia attiva if string.sub( Part.CombinationList[i].sBitIndexCombination, j, j) == '1' then dMaxOnHead = max( Rotations[j][nProcIndex].NotClampableLength.dNotClampableLengthHead, dMaxOnHead) dMaxOnTail = max( Rotations[j][nProcIndex].NotClampableLength.dNotClampableLengthTail, dMaxOnTail) end end end return dMaxOnHead, dMaxOnTail end ------------------------------------------------------------------------------------------------------------- return FeatureLib