-- BLADETOWASTE.lua by Egalware s.r.l. 2025/01/08 -- Libreria di supporto a strategie con funzioni comune a strategie diverse. -- Tabella per definizione modulo local BLADETOWASTE = {} -- Include require( 'EgtBase') -- Carico i dati globali local BeamData = require( 'BeamData') local BeamLib = require( 'BeamLib') local FaceData = require( 'FaceData') local FeatureLib = require( 'FeatureLib') local MachiningLib = require( 'MachiningLib') local DiceCut = require( 'DiceCut') -- strategie di base local FaceByBlade = require('FACEBYBLADE') EgtOutLog( ' BLADETOWASTE started', 1) ------------------------------------------------------------------------------------------------------------- local function CompareEdgesTopHead( EdgeA, EdgeB) -- prima i lati a minore elevazione (se entro 5 mm si considerano uguali) if EdgeA.dElevation < EdgeB.dElevation - 5 then return true elseif EdgeA.dElevation > EdgeB.dElevation + 5 then return false -- se stessa elevazione si preferiscono i lati più in basso (testa sopra) else if EdgeA.vtN:getZ() > EdgeB.vtN:getZ() + 10 * GEO.EPS_SMALL then return true elseif EdgeA.vtN:getZ() < EdgeB.vtN:getZ() - 10 * GEO.EPS_SMALL then return false -- se stessa Z si preferiscono i lati verso il fronte della trave else if EdgeA.vtN:getY() > EdgeB.vtN:getY() + 10 * GEO.EPS_SMALL then return true elseif EdgeA.vtN:getY() < EdgeB.vtN:getY() - 10 * GEO.EPS_SMALL then return false else return false end end end end local function CompareEdgesBottomHead( EdgeA, EdgeB) -- prima i lati a minore elevazione (se entro 5 mm si considerano uguali) if EdgeA.dElevation < EdgeB.dElevation - 5 then return true elseif EdgeA.dElevation > EdgeB.dElevation + 5 then return false -- se stessa elevazione si preferiscono i lati più in alto (testa sotto) else if EdgeA.vtN:getZ() < EdgeB.vtN:getZ() - 10 * GEO.EPS_SMALL then return true elseif EdgeA.vtN:getZ() > EdgeB.vtN:getZ() + 10 * GEO.EPS_SMALL then return false -- se stessa Z si preferiscono i lati verso il fronte della trave else if EdgeA.vtN:getY() > EdgeB.vtN:getY() + 10 * GEO.EPS_SMALL then return true elseif EdgeA.vtN:getY() < EdgeB.vtN:getY() - 10 * GEO.EPS_SMALL then return false else return false end end end end local function GetBestBlade( Proc, Part, Face, OptionalParameters) local nChosenToolIndex -- parametri opzionali local dMinNzTopBladeIfEqual = OptionalParameters.dMinNzTopBlade or sin(-5) local dMaxNyTopBlade = OptionalParameters.dMaxNyTopBlade or sin(1) local dShortPartLength = OptionalParameters.dShortPartLength or BeamData.LEN_SHORT_PART -- ricerca lama testa sopra local ToolSearchParameters = {} ToolSearchParameters.vtN = Face.vtN ToolSearchParameters.bAllowTopHead = true ToolSearchParameters.bAllowBottomHead = false ToolSearchParameters.bForceLongcutBlade = false ToolInfo = MachiningLib.FindBlade( Proc, ToolSearchParameters) local nToolIndexTop = ToolInfo.nToolIndex -- ricerca lama testa sotto ToolSearchParameters = {} ToolSearchParameters.vtN = Face.vtN ToolSearchParameters.bAllowTopHead = false ToolSearchParameters.bAllowBottomHead = true ToolSearchParameters.bForceLongcutBlade = false ToolInfo = MachiningLib.FindBlade( Proc, ToolSearchParameters) local nToolIndexBottom = ToolInfo.nToolIndex -- lama sopra e sotto if nToolIndexTop and nToolIndexBottom then -- angolo minimo che determina la preferenza tra lama sopra e sotto local dMinNzTopBlade -- entrambe le lame senza aggregato o entrambe con aggregato if TOOLS[nToolIndexTop].SetupInfo.bIsCSymmetrical == TOOLS[nToolIndexBottom].SetupInfo.bIsCSymmetrical then dMinNzTopBlade = dMinNzTopBladeIfEqual -- lama sopra con aggregato - preferenza testa sopra elseif not TOOLS[nToolIndexTop].SetupInfo.bIsCSymmetrical then dMinNzTopBlade = OptionalParameters.dMinNzTopBlade or TOOLS[nToolIndexTop].SetupInfo.dMinNz / 2 -- lama sotto con aggregato - preferenza testa sotto elseif not TOOLS[nToolIndexBottom].SetupInfo.bIsCSymmetrical then dMinNzTopBlade = OptionalParameters.dMinNzTopBlade or TOOLS[nToolIndexBottom].SetupInfo.dMaxNz / 2 else error( 'GetBestBlade : unknown blade type') end -- se la Z della faccia è sotto all'angolo minimo e inclinata in Y oppure il pezzo è corto, si preferisce la lama sotto if Face.vtN:getZ() < dMinNzTopBlade - GEO.EPS_SMALL and ( abs( Face.vtN:getY()) > dMaxNyTopBlade or Part.b3Raw:getDimX() < dShortPartLength - 10 * GEO.EPS_SMALL) then nChosenToolIndex = nToolIndexBottom else nChosenToolIndex = nToolIndexTop end -- solo lama sopra elseif nToolIndexTop then nChosenToolIndex = nToolIndexTop -- solo lama sotto elseif nToolIndexBottom then nChosenToolIndex = nToolIndexBottom end -- se non trovata alcuna lama ritorna nil return nChosenToolIndex end local function GetEdgeToMachine( Edges, nToolIndex) local EdgeToMachine = {} local EdgesSorted = {} for i = 1, #Edges do table.insert( EdgesSorted, Edges[i]) end if TOOLS[nToolIndex].SetupInfo.HeadType.bBottom then table.sort( EdgesSorted, CompareEdgesBottomHead) else table.sort( EdgesSorted, CompareEdgesTopHead) end EdgeToMachine = EdgesSorted[1] return EdgeToMachine end local function SetDiceFaceInfo( Proc, idDiceFace, bSaveAddedGeometries) EgtSetName( idDiceFace, 'Face' .. tostring( Proc.idFeature) .. '_Dice') -- TODO la scrittura del TaskId probabilmente non serve (è fatta nell'AddOperations) EgtSetInfo( idDiceFace, 'TASKID', Proc.idTask) if not bSaveAddedGeometries then EgtSetLevel( idDiceFace, GDB_LV.TEMP) end end function BLADETOWASTE.Make( ProcOrId, Part, OptionalParameters) local Machinings = {} local Result = {} local dCompletionPercentage = 0 -- disambiguazione feature vs id trimesh local Proc = {} if type( ProcOrId) == "table" then Proc = ProcOrId elseif type( ProcOrId) == "number" then Proc.id = ProcOrId Proc.nFct = EgtSurfTmFacetCount( Proc.id) or 0 Proc.b3Box = EgtGetBBoxGlob( ProcOrId or GDB_ID.NULL, GDB_BB.STANDARD) Proc.AffectedFaces = BeamLib.GetAffectedFaces( Proc, Part) Proc.AdjacencyMatrix = FaceData.GetAdjacencyMatrix( Proc) Proc.Faces = FaceData.GetFacesInfo( Proc, Part) else error( 'BLADETOWASTE : Only feature or trimesh supported') end -- controlli preventivi if Proc.nFct > 2 then error( 'BladeToWaste : max 2 faces supported') elseif Proc.nFct == 2 then if Proc.AdjacencyMatrix[1][2] > 10 * GEO.EPS_SMALL or Proc.AdjacencyMatrix[1][2] < -91 then error( 'BladeToWaste : angle between faces must be concave and >= 90deg') end end -- parametri opzionali e default if not OptionalParameters then OptionalParameters = {} end local nToolIndex = OptionalParameters.nToolIndex local bDropWholeWaste = OptionalParameters.bDropWholeWaste or false local dMaxWasteVolume = OptionalParameters.dMaxWasteVolume or 0 local dMaxWasteLength = OptionalParameters.dMaxWasteLength or 0 local bSaveAddedGeometries = OptionalParameters.bSaveAddedGeometries if bSaveAddedGeometries == nil then bSaveAddedGeometries = true end -- dimensioni feature local dFeatureVolume = FeatureLib.GetFeatureVolume( Proc, Part) local dFeatureMaxDimension = max( Proc.b3Box:getDimX(), Proc.b3Box:getDimY()) local bIsFeatureSmall = dFeatureVolume < dMaxWasteVolume + 10 * GEO.EPS_SMALL and dFeatureMaxDimension < dMaxWasteLength + 10 * GEO.EPS_SMALL -- si taglia tutto lo scarto in una sola lavorazione -- TODO qui si deve entrare anche se lo spessore lama è maggiore dell'elevazione delle faccia if Proc.nFct == 1 and ( bIsFeatureSmall or bDropWholeWaste) then local Cutting = {} local EdgeToMachine = {} local dDepthToMachine = 0 -- ricerca utensile if not nToolIndex then -- scelta lama da sopra o da sotto if not nToolIndex then -- quetsa deve anche verificare il lato migliore per lama sopra sotto e usare la sua elevazione per la ricerca (opzionalmente) nToolIndex = GetBestBlade( Proc, Part, Proc.Faces[1], OptionalParameters) end end -- scelta lato da lavorare EdgeToMachine = GetEdgeToMachine( Proc.Faces[1].Edges, nToolIndex) local dResidualDepth = EdgeToMachine.dElevation - TOOLS[nToolIndex].dMaxMaterial -- TODO qui gestire il caso in cui si può tagliare da due lati (inizialmente solo se vtN:Y è ~= 0?) if dResidualDepth < 10 * GEO.EPS_SMALL then local OptionalParametersFaceByBlade = { dDepthToMachine = dDepthToMachine, nToolIndex = nToolIndex} Cutting = FaceByBlade.Make( Proc, Part, Proc.Faces[1], EdgeToMachine, OptionalParametersFaceByBlade) end if Cutting.bIsApplicable or bDropWholeWaste then table.insert( Machinings, Cutting) dCompletionPercentage = Cutting.dCompletionPercentage or dCompletionPercentage end -- risultati del calcolo -- TODO funzione? if Cutting.bIsApplicable then if dCompletionPercentage > 100 - 10 * GEO.EPS_SMALL then Result.sStatus = 'Completed' else Result.sStatus = 'Not-Completed' end else Result.sStatus = 'Not-Applicable' end Result.dCompletionPercentage = dCompletionPercentage Result.nCompletionIndex = FeatureLib.GetFeatureCompletionIndex( dCompletionPercentage) Result.nQuality = FeatureLib.GetFeatureQuality( 'Blade') local dMRR = dFeatureVolume / ( EdgeToMachine.dLength / TOOLS[nToolIndex].Feeds.dFeed) Result.dMRR = dMRR / pow( 10, 6) -- TODO bisogna ritornare solo se ha successo return Machinings, Result end -- lavorazione con cubetti -- scelta faccia da lavorare -- se due facce, la faccia principale Face1 è la più grande local Face1 = Proc.Faces[1] local Face2 = {} if Proc.nFct == 2 then Face2 = Proc.Faces[2] if Face2.dArea > Face1.dArea then Face1, Face2 = Face2, Face1 end end -- scelta lama da sopra o da sotto if not nToolIndex then nToolIndex = GetBestBlade( Proc, Part, Face1, OptionalParameters) end -- se non trovata lama la lavorazione non è fattibile if not nToolIndex then return Machinings, Result end -- limite per taglio downUp local dMinNzDownUp = TOOLS[nToolIndex].SetupInfo.GetMinNzDownUp( Part.b3Raw, Face1.vtN) -- calcolo dimensione cubetto e eventuale cubetto ridotto (tagli orizzontali con affondamento verticale) local dDiceDimension = min( TOOLS[nToolIndex].dMaxMaterial, BeamData.MAX_DIM_DICE) local dDiceDimensionReduced = dDiceDimension if TOOLS[nToolIndex].SetupInfo.dMaxMatDecrease then dDiceDimensionReduced = min( dDiceDimension, dDiceDimension - TOOLS[nToolIndex].SetupInfo.dMaxMatDecrease) end dDiceDimension = dDiceDimension - BeamData.CUT_EXTRA dDiceDimensionReduced = dDiceDimensionReduced - BeamData.CUT_EXTRA -- calcolo cubetti local OptionalParametersDiceCut = {} OptionalParametersDiceCut.dOffsetParallel = dDiceDimension OptionalParametersDiceCut.dOffsetOrthogonal = dDiceDimension OptionalParametersDiceCut.dOffsetOrthogonalReduced = dDiceDimensionReduced OptionalParametersDiceCut.dMinNzDownUp = dMinNzDownUp local vCuts = DiceCut.GetDice( Part, Face1, Face2, OptionalParametersDiceCut) -- lavorazione cubetti -- calcolo vtToolDirection -- sistemo posizione nel DB e nome local bAreOrthogonalCutsInverted = false for i = 1, #vCuts do for j = 1, #vCuts[i] do SetDiceFaceInfo( Proc, vCuts[i][j]) if ( i % 2) == 1 then local vtO = EgtSurfTmFacetNormVersor( vCuts[i][j], 0, GDB_ID.ROOT) if ( Face1.vtN:getY() > 0.766 and vtO:getY() < -0.05) or ( Face1.vtN:getY() < -0.766 and vtO:getY() > 0.05) then EgtInvertSurf( vCuts[i][j]) bAreOrthogonalCutsInverted = true end end end end -- eseguo for i = 1, #vCuts do -- determino il modo di tagliare local vtToolDirection local bNoPerpCuts = false if i % 2 == 1 then vtToolDirection = Vector3d( Face1.vtN) else local vtO if #vCuts[i-1] > 0 then vtO = EgtSurfTmFacetNormVersor( vCuts[i-1][1], 0, GDB_ID.ROOT) elseif vCuts[i+1] and #vCuts[i+1] > 0 then -- lunghezza faccia nell'eventuale direzione ortogonale local asseX = EgtSurfTmFacetNormVersor( vCuts[i+1][1], 0, GDB_ID.ROOT) local asseY = asseX ^ Face1.vtN local Frame = Frame3d( Face1.ptCenter, Face1.ptCenter + asseX, Face1.ptCenter + asseY) local b3Fac = EgtGetBBoxRef( vCuts[i][1], GDB_BB.STANDARD, Frame) -- se lunghezza inferiore al limite, accetto la direzione if b3Fac:getDimX() < TOOLS[nToolIndex].dMaxDepth - BeamData.CUT_EXTRA then vtO = asseX else bNoPerpCuts = true end else bNoPerpCuts = true end if vtO then vtToolDirection = Vector3d( vtO) * EgtIf( bAreOrthogonalCutsInverted, -1, 1) else -- scelta lato da lavorare per stabilire la vtToolDirection local EdgeToMachine = {} EdgeToMachine = GetEdgeToMachine( vCuts[i][1], nToolIndex) if bCutDirection then vtToolDirection = Z_AX() else if Face1.vtN:getZ() < dMinNzDownUp then vtToolDirection = EgtIf( Face1.vtN:getY() > -0.02, -Y_AX(), Y_AX()) else vtToolDirection = EgtIf( Face1.vtN:getY() > 0.02, Y_AX(), -Y_AX()) end end end end -- caso standard -- lavoro la faccia for j = 1, #vCuts[i] do -- se taglio dal basso if bDownCut then -- se strato pari composto da 1 o 2 elementi if ( i % 2) == 0 and #vCuts[i] <= 2 then -- il primo elemento prende la direzione prevista, il secondo quella opposta local vtToolDirectionNew = Vector3d( vtToolDirection) local dVzLimDwnUp = dMinNzDownUp if j ~= 1 then vtToolDirectionNew = -vtToolDirection if BeamData.GetNzLimDownUp then dVzLimDwnUp = BeamData.GetNzLimDownUp( Part.b3Raw, Face1.vtN, V_NULL(), true) elseif not BeamData.C_SIMM and not BeamData.TURN and abs( Face1.vtN:getY()) > 0.05 then dVzLimDwnUp = -0.708 end end local bSpecialTangentLeadInOut = ( i % 2 == 0) and ( Proc.AffectedFaces.Left or Proc.AffectedFaces.Right) local bOk, sErr = Fbs.MakeOne( vCuts[i][j], 0, sCutting, dSawDiam, vtToolDirectionNew, dVzLimDwnUp, BD.CUT_EXTRA, BD.CUT_SIC, 0, 0, 0, nil, b3Raw, nil, nil, bSpecialTangentLeadInOut) if not bOk then return bOk, sErr end end -- tutti gli altri casi vengono saltati -- caso generale else -- in generale sta sollevato di pochissimo local dExtraCut = -0.1 -- se tagli paralleli if ( i % 2) == 0 then -- se non ci sono tagli ortogonali devo affondare if bNoPerpCuts then dExtraCut = BeamData.CUT_EXTRA -- se altrimenti tagli ortogonali invertiti, devo approfondire dello spessore lama elseif bAreOrthogonalCutsInverted then dExtraCut = dSawThick -- se ultimo taglio, devo affondare elseif j == #vCuts[i] then dExtraCut = BD.CUT_EXTRA end end local dVzLimDwnUp = dMinNzDownUp if BeamData.GetNzLimDownUp then dVzLimDwnUp = BeamData.GetNzLimDownUp( b3Raw, Face1.vtN, V_NULL(), true) elseif not BeamData.C_SIMM and not BeamData.TURN and Face1.vtN:getZ() > 0.707 then dVzLimDwnUp = -0.708 end local bSpecialTangentLeadInOut = ( i % 2 == 0) and ( Proc.AffectedFaces.Left or Proc.AffectedFaces.Right) local vtToolDirectionAlternative if ( i % 2 == 0) and ( Proc.Fct == 1) and bNoPerpCuts then vtToolDirectionAlternative = - vtToolDirection end bForceTangentLeadInOut = bForceTangentLeadInOut and ( ( i % 2) ~= 0) local bOk, sErr = Fbs.MakeOne( vCuts[i][j], 0, sCutting, dSawDiam, vtToolDirection, dVzLimDwnUp, dExtraCut, BD.CUT_SIC, 0, 0, 0, sNotes, b3Raw, nil, nil, bSpecialTangentLeadInOut, bForceTangentLeadInOut, vtToolDirectionAlternative) if not bOk then return bOk, sErr end end end end -- risultati del calcolo -- restituire tabella contenente lavorazioni, già con cloni se necessari return Machinings, Result end ------------------------------------------------------------------------------------------------------------- return BLADETOWASTE