-- 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') -- tabella per definizione modulo local FeatureInfo = {} ------------------------------------------------------------------------------------------------------------- local function CompareEdgesTopHead( EdgeA, EdgeB) -- prima i lati vicini all'orizzontale (30deg) if ( abs( EdgeA.vtN:getZ()) < 0.5 + 10 * GEO.EPS_SMALL) and ( abs( EdgeB.vtN:getZ()) > 0.5 + 10 * GEO.EPS_SMALL) then return true elseif ( abs( EdgeA.vtN:getZ()) > 0.5 + 10 * GEO.EPS_SMALL) and ( abs( EdgeB.vtN:getZ()) < 0.5 + 10 * GEO.EPS_SMALL) then return false else -- se entrambi entro i 30deg si preferiscono i lati a minore elevazione if ( abs( EdgeA.vtN:getZ()) < 0.5 + 10 * GEO.EPS_SMALL) and ( abs( EdgeB.vtN:getZ()) < 0.5 + 10 * GEO.EPS_SMALL) then 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ù lunghi else if EdgeA.dLength > EdgeB.dLength + 10 * GEO.EPS_SMALL then return true elseif EdgeA.dLength < EdgeB.dLength - 10 * GEO.EPS_SMALL then return false else return false end end -- se entrambi oltre i 30deg si preferiscono i lati più vicini all'orizzontale elseif ( abs( EdgeA.vtN:getZ()) > 0.5 + 10 * GEO.EPS_SMALL) and ( abs( EdgeB.vtN:getZ()) > 0.5 + 10 * GEO.EPS_SMALL) then if abs( EdgeA.vtN:getZ()) < abs( EdgeB.vtN:getZ()) - 100 * GEO.EPS_SMALL then return true elseif abs( EdgeA.vtN:getZ()) > abs( EdgeB.vtN:getZ()) + 100 * GEO.EPS_SMALL then return false -- se stessa Z si preferiscono i lati a minore elevazione else 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ù lunghi else if EdgeA.dLength > EdgeB.dLength + 10 * GEO.EPS_SMALL then return true elseif EdgeA.dLength < EdgeB.dLength - 10 * GEO.EPS_SMALL then return false else return false end end 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 CompareEdgesTopHeadGuillotine( EdgeA, EdgeB) -- prima il lato sotto if ( EdgeA.vtN:getZ()) > 0.98 + 10 * GEO.EPS_SMALL and ( EdgeB.vtN:getZ()) < 0.98 + 10 * GEO.EPS_SMALL then return true elseif ( EdgeA.vtN:getZ()) < 0.98 + 10 * GEO.EPS_SMALL and ( EdgeB.vtN:getZ()) > 0.98 + 10 * GEO.EPS_SMALL then return false else -- se entrambi lati sotto, si sceglie quello a minor elevazione if EdgeA.vtN:getZ() > 0.98 + 10 * GEO.EPS_SMALL and EdgeB.vtN:getZ() > 0.98 + 10 * GEO.EPS_SMALL then if EdgeA.dElevation < EdgeB.dElevation - 10 * GEO.EPS_SMALL then return true elseif EdgeA.dElevation > EdgeB.dElevation + 10 * GEO.EPS_SMALL then return false else return false end -- se entrambi non lati sotto, si sceglie quello con normale non oltre l'orizzontale elseif ( EdgeA.vtN:getZ()) > -0.1 + 10 * GEO.EPS_SMALL and ( EdgeB.vtN:getZ()) < -0.1 + 10 * GEO.EPS_SMALL then return true elseif ( EdgeA.vtN:getZ()) < -0.1 + 10 * GEO.EPS_SMALL and ( EdgeB.vtN:getZ()) > -0.1 + 10 * GEO.EPS_SMALL then return false else -- se entrambi entro l'orizzontale, si sceglie quello a minor elevazione if EdgeA.vtN:getZ() > -0.1 + 10 * GEO.EPS_SMALL and EdgeB.vtN:getZ() > -0.1 + 10 * GEO.EPS_SMALL then if EdgeA.dElevation < EdgeB.dElevation - 10 * GEO.EPS_SMALL then return true elseif EdgeA.dElevation > EdgeB.dElevation + 10 * GEO.EPS_SMALL then return false else return false end -- se entrambi oltre l'orizzontale, si sceglie quello più verso l'orizzontale else if EdgeA.vtN:getZ() > EdgeB.vtN:getZ() + 100 * GEO.EPS_SMALL then return true elseif EdgeA.vtN:getZ() < EdgeB.vtN:getZ() - 100 * GEO.EPS_SMALL then return false -- se stessa Z si preferiscono i lati a minore elevazione else 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ù lunghi else if EdgeA.dLength > EdgeB.dLength + 10 * GEO.EPS_SMALL then return true elseif EdgeA.dLength < EdgeB.dLength - 10 * GEO.EPS_SMALL then return false else return false end end end end end end end -- si cerca il lato più verticale local function CompareEdgesVertical( EdgeA, EdgeB) if abs( EdgeA.vtN:getZ()) < abs( EdgeB.vtN:getZ()) - 10 * GEO.EPS_SMALL then return true end if abs( EdgeA.vtN:getZ()) > abs( EdgeB.vtN:getZ()) + 10 * GEO.EPS_SMALL then return false end return false end -- si cerca il lato orizzontale più in basso local function CompareEdgesHorizontalBottom( EdgeA, EdgeB) if EdgeA.vtN:getZ() > EdgeB.vtN:getZ() + 10 * GEO.EPS_SMALL then return true end if EdgeA.vtN:getZ() < EdgeB.vtN:getZ() - 10 * GEO.EPS_SMALL then return false end return false end local function CompareEdgesNoPreference( EdgeA, EdgeB) -- prima i lati a minore elevazione if EdgeA.dElevation < EdgeB.dElevation then return true elseif EdgeA.dElevation > EdgeB.dElevation 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 GetEdgeToMachine( Edges, vtNFace, sBladeType) local EdgeToMachine = {} local EdgesSorted = {} for i = 1, #Edges do table.insert( EdgesSorted, Edges[i]) end if ( sBladeType == 'Top') then table.sort( EdgesSorted, CompareEdgesTopHead) EdgeToMachine = EdgesSorted[1] elseif sBladeType == 'Bottom' then table.sort( EdgesSorted, CompareEdgesBottomHead) EdgeToMachine = EdgesSorted[1] elseif sBladeType == 'TopDownUp' then local vtEdgeDirection if vtNFace:getY() > -0.02 then vtEdgeDirection = -Y_AX() else vtEdgeDirection = Y_AX() end EdgeToMachine = BeamLib.FindEdgeBestOrientedAsDirection( Edges, vtEdgeDirection) elseif sBladeType == 'TopGuillotine' then table.sort( EdgesSorted, CompareEdgesTopHeadGuillotine) EdgeToMachine = EdgesSorted[1] elseif sBladeType == 'HorizontalBottom' then table.sort( EdgesSorted, CompareEdgesHorizontalBottom) EdgeToMachine = EdgesSorted[1] elseif sBladeType == 'Vertical' then table.sort( EdgesSorted, CompareEdgesVertical) EdgeToMachine = EdgesSorted[1] -- TODO non testato; non si dovrebbe mai entrare in questo caso else table.sort( EdgesSorted, CompareEdgesNoPreference) EdgeToMachine = EdgesSorted[1] end return EdgeToMachine end local function GetBestBlade( Proc, Part, Face, OptionalParameters) local nChosenToolIndex local sChosenBladeType local nToolIndexTop local nToolIndexBottom -- parametri opzionali e default if not OptionalParameters then OptionalParameters = {} end local dMinNzTopBladeIfEqual = OptionalParameters.dMinNzTopBlade or sin(-5) local dMaxNyTopBlade = OptionalParameters.dMaxNyTopBlade or sin(1) local dShortPartLength = OptionalParameters.dShortPartLength or BeamData.LEN_SHORT_PART local dElevationTop = OptionalParameters.dElevationTop local dElevationBottom = OptionalParameters.dElevationBottom local dElevationTopDownUp = OptionalParameters.dElevationTopDownUp or 0 -- se ho già l'utensile, devo solo discriminare se è lama sopra o sotto e verificare se ci arriva if OptionalParameters.nToolIndex then if TOOLS[OptionalParameters.nToolIndex].SetupInfo.HeadType.bTop then if TOOLS[OptionalParameters.nToolIndex].dMaxMaterial > dElevationTop - 10 * GEO.EPS_SMALL then nToolIndexTop = OptionalParameters.nToolIndex end elseif TOOLS[OptionalParameters.nToolIndex].SetupInfo.HeadType.bBottom then if TOOLS[OptionalParameters.nToolIndex].dMaxMaterial > dElevationBottom - 10 * GEO.EPS_SMALL then nToolIndexBottom = OptionalParameters.nToolIndex end end -- non ho l'utensile, devo cercarlo e scegliere il migliore tra testa sopra e sotto else local ToolInfo -- ricerca lama testa sopra ToolInfo = MachiningLib.FindBlade( Proc, { vtN = Face.vtN, bAllowTopHead = true, bAllowBottomHead = false, dElevation = dElevationTop }) if ToolInfo.dResidualDepth < 10 * GEO.EPS_SMALL then nToolIndexTop = ToolInfo.nToolIndex end -- ricerca lama testa sotto ToolInfo = MachiningLib.FindBlade( Proc, { vtN = Face.vtN, bAllowTopHead = false, bAllowBottomHead = true, dElevation = dElevationBottom }) if ToolInfo.dResidualDepth < 10 * GEO.EPS_SMALL then nToolIndexBottom = ToolInfo.nToolIndex end end -- 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.GetMinNz( Face.vtN, TOOLS[nToolIndexTop]) / 2 -- lama sotto con aggregato - preferenza testa sotto elseif not TOOLS[nToolIndexBottom].SetupInfo.bIsCSymmetrical then dMinNzTopBlade = OptionalParameters.dMinNzTopBlade or TOOLS[nToolIndexBottom].SetupInfo.GetMaxNz( Face.vtN, TOOLS[nToolIndexBottom]) / 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 -- assegnazione tipo lama if nChosenToolIndex == nToolIndexBottom then sChosenBladeType = 'Bottom' -- se lama sopra va verificato se va usata in DownUp elseif nChosenToolIndex == nToolIndexTop then local dMinNzDownUp = TOOLS[nToolIndexTop].SetupInfo.GetMinNzDownUp( Part.b3Raw, Face.vtN, nil, TOOLS[nToolIndexTop]) local dCurrentResidualDepth = dElevationTopDownUp - TOOLS[nToolIndexTop].dMaxDepth if Face.vtN:getZ() < dMinNzDownUp then if ( dCurrentResidualDepth > 10 * GEO.EPS_SMALL) then -- TODO serve messaggio per motivare il non applicabile?? nChosenToolIndex = nil end sChosenBladeType = 'TopDownUp' else sChosenBladeType = 'Top' end end -- se non trovata alcuna lama ritorna nil return nChosenToolIndex, sChosenBladeType 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 local function GetSingleCutStrategy( Proc, Part, OptionalParameters) local FaceToMachine = Proc.Faces[1] local sChosenBladeType -- lati da lavorare in base al tipo di lama local EdgeToMachineList = { Top = GetEdgeToMachine(FaceToMachine.Edges, FaceToMachine.vtN, 'Top'), Bottom = GetEdgeToMachine(FaceToMachine.Edges, FaceToMachine.vtN, 'Bottom'), TopDownUp = GetEdgeToMachine(FaceToMachine.Edges, FaceToMachine.vtN, 'TopDownUp'), TopGuillotine = GetEdgeToMachine(FaceToMachine.Edges, FaceToMachine.vtN, 'TopGuillotine') } -- parametri opzionali OptionalParameters = OptionalParameters or {} local nToolIndex = OptionalParameters.nToolIndex local bReduceBladePath = OptionalParameters.bReduceBladePath -- se nToolIndex è presente, GetBestBlade sceglierà solo il lato, altrimenti sceglierà lama e lato local OptionalParametersGetBestBlade = { dElevationTop = EdgeToMachineList.Top.dElevation + BeamData.CUT_EXTRA, dElevationBottom = EdgeToMachineList.Bottom.dElevation + BeamData.CUT_EXTRA, dElevationTopDownUp = EdgeToMachineList.TopDownUp.dElevation + BeamData.CUT_EXTRA, nToolIndex = nToolIndex } nToolIndex, sChosenBladeType = GetBestBlade( Proc, Part, FaceToMachine, OptionalParametersGetBestBlade) -- se possibile, upgrade del taglio tipo 'Top' a taglio a ghigliottina if sChosenBladeType == 'Top' and bReduceBladePath and FaceData.IsFaceRectangular( Proc, FaceToMachine.id) then -- ricerca lama migliore testa sopra local ToolInfo = MachiningLib.FindBlade( Proc, { vtN = Proc.Faces[1].vtN, bAllowTopHead = true, bAllowBottomHead = false }) -- test fattibilità taglio a ghigliottina if ToolInfo.nToolIndex then local dRadialOffset = FaceByBlade.GetRadialOffsetForGuillotine( nToolIndex, EdgeToMachineList.TopGuillotine.dLength) if ( TOOLS[ToolInfo.nToolIndex].dMaxMaterial - EdgeToMachineList.TopGuillotine.dElevation - BeamData.CUT_SIC) > dRadialOffset + 10 * GEO.EPS_SMALL then nToolIndex = ToolInfo.nToolIndex sChosenBladeType = 'TopGuillotine' end end end return nToolIndex, EdgeToMachineList[sChosenBladeType], sChosenBladeType end local function GetDualSideCutStrategy( Proc, OptionalParameters) local FaceToMachine = Proc.Faces[1] -- parametri opzionali OptionalParameters = OptionalParameters or {} local nToolIndex = OptionalParameters.nToolIndex local CuttingParametersList local EdgeToMachine -- se possibile, si prova il doppio taglio orizzontale con mix testa sopra/sotto (questa modalità si esclude se l'utensile arriva dall'esterno) -- TODO valutare se farlo anche doppio orizzontale solo con testa sopra, se la lama ha la corsa per andare sotto! EdgeToMachine = GetEdgeToMachine( FaceToMachine.Edges, FaceToMachine.vtN, 'HorizontalBottom') if not nToolIndex and AreSameOrOppositeVectorApprox( EdgeToMachine.vtN, Z_AX()) then -- inizialmente si prova a lavorare in mezzeria local dDepthToMachineTop = EdgeToMachine.dElevation / 2 + BeamData.CUT_EXTRA_MIN local dDepthToMachineBottom = dDepthToMachineTop -- ricerca lama testa sopra local ToolInfoTopBlade = MachiningLib.FindBlade( Proc, { vtN = FaceToMachine.vtN, bAllowTopHead = true, bAllowBottomHead = false, dElevation = dDepthToMachineTop }) -- ricerca lama testa sotto local ToolInfoBottomBlade = MachiningLib.FindBlade( Proc, { vtN = FaceToMachine.vtN, bAllowTopHead = false, bAllowBottomHead = true, dElevation = dDepthToMachineBottom }) local bIsTopLimited = ToolInfoTopBlade.dResidualDepth > 10 * GEO.EPS_SMALL local bIsBottomLimited = ToolInfoBottomBlade.dResidualDepth > 10 * GEO.EPS_SMALL -- testa sopra limitante if bIsTopLimited and not bIsBottomLimited then -- ricalcolo profondità di lavoro dDepthToMachineTop = TOOLS[ToolInfoTopBlade.nToolIndex].dMaxMaterial dDepthToMachineBottom = EdgeToMachine.dElevation - dDepthToMachineTop + BeamData.CUT_EXTRA_MIN -- ricerca lama sotto con la nuova depth ToolInfoBottomBlade = MachiningLib.FindBlade( Proc, { vtN = FaceToMachine.vtN, bAllowTopHead = false, bAllowBottomHead = true, dElevation = dDepthToMachineBottom }) bIsBottomLimited = ToolInfoBottomBlade.dResidualDepth > 10 * GEO.EPS_SMALL -- testa sotto limitante elseif bIsBottomLimited and not bIsTopLimited then -- ricalcolo profondità di lavoro dDepthToMachineBottom = TOOLS[ToolInfoBottomBlade.nToolIndex].dMaxMaterial dDepthToMachineTop = EdgeToMachine.dElevation - dDepthToMachineBottom + BeamData.CUT_EXTRA_MIN -- ricerca lama sopra con la nuova depth ToolInfoTopBlade = MachiningLib.FindBlade( Proc, { vtN = FaceToMachine.vtN, bAllowTopHead = true, bAllowBottomHead = false, dElevation = dDepthToMachineTop }) bIsTopLimited = ToolInfoTopBlade.dResidualDepth > 10 * GEO.EPS_SMALL end -- se almeno una delle due lame ci arriva si assegnano utensili e profondità da ritornare if not ( bIsTopLimited and bIsBottomLimited) and ToolInfoTopBlade.nToolIndex and ToolInfoBottomBlade.nToolIndex then CuttingParametersList = { { nToolIndex = ToolInfoBottomBlade.nToolIndex, dDepthToMachine = dDepthToMachineBottom }, { nToolIndex = ToolInfoTopBlade.nToolIndex, dDepthToMachine = dDepthToMachineTop } } return CuttingParametersList, EdgeToMachine end end -- arrivati qui si prova il taglio doppio verticale (taglio doppio orizzontale non si può fare oppure non ci arriva) EdgeToMachine = GetEdgeToMachine( FaceToMachine.Edges, FaceToMachine.vtN, 'Vertical') local dDepthToMachine = EdgeToMachine.dElevation / 2 + BeamData.CUT_EXTRA_MIN -- ricerca lama testa sopra local ToolInfo = MachiningLib.FindBlade( Proc, { vtN = FaceToMachine.vtN, bAllowTopHead = true, bAllowBottomHead = false, dElevation = dDepthToMachine }) -- se lama ci arriva si assegnano utensili e profondità da ritornare if ToolInfo.nToolIndex and ToolInfo.dResidualDepth < 10 * GEO.EPS_SMALL then CuttingParametersList = { { nToolIndex = ToolInfo.nToolIndex, dDepthToMachine = dDepthToMachine }, { nToolIndex = ToolInfo.nToolIndex, dDepthToMachine = dDepthToMachine } } else EdgeToMachine = nil end return CuttingParametersList, EdgeToMachine end local function CutWholeWaste( Proc, Part, OptionalParameters) local Machinings = {} local Cutting = nil local CuttingDouble = nil local Result = {} local EdgeToMachine local sChosenBladeType = '' local dCompletionPercentage = 0 -- controlli preventivi local bIsFeatureLong = FeatureLib.IsMachiningLong( Proc.b3Box:getDimX(), Part, { dMaxSegmentLength = BeamData.LONGCUT_ENDLEN}) if bIsFeatureLong then Result = FeatureLib.GetStrategyResultNotApplicable('Feature too long') return Machinings, Result end -- parametri opzionali e default if not OptionalParameters then OptionalParameters = {} end local nToolIndex = OptionalParameters.nToolIndex local dExtendAfterTail = OptionalParameters.dExtendAfterTail or 10000 local bReduceBladePath = OptionalParameters.bReduceBladePath or false -- si cerca di tagliare tutto da un lato nToolIndex, EdgeToMachine, sChosenBladeType = GetSingleCutStrategy( Proc, Part, OptionalParameters) -- se taglio da un lato ce la fa, si crea la lavorazione if nToolIndex then local OptionalParametersFaceByBlade = { dDepthToMachine = EdgeToMachine.dElevation + BeamData.CUT_EXTRA, nToolIndex = nToolIndex, dExtendAfterTail = dExtendAfterTail, bReduceBladePath = bReduceBladePath } if sChosenBladeType == 'Top' or sChosenBladeType == 'Bottom' then OptionalParametersFaceByBlade.OppositeToolDirectionMode = 'Optimized' end Cutting = FaceByBlade.Make( Proc, Part, Proc.Faces[1], EdgeToMachine, OptionalParametersFaceByBlade) -- se taglio da un lato non ce la fa, si prova da due lati -- TODO valutare se estendere ai non parallelogrammi elseif FaceData.IsFaceRhomboid( Proc, Proc.Faces[1].id) then local CuttingParametersList CuttingParametersList, EdgeToMachine = GetDualSideCutStrategy( Proc, OptionalParameters) if CuttingParametersList then local OptionalParametersFaceByBlade = { dDepthToMachine = CuttingParametersList[1].dDepthToMachine, nToolIndex = CuttingParametersList[1].nToolIndex, dExtendAfterTail = dExtendAfterTail, bReduceBladePath = bReduceBladePath, OppositeToolDirectionMode = 'Enabled' } local OptionalParametersFaceByBladeDouble = { dDepthToMachine = CuttingParametersList[2].dDepthToMachine, nToolIndex = CuttingParametersList[2].nToolIndex, dExtendAfterTail = dExtendAfterTail, bReduceBladePath = bReduceBladePath } Cutting = FaceByBlade.Make( Proc, Part, Proc.Faces[1], EdgeToMachine, OptionalParametersFaceByBlade) CuttingDouble = FaceByBlade.Make( Proc, Part, Proc.Faces[1], EdgeToMachine, OptionalParametersFaceByBladeDouble) end end if Cutting and Cutting.bIsApplicable then table.insert( Machinings, Cutting) dCompletionPercentage = Cutting.dCompletionPercentage or dCompletionPercentage if CuttingDouble and CuttingDouble.bIsApplicable then table.insert( Machinings, CuttingDouble) dCompletionPercentage = 0.5 * ( Cutting.dCompletionPercentage or dCompletionPercentage) + 0.5 * ( CuttingDouble.dCompletionPercentage or dCompletionPercentage) end end -- risultati del calcolo -- TODO funzione? if ( Cutting and Cutting.bIsApplicable) and ( not CuttingDouble or CuttingDouble.bIsApplicable) then if dCompletionPercentage > 100 - 10 * GEO.EPS_SMALL then Result.sStatus = 'Completed' else Result.sStatus = 'Not-Completed' end Result.dCompletionPercentage = dCompletionPercentage Result.nCompletionIndex = FeatureLib.GetFeatureCompletionIndex( dCompletionPercentage) Result.nQuality = FeatureLib.GetStrategyQuality( Machinings) Result.dTimeToMachine = FeatureLib.GetStrategyTimeToMachine( Machinings) Result.dMRR = ( FeatureInfo.dFeatureVolume / Result.dTimeToMachine) / pow( 10, 6) else Result = FeatureLib.GetStrategyResultNotApplicable() end return Machinings, Result end local function CutWithDicing( Proc, Part, OptionalParameters) local Machinings = {} local Result = {} -- parametri opzionali e default if not OptionalParameters then OptionalParameters = {} end local nToolIndex = OptionalParameters.nToolIndex local dExtendAfterTail = OptionalParameters.dExtendAfterTail or 10000 local b3BoxDicing = OptionalParameters.b3BoxDicing local bSaveAddedGeometries = OptionalParameters.bSaveAddedGeometries if bSaveAddedGeometries == nil then bSaveAddedGeometries = true end -- 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 local sChosenBladeType = '' if not nToolIndex then nToolIndex, sChosenBladeType = GetBestBlade( Proc, Part, Face1) end -- se non trovata lama la lavorazione non è fattibile if not nToolIndex then Result = FeatureLib.GetStrategyResultNotApplicable( 'Blade not found') return Machinings, Result end -- limite per taglio DownUp local dMinNzDownUp = TOOLS[nToolIndex].SetupInfo.GetMinNzDownUp( Part.b3Raw, Face1.vtN, nil, TOOLS[nToolIndex]) -- 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 dDiceDimensionReduced = min( dDiceDimension, dDiceDimension - TOOLS[nToolIndex].SetupInfo.dMaxMatDecrease) 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 OptionalParametersDiceCut.b3BoxDicing = b3BoxDicing local vCuts = DiceCut.GetDice( Part, Face1, Face2, OptionalParametersDiceCut) -- se nessun cubetto trovato, si richiama il taglio singolo if #vCuts == 0 then Machinings, Result = CutWholeWaste( Proc, Part, OptionalParameters) return Machinings, Result end -- lavorazione cubetti local bIsDicingOk = true local bMoveAfterSplit = false -- eventuale inversione tagli ortogonali e aggiunta informazioni alla geometria local bAreOrthogonalCutsInverted = false for i = 1, #vCuts do for j = 1, #vCuts[i] do SetDiceFaceInfo( Proc, vCuts[i][j], bSaveAddedGeometries) 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 -- calcolo lavorazioni for i = 1, #vCuts do -- determinazione direzione di taglio 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 = {} local _, Edges = FaceData.GetEdgesInfo( vCuts[i][1], 0) local vtNCurrentFace = EgtSurfTmFacetNormVersor( vCuts[i][1], 0, GDB_ID.ROOT) EdgeToMachine = GetEdgeToMachine( Edges, vtNCurrentFace, sChosenBladeType) vtToolDirection = EdgeToMachine.vtN end end -- calcolo lavorazione della singola faccia -- per tagli paralleli e faccia aperta si prova a tagliare come se fosse una faccia singola if ( Proc.nFct == 1) and ( ( i % 2) == 0) then local nAddGrpId = BeamLib.GetAddGroup( Part.id) local nSurfToCut = EgtSurfTmBySewing( nAddGrpId, vCuts[i], false) local ProcTrimesh = FeatureLib.GetProcFromTrimesh( nSurfToCut, Part) -- TODO ridurre con bReduceBladePath anche questi tagli? local OptionalParametersCutWholeWaste = { nToolIndex = nToolIndex, dExtendAfterTail = dExtendAfterTail } local CutWholeWasteMachinings, CutWholeWasteResult = CutWholeWaste( ProcTrimesh, Part, OptionalParametersCutWholeWaste) if CutWholeWasteResult.sStatus == 'Completed' then for k = 1, #CutWholeWasteMachinings do table.insert( Machinings, CutWholeWasteMachinings[k]) if CutWholeWasteMachinings[k].sStage == 'AfterTail' then bMoveAfterSplit = true end end else EgtErase( nSurfToCut) bIsDicingOk = false end end -- per tagli perpendicolari oppure se il taglio a faccia singola non è andato si fanno i tagli standard if ( i % 2) ~= 0 or not bIsDicingOk then for j = 1, #vCuts[i] do local Cutting = {} local vtNCurrentFace = EgtSurfTmFacetNormVersor( vCuts[i][j], 0, GDB_ID.ROOT) local FaceToMachine = { id = 0, vtN = vtNCurrentFace} -- se taglio DownUp e strato pari composto da 1 o 2 elementi (tutti gli altri casi vengono saltati) if ( vtNCurrentFace:getZ() < dMinNzDownUp) and ( ( i % 2) == 0) and ( #vCuts[i] <= 2) then -- il primo elemento prende la direzione prevista, il secondo quella opposta local vtToolDirectionNew = Vector3d( vtToolDirection) if j ~= 1 then vtToolDirectionNew = -vtToolDirection end -- lavorazione local ProcTrimesh = FeatureLib.GetProcFromTrimesh( vCuts[i][j], Part) local _, Edges = FaceData.GetEdgesInfo( vCuts[i][j], 0) local EdgeToMachine = BeamLib.FindEdgeBestOrientedAsDirection( Edges, vtToolDirectionNew) local dDepthToMachine = EdgeToMachine.dElevation + BeamData.CUT_EXTRA local OptionalParametersFaceByBlade = { dDepthToMachine = dDepthToMachine, nToolIndex = nToolIndex, dRadialStepSpan = 0, dExtendAfterTail = dExtendAfterTail } Cutting = FaceByBlade.Make( ProcTrimesh, Part, FaceToMachine, EdgeToMachine, OptionalParametersFaceByBlade) Cutting.ptCenter = Point3d( ProcTrimesh.Faces[1].ptCenter:getX(), 0, 0) if Cutting.bIsApplicable then table.insert( Machinings, Cutting) else bIsDicingOk = false end if Cutting.sStage == 'AfterTail' then bMoveAfterSplit = true end -- 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 è faccia singola si affonda elseif Proc.nFct < 2 then -- se tagli ortogonali invertiti, devo approfondire dello spessore lama if bAreOrthogonalCutsInverted then dExtraCut = TOOLS[nToolIndex].dThickness -- se ultimo taglio, devo affondare elseif j == #vCuts[i] then dExtraCut = BeamData.CUT_EXTRA end end end local ProcTrimesh = FeatureLib.GetProcFromTrimesh( vCuts[i][j], Part) local _, Edges = FaceData.GetEdgesInfo( vCuts[i][j], 0) local EdgeToMachine = BeamLib.FindEdgeBestOrientedAsDirection( Edges, vtToolDirection) local dDepthToMachine = EdgeToMachine.dElevation + dExtraCut local OptionalParametersFaceByBlade = { dDepthToMachine = dDepthToMachine, nToolIndex = nToolIndex, dRadialStepSpan = 0, dExtendAfterTail = dExtendAfterTail } Cutting = FaceByBlade.Make( ProcTrimesh, Part, FaceToMachine, EdgeToMachine, OptionalParametersFaceByBlade) Cutting.ptCenter = Point3d( ProcTrimesh.Faces[1].ptCenter:getX(), 0, 0) if Cutting.bIsApplicable then table.insert( Machinings, Cutting) bIsDicingOk = true else bIsDicingOk = false end if Cutting.sStage == 'AfterTail' then bMoveAfterSplit = true end end end end end -- se presente anche solo una lavorazione AfterTail si spostano tutte if bMoveAfterSplit then for i = 1, #Machinings do Machinings[i].sStage = 'AfterTail' end end -- risultati del calcolo if bIsDicingOk then Result.sStatus = 'Completed' Result.dCompletionPercentage = 100 Result.nQuality = TOOLS[nToolIndex].nQuality Result.dTimeToMachineOriginal = FeatureLib.GetStrategyTimeToMachine( Machinings) Result.dTimeToMachine = Result.dTimeToMachineOriginal * 1.3 Result.dMRR = ( FeatureInfo.dFeatureVolume / Result.dTimeToMachine) / pow( 10, 6) else Result = FeatureLib.GetStrategyResultNotApplicable() end return Machinings, Result end local function MakeOneFace( ProcOrId, Part, OptionalParameters) local Machinings = {} local Result = {} -- disambiguazione feature vs id trimesh local Proc = {} if type( ProcOrId) == "table" then Proc = ProcOrId elseif type( ProcOrId) == "number" then Proc = FeatureLib.GetProcFromTrimesh( ProcOrId, Part) else error( 'BLADETOWASTE : Only feature or trimesh supported') end -- parametri opzionali e default if not OptionalParameters then OptionalParameters = {} end local bDisableDicing = OptionalParameters.bDisableDicing or false -- 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 bDisableDicing or FeatureInfo.bIsFeatureSmall then Machinings, Result = CutWholeWaste( Proc, Part, OptionalParameters) end -- se non completata con taglio singolo, lavorazione con cubetti if ( not bDisableDicing) and ( not Result.sStatus or Result.sStatus ~= 'Completed') then Machinings, Result = CutWithDicing( Proc, Part, OptionalParameters) end return Machinings, Result end function BLADETOWASTE.Make( ProcOrId, Part, OptionalParameters) local Machinings = {} local Result = {} -- disambiguazione feature vs id trimesh local Proc = {} if type( ProcOrId) == "table" then Proc = ProcOrId elseif type( ProcOrId) == "number" then Proc = FeatureLib.GetProcFromTrimesh( ProcOrId, 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.Topology and ( Proc.Topology.sName == 'Rabbet-2-Through') then -- ricerca lato in comune local nCommonEdge for i = 1, #Proc.Faces[1].Edges do if Proc.Faces[1].Edges[i].idAdjacentFace > -1 then nCommonEdge = i end end -- se due facce, cubetti troppo lunghi in X non si possono fare local ptEdge1, _, ptEdge2 = EgtSurfTmFacetOppositeSide( Proc.id, Proc.Faces[1].id, -Proc.Faces[1].Edges[nCommonEdge].vtN, GDB_ID.ROOT) local b3BoxEdge = BBox3d( ptEdge1, ptEdge2) local dEdgeLengthOnX = b3BoxEdge:getDimX() if FeatureLib.IsMachiningLong( dEdgeLengthOnX, Part) then -- TODO qui meglio return nil? è una funzione di libreria error( 'BLADETOWASTE : common edge too long') end end -- parametri opzionali e default -- quelli non censiti sono gestiti direttamente nelle funzioni sotto e devono quindi solo transitare OptionalParameters = OptionalParameters or {} 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 if OptionalParameters.b3BoxDicing then FeatureInfo.dFeatureVolume = OptionalParameters.b3BoxDicing:getDimX() * OptionalParameters.b3BoxDicing:getDimY() * OptionalParameters.b3BoxDicing:getDimZ() FeatureInfo.dFeatureMaxDimension = max( OptionalParameters.b3BoxDicing:getDimX(), OptionalParameters.b3BoxDicing:getDimY()) else FeatureInfo.dFeatureVolume = FeatureLib.GetFeatureVolume( Proc, Part) FeatureInfo.dFeatureMaxDimension = max( Proc.b3Box:getDimX(), Proc.b3Box:getDimY()) end FeatureInfo.bIsFeatureSmall = FeatureInfo.dFeatureVolume < dMaxWasteVolume + 10 * GEO.EPS_SMALL and FeatureInfo.dFeatureMaxDimension < dMaxWasteLength + 10 * GEO.EPS_SMALL -- calcolo lavorazioni if Proc.nFct == 1 then Machinings, Result = MakeOneFace( Proc, Part, OptionalParameters) elseif Proc.nFct == 2 then local dAngleBetweenFaces = Proc.AdjacencyMatrix[1][2] -- feature convessa: lavorata come 2 facce singole con prolungamento -- TODO qui sostituire con check topologia DoubleBevel if dAngleBetweenFaces > 10 * GEO.EPS_SMALL then -- si creano superfici singole per le facce, estese fino al box della parte local nAddGrpId = BeamLib.GetAddGroup( Part.id) local idFace1 = EgtSurfTmPlaneInBBox( nAddGrpId, Proc.Faces[1].ptCenter, Proc.Faces[1].vtN, Part.b3Part, GDB_ID.ROOT) local idFace2 = EgtSurfTmPlaneInBBox( nAddGrpId, Proc.Faces[2].ptCenter, Proc.Faces[2].vtN, Part.b3Part, GDB_ID.ROOT) if not bSaveAddedGeometries then EgtSetLevel( idFace1, GDB_LV.TEMP) EgtSetLevel( idFace2, GDB_LV.TEMP) end -- lavorazione facce local Machinings1, Result1 = MakeOneFace( idFace1, Part, OptionalParameters) local Machinings2, Result2 = MakeOneFace( idFace2, Part, OptionalParameters) -- tutte le lavorazioni raggruppatee e calcolo area lavorata local dAreaToMachineTotal = 0 -- TODO SISTEMARE!! A volte la lavorazione risulta completa, ma l'area lavorata è inferiore all'area totale e ritorna lavorazione incompleta (vedi i due casi sotto) for i = 1, #Machinings1 do if Machinings1[i].bIsApplicable then table.insert( Machinings, Machinings1[i]) -- TODO controllare come mai nel progetto fiera saomad, pezzo 8 ritorna area lavorata più piccola pur lavorando tutto if Result1.sStatus == 'Completed' then dAreaToMachineTotal = dAreaToMachineTotal + EgtSurfArea( idFace1) else dAreaToMachineTotal = dAreaToMachineTotal + Machinings1[i].dAreaToMachine end end end for i = 1, #Machinings2 do if Machinings2[i].bIsApplicable then table.insert( Machinings, Machinings2[i]) -- TODO controllare come mai nel progetto fiera saomad, pezzo 8 ritorna area lavorata più piccola pur lavorando tutto if Result2.sStatus == 'Completed' then dAreaToMachineTotal = dAreaToMachineTotal + EgtSurfArea( idFace2) else dAreaToMachineTotal = dAreaToMachineTotal + Machinings2[i].dAreaToMachine end end end -- calcolo risultati Result.nQuality = FeatureLib.GetStrategyQuality( Machinings) Result.dCompletionPercentage = dAreaToMachineTotal / ( EgtSurfArea( idFace1) + EgtSurfArea( idFace2)) * 100 Result.nCompletionIndex = FeatureLib.GetFeatureCompletionIndex( Result.dCompletionPercentage) Result.dTimeToMachine = Result1.dTimeToMachine + Result2.dTimeToMachine Result.dMRR = ( FeatureInfo.dFeatureVolume / Result.dTimeToMachine) / pow( 10, 6) if Result1.sStatus == 'Completed' and Result2.sStatus == 'Completed' then Result.sStatus = 'Completed' elseif Result1.sStatus == 'Completed' or Result2.sStatus == 'Completed' then Result.sStatus = 'Not-Completed' else Result = FeatureLib.GetStrategyResultNotApplicable() end -- feature concava >= 90deg: lavorata con cubetti elseif dAngleBetweenFaces >= -91 then Machinings, Result = CutWithDicing( Proc, Part, OptionalParameters) -- feature concava < 90deg: lavorata come 2 facce singole con accorciamento else -- TODO da fare end end return Machinings, Result end ------------------------------------------------------------------------------------------------------------- return BLADETOWASTE