-- 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') ------------------------------------------------------------------------------------------------------------- -- 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.IsRidgeLap( 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 SOLO se non raggiata -- TODO oppure riconoscerla come feature speciale; valutare se controllare il numero di facce è un metodo efficace elseif ID.IsMortise( Proc) and Proc.nFct < 6 then return true -- calcolo topologia SOLO se non raggiata -- TODO oppure riconoscerla come feature speciale; valutare se controllare il numero di facce è un metodo efficace elseif ID.IsFrontMortise( Proc) and Proc.nFct < 6 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) local bResult = false local nBoxSolidId = EgtGetFirstNameInGroup( Proc.idPart or GDB_ID.NULL, 'Box') local b3Solid = EgtGetBBoxGlob( nBoxSolidId, GDB_BB.STANDARD) if Proc.b3Box:getDimX() > b3Solid:getDimX() - 1000 * GEO.EPS_SMALL or Proc.b3Box:getDimY() > b3Solid:getDimY() - 1000 * GEO.EPS_SMALL or Proc.b3Box:getDimZ() > b3Solid: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, rappresentata dalle sue dimensioni W e H local function IsFeatureCuttingEntireSection( b3Proc, Part) return ( b3Proc:getDimY() > ( Part.b3Raw:getDimY() - 500 * GEO.EPS_SMALL) and b3Proc:getDimZ() > ( Part.b3Raw:getDimZ() - 500 * GEO.EPS_SMALL)) end ------------------------------------------------------------------------------------------------------------- -- restituisce vero se la feature con box b3Proc taglia l'intera lunghezza della barra, rappresentata dalle sue dimensioni W e L oppure H e L local function IsFeatureCuttingEntireLength( b3Proc, Part) return ( ( b3Proc:getDimY() > ( Part.b3Raw:getDimY() - 500 * GEO.EPS_SMALL) and b3Proc:getDimX() > ( Part.b3Raw:getDimX() - 500 * GEO.EPS_SMALL)) or ( b3Proc:getDimZ() > ( Part.b3Raw:getDimZ() - 500 * GEO.EPS_SMALL) and b3Proc:getDimX() > ( Part.b3Raw: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) 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 ------------------------------------------------------------------------------------------------------------- -- Recupero dati foro e adattamento se speciale function FeatureLib.GetDrillingData( Proc) local AuxId = EgtGetInfo( Proc.id, 'AUXID', 'i') -- 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 local dDiam = EgtGetInfo( Proc.id, 'P12', 'd') or 0 local dLen = abs( EgtCurveThickness( Proc.id + AuxId)) or 0 local nFcs = EgtGetInfo( Proc.id, 'FCS', 'i') or 0 local nFce = EgtGetInfo( Proc.id, 'FCE', 'i') or 0 return dDiam, dLen, nFcs, nFce 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 ------------------------------------------------------------------------------------------------------------- -- 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 StrategyResult.dCompositeRating = ceil( StrategyResult.nQuality * StrategyResult.nCompletionIndex * StrategyResult.dMRR) else StrategyResult.dCompositeRating = 0 end return StrategyResult end ------------------------------------------------------------------------------------------------------------- function FeatureLib.MachiningNeedsSplitting( dMachiningLengthOnX, Part, OptionalParameters) local bMachiningNeedsSplitting -- parametri opzionali if not OptionalParameters then OptionalParameters = {} end local dMaxSegmentLength = OptionalParameters.dMaxSegmentLength or BeamData.LONGCUT_MAXLEN bMachiningNeedsSplitting = ( dMachiningLengthOnX > dMaxSegmentLength + 10 * GEO.EPS_SMALL) or ( dMachiningLengthOnX > 0.7 * Part.b3Solid:getDimX() + 10 * GEO.EPS_SMALL) return bMachiningNeedsSplitting end ------------------------------------------------------------------------------------------------------------- function FeatureLib.GetFeatureSplittingPoints( Proc, Part, OptionalParameters) local vFeatureSplittingPoints = {} 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.MachiningNeedsSplitting( Proc.b3Box:getDimX(), Part) then return {} end -- verifica se necessari spezzoni differenti sugli estremi if Proc.b3Box:getMin():getX() < Part.b3Solid:getMin():getX() + dMaxSegmentLengthOnEdges - 10 * GEO.EPS_SMALL then bFeatureStartsOnEdgeLeft = true end if Proc.b3Box:getMax():getX() > Part.b3Solid: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.b3Solid:getMin():getX() + dMaxSegmentLengthOnEdges, Proc.b3Box:getMin():getX() + dMinSegmentLength) else -- se pezzo abbastanza piccolo, spezzo in mezzo al 'pezzo + grezzo restante' if Part.dRestLength + Part.b3Solid:getDimX() < BeamData.dMinRaw * 1.5 then dSplitXLeft = Part.b3Solid:getMax():getX() - ( ( Part.dRestLength + Part.b3Solid:getDimX()) / 2) else dSplitXLeft = max( Proc.b3Box:getMin():getX() + ( BeamData.dMinRaw)/2 + 150, Part.b3Solid: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.b3Solid:getMax():getX() - dMaxSegmentLengthOnEdges) if dSplitXRight - dSplitXLeft < 500 * GEO.EPS_SMALL then dSplitXRight = dSplitXLeft - dToolOverlapBetweenSegments dFeatureCentralLength = 0 else dFeatureCentralLength = dSplitXRight - dSplitXLeft end ptSplitXRight = Point3d( dSplitXRight, 0, 0) end -- aggiungo eventuale punto estremo destro if bFeatureStartsOnEdgeRight then table.insert( vFeatureSplittingPoints, 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( vFeatureSplittingPoints, ptOn) end end -- aggiungo eventuale punto estemo sinistro if bFeatureStartsOnEdgeLeft then table.insert( vFeatureSplittingPoints, ptSplitXLeft) end return vFeatureSplittingPoints end ------------------------------------------------------------------------------------------------------------- return FeatureLib