-- 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( 'BeamDataNew') 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') 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, sBladeType, OptionalParameters) local EdgeToMachine = {} -- parametri opzionali OptionalParameters = OptionalParameters or {} local bAllowFastCuts = OptionalParameters.bAllowFastCuts or false local nIndex = OptionalParameters.nIndex or 1 local EdgesSorted = {} for i = 1, #Edges do table.insert( EdgesSorted, Edges[i]) end if ( sBladeType == 'Top') then if bAllowFastCuts then table.sort( EdgesSorted, CompareEdgesNoPreference) else table.sort( EdgesSorted, CompareEdgesTopHead) end EdgeToMachine = EdgesSorted[nIndex] elseif sBladeType == 'Bottom' then table.sort( EdgesSorted, CompareEdgesBottomHead) EdgeToMachine = EdgesSorted[nIndex] elseif sBladeType == 'TopGuillotine' then table.sort( EdgesSorted, CompareEdgesTopHeadGuillotine) EdgeToMachine = EdgesSorted[nIndex] elseif sBladeType == 'HorizontalBottom' then table.sort( EdgesSorted, CompareEdgesHorizontalBottom) EdgeToMachine = EdgesSorted[nIndex] elseif sBladeType == 'Vertical' then table.sort( EdgesSorted, CompareEdgesVertical) EdgeToMachine = EdgesSorted[nIndex] -- TODO non testato; non si dovrebbe mai entrare in questo caso else table.sort( EdgesSorted, CompareEdgesNoPreference) EdgeToMachine = EdgesSorted[nIndex] 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 EdgeToMachineTop = OptionalParameters.EdgeToMachineTop local EdgeToMachineBottom = OptionalParameters.EdgeToMachineBottom local bIsDicing = OptionalParameters.bIsDicing local sRestLengthSideForPreSimulation = OptionalParameters.sRestLengthSideForPreSimulation local bCannotSplitRestLength = OptionalParameters.bCannotSplitRestLength -- TODO qui sarebbe meglio avere dExtra come OptionalParameter??? local dElevationTop = EdgeToMachineTop and EdgeToMachineTop.dElevation + BeamData.CUT_EXTRA or nil local dElevationBottom = EdgeToMachineBottom and EdgeToMachineBottom.dElevation + BeamData.CUT_EXTRA or nil -- 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 ( not dElevationTop) or ( TOOLS[OptionalParameters.nToolIndex].dMaxMaterial > dElevationTop - 10 * GEO.EPS_SMALL) then nToolIndexTop = OptionalParameters.nToolIndex end elseif TOOLS[OptionalParameters.nToolIndex].SetupInfo.HeadType.bBottom then if ( not dElevationBottom) or ( 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, { bAllowTopHead = true, bAllowBottomHead = false, dElevation = dElevationTop, FaceToMachine = Face, EdgeToMachine = EdgeToMachineTop, Part = Part, bIsDicing = bIsDicing, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength }) if ToolInfo.dResidualDepth < 10 * GEO.EPS_SMALL then nToolIndexTop = ToolInfo.nToolIndex end -- ricerca lama testa sotto ToolInfo = MachiningLib.FindBlade( Proc, { bAllowTopHead = false, bAllowBottomHead = true, dElevation = dElevationBottom, FaceToMachine = Face, EdgeToMachine = EdgeToMachineBottom, Part = Part, bIsDicing = bIsDicing, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength }) if ToolInfo.dResidualDepth < 10 * GEO.EPS_SMALL then nToolIndexBottom = ToolInfo.nToolIndex end end -- lama sopra e sotto -- TODO rivedere con Engagement, MinNzTopBlade e MaxNyTopBlade sono obsoleti!!!!!!!!!!!! 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' elseif nChosenToolIndex == nToolIndexTop then sChosenBladeType = 'Top' end -- se non trovata alcuna lama ritorna nil return nChosenToolIndex, sChosenBladeType end local function SetDiceFaceInfo( Proc, idDiceFace) EgtSetName( idDiceFace, 'Face' .. tostring( Proc.idFeature or 'Add') .. '_Dice') end local function GetSingleCutStrategy( Proc, Part, OptionalParameters) -- parametri opzionali OptionalParameters = OptionalParameters or {} local nToolIndex = OptionalParameters.nToolIndex local bReduceBladePath = OptionalParameters.bReduceBladePath or false local bAllowFastCuts = OptionalParameters.bAllowFastCuts or false local FaceToMachine = Proc.Faces[OptionalParameters.nFaceToMachineIndex or 1] local bIsDicing = OptionalParameters.bIsDicing or false local sRestLengthSideForPreSimulation = OptionalParameters.sRestLengthSideForPreSimulation or 'Tail' local bCannotSplitRestLength = OptionalParameters.bCannotSplitRestLength or false -- lati da lavorare in base al tipo di lama -- se non arrivano dall'esterno si cercano i migliori disponibili, dapprima ignorando il flag bAllowFastCuts local EdgeToMachineList = OptionalParameters.EdgeToMachineList or { Top = GetEdgeToMachine( FaceToMachine.Edges, 'Top'), Bottom = GetEdgeToMachine( FaceToMachine.Edges, 'Bottom'), TopGuillotine = GetEdgeToMachine( FaceToMachine.Edges, 'TopGuillotine') } local sChosenBladeType local bIsFastCut = false local OptionalParametersGetBestBlade = { EdgeToMachineTop = EdgeToMachineList.Top, EdgeToMachineBottom = EdgeToMachineList.Bottom, nToolIndex = nToolIndex, bIsDicing = bIsDicing, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength } -- se nToolIndex è presente, GetBestBlade sceglierà solo il lato, altrimenti sceglierà lama e lato nToolIndex, sChosenBladeType = GetBestBlade( Proc, Part, FaceToMachine, OptionalParametersGetBestBlade) -- se non è stato trovato un utensile e utensile e lati non arrivavano dall'esterno, si prova a cambiare lato (si prende il secondo della lista), sempre ignorando bAllowFastCuts if not nToolIndex and not OptionalParameters.nToolIndex and not OptionalParameters.EdgeToMachineList then EdgeToMachineList.Top = GetEdgeToMachine( FaceToMachine.Edges, 'Top', { nIndex = 2}) EdgeToMachineList.Bottom = GetEdgeToMachine( FaceToMachine.Edges, 'Bottom', { nIndex = 2}) OptionalParametersGetBestBlade.EdgeToMachineTop = EdgeToMachineList.Top OptionalParametersGetBestBlade.EdgeToMachineBottom = EdgeToMachineList.Bottom nToolIndex, sChosenBladeType = GetBestBlade( Proc, Part, FaceToMachine, OptionalParametersGetBestBlade) end -- se ancora non è stato trovato un utensile e utensile e lati non arrivavano dall'esterno, se permesso, si prova a scegliere il primo lato a minor elevazione (bAllowFastCuts) if bAllowFastCuts and not nToolIndex and not OptionalParameters.nToolIndex and not OptionalParameters.EdgeToMachineList then EdgeToMachineList.Top = GetEdgeToMachine( FaceToMachine.Edges, 'Top', { bAllowFastCuts = true}) EdgeToMachineList.Bottom = GetEdgeToMachine( FaceToMachine.Edges, 'Bottom', { bAllowFastCuts = true}) OptionalParametersGetBestBlade.EdgeToMachineTop = EdgeToMachineList.Top OptionalParametersGetBestBlade.EdgeToMachineBottom = EdgeToMachineList.Bottom nToolIndex, sChosenBladeType = GetBestBlade( Proc, Part, FaceToMachine, OptionalParametersGetBestBlade) bIsFastCut = true end -- se ancora non è stato trovato un utensile e utensile e lati non arrivavano dall'esterno, se permesso, si prova a scegliere il secondo lato a minor elevazione (bAllowFastCuts) if bAllowFastCuts and not nToolIndex and not OptionalParameters.nToolIndex and not OptionalParameters.EdgeToMachineList then EdgeToMachineList.Top = GetEdgeToMachine( FaceToMachine.Edges, 'Top', { bAllowFastCuts = true, nIndex = 2}) EdgeToMachineList.Bottom = GetEdgeToMachine( FaceToMachine.Edges, 'Bottom', { bAllowFastCuts = true, nIndex = 2}) OptionalParametersGetBestBlade.EdgeToMachineTop = EdgeToMachineList.Top OptionalParametersGetBestBlade.EdgeToMachineBottom = EdgeToMachineList.Bottom nToolIndex, sChosenBladeType = GetBestBlade( Proc, Part, FaceToMachine, OptionalParametersGetBestBlade) bIsFastCut = true end -- se possibile, upgrade del taglio tipo 'Top' a taglio a ghigliottina. Si tenta anche se il taglio singolo non è riuscito. if ( sChosenBladeType == 'Top' or not nToolIndex) and bReduceBladePath and FaceData.IsFaceRectangle( FaceToMachine) then local nToolIndexGuillotine -- ricerca lama migliore testa sopra local ToolInfo = MachiningLib.FindBlade( Proc, { bAllowTopHead = true, bAllowBottomHead = false, dElevation = EdgeToMachineList.TopGuillotine.dElevation, FaceToMachine = FaceToMachine, EdgeToMachine = EdgeToMachineList.TopGuillotine, Part = Part, bIsDicing = bIsDicing, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength }) nToolIndexGuillotine = ToolInfo.nToolIndex -- test fattibilità taglio a ghigliottina if nToolIndexGuillotine then local dRadialOffset = FaceByBlade.GetRadialOffsetForGuillotine( nToolIndexGuillotine, EdgeToMachineList.TopGuillotine.dLength) if ( TOOLS[nToolIndexGuillotine].dMaxMaterial - EdgeToMachineList.TopGuillotine.dElevation - BeamData.CUT_SIC) > dRadialOffset + 10 * GEO.EPS_SMALL then nToolIndex = nToolIndexGuillotine sChosenBladeType = 'TopGuillotine' end end end -- se taglio su lati brutti si cambia il tipo local sChosenBladeTypeOriginal = sChosenBladeType if bIsFastCut then sChosenBladeType = sChosenBladeType .. 'Fast' end return nToolIndex, EdgeToMachineList[sChosenBladeTypeOriginal], sChosenBladeType end local function GetDualSideCutStrategy( Proc, Part, OptionalParameters) -- parametri opzionali OptionalParameters = OptionalParameters or {} local nToolIndex = OptionalParameters.nToolIndex local FaceToMachine = Proc.Faces[OptionalParameters.nFaceToMachineIndex or 1] local bIsDicing = OptionalParameters.bIsDicing or false local sRestLengthSideForPreSimulation = OptionalParameters.sRestLengthSideForPreSimulation or 'Tail' local bCannotSplitRestLength = OptionalParameters.bCannotSplitRestLength or false 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, '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, { bAllowTopHead = true, bAllowBottomHead = false, dElevation = dDepthToMachineTop, FaceToMachine = FaceToMachine, EdgeToMachine = EdgeToMachine, Part = Part, bIsDicing = bIsDicing, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength }) -- ricerca lama testa sotto -- lavorando dalla direzione opposta si verifica come se si lavorasse il lato opposto local ToolInfoBottomBlade = MachiningLib.FindBlade( Proc, { bAllowTopHead = false, bAllowBottomHead = true, dElevation = dDepthToMachineBottom, FaceToMachine = FaceToMachine, EdgeToMachine = BeamLib.FindEdgeBestOrientedAsDirection( FaceToMachine.Edges, -EdgeToMachine.vtN), Part = Part, bIsDicing = bIsDicing, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength }) 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 -- lavorando dalla direzione opposta si verifica come se si lavorasse il lato opposto ToolInfoBottomBlade = MachiningLib.FindBlade( Proc, { bAllowTopHead = false, bAllowBottomHead = true, dElevation = dDepthToMachineBottom, FaceToMachine = FaceToMachine, EdgeToMachine = BeamLib.FindEdgeBestOrientedAsDirection( FaceToMachine.Edges, -EdgeToMachine.vtN), Part = Part, bIsDicing = bIsDicing, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength }) 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, { bAllowTopHead = true, bAllowBottomHead = false, dElevation = dDepthToMachineTop, FaceToMachine = FaceToMachine, EdgeToMachine = EdgeToMachine, Part = Part, bIsDicing = bIsDicing, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength }) 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, 'Vertical') local dDepthToMachine = EdgeToMachine.dElevation / 2 + BeamData.CUT_EXTRA_MIN -- ricerca lama testa sopra local ToolInfo = MachiningLib.FindBlade( Proc, { bAllowTopHead = true, bAllowBottomHead = false, dElevation = dDepthToMachine, FaceToMachine = FaceToMachine, EdgeToMachine = EdgeToMachine, Part = Part, bIsDicing = bIsDicing, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength }) -- se lama ci arriva si assegnano utensili e profondità da ritornare if ToolInfo.nToolIndex and ToolInfo.dResidualDepth < 10 * GEO.EPS_SMALL then -- lavorando dalla direzione opposta si verifica come se si lavorasse il lato opposto local EdgeToMachineOpposite = BeamLib.FindEdgeBestOrientedAsDirection( FaceToMachine.Edges, -EdgeToMachine.vtN) local BladeEngagementParameters = { Face = FaceToMachine, Edge = EdgeToMachineOpposite, Part = Part, Tool = TOOLS[ToolInfo.nToolIndex], dDepthToMachine = dDepthToMachine } local BladeEngagementOptionalParameters = { bIsDicing = bIsDicing, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength } TIMER:startElapsed( 'GetBladeEngagement') local bIsApplicableOpposite = MachiningLib.GetBladeEngagement( BladeEngagementParameters, BladeEngagementOptionalParameters) TIMER:stopElapsed( 'GetBladeEngagement') if not bIsApplicableOpposite then return CuttingParametersList, EdgeToMachine end -- si deve decidere se fare per primo il taglio opposto oppure no local bIsOppositeCutFirst = false local vtTemp = EdgeToMachine.vtN ^ FaceToMachine.vtN local bIsEdgeLeftSide = vtTemp:getZ() < 10 * GEO.EPS_SMALL if ( TOOLS[ToolInfo.nToolIndex].bIsCCW and bIsEdgeLeftSide) or ( not TOOLS[ToolInfo.nToolIndex].bIsCCW and not bIsEdgeLeftSide) then bIsOppositeCutFirst = true end CuttingParametersList = { { nToolIndex = ToolInfo.nToolIndex, dDepthToMachine = dDepthToMachine }, { nToolIndex = ToolInfo.nToolIndex, dDepthToMachine = dDepthToMachine }, bIsOppositeCutFirst = bIsOppositeCutFirst } 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 local sRestLengthSideForPreSimulation = OptionalParameters.sRestLengthSideForPreSimulation or 'Tail' local bCannotSplitRestLength = OptionalParameters.bCannotSplitRestLength 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, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength } if sChosenBladeType ~= 'TopGuillotine' 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 elseif FaceData.IsFaceRectangle( Proc.Faces[1]) then local CuttingParametersList CuttingParametersList, EdgeToMachine = GetDualSideCutStrategy( Proc, Part, OptionalParameters) if CuttingParametersList then local OptionalParametersFaceByBlade = { dDepthToMachine = CuttingParametersList[1].dDepthToMachine, nToolIndex = CuttingParametersList[1].nToolIndex, dExtendAfterTail = dExtendAfterTail, bReduceBladePath = bReduceBladePath, OppositeToolDirectionMode = 'Enabled', sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength } local OptionalParametersFaceByBladeDouble = { dDepthToMachine = CuttingParametersList[2].dDepthToMachine, nToolIndex = CuttingParametersList[2].nToolIndex, dExtendAfterTail = dExtendAfterTail, bReduceBladePath = bReduceBladePath, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength } -- se necessario, si inverte l'ordine delle lavorazioni per avere il secondo taglio verso l'alto if not CuttingParametersList.bIsOppositeCutFirst then OptionalParametersFaceByBlade, OptionalParametersFaceByBladeDouble = OptionalParametersFaceByBladeDouble, OptionalParametersFaceByBlade end Cutting = FaceByBlade.Make( Proc, Part, Proc.Faces[1], EdgeToMachine, OptionalParametersFaceByBlade) CuttingDouble = FaceByBlade.Make( Proc, Part, Proc.Faces[1], EdgeToMachine, OptionalParametersFaceByBladeDouble) end end -- se nessun taglio è riuscito si riprova il taglio singolo permettendo i lati a minor elevazione, abbassando la qualità if not ( Cutting and Cutting.bIsApplicable) or ( CuttingDouble and not CuttingDouble.bIsApplicable) then OptionalParameters.bAllowFastCuts = true nToolIndex, EdgeToMachine, sChosenBladeType = GetSingleCutStrategy( Proc, Part, OptionalParameters) if nToolIndex then local OptionalParametersFaceByBlade = { dDepthToMachine = EdgeToMachine.dElevation + BeamData.CUT_EXTRA, nToolIndex = nToolIndex, dExtendAfterTail = dExtendAfterTail, bReduceBladePath = bReduceBladePath, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength } if sChosenBladeType ~= 'TopGuillotine' then OptionalParametersFaceByBlade.OppositeToolDirectionMode = 'Optimized' end Cutting = FaceByBlade.Make( Proc, Part, Proc.Faces[1], EdgeToMachine, OptionalParametersFaceByBlade) 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.dCompletionIndex = FeatureLib.GetFeatureCompletionIndex( dCompletionPercentage) Result.dQuality = FeatureLib.GetStrategyQuality( Machinings) -- per tagli con lati brutti o ghigliottina si abbassa la qualità if ( sChosenBladeType ~= 'Top') and ( sChosenBladeType ~= 'Bottom') then Result.dQuality = FeatureLib.GetStrategyQuality( 'STD') end 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 CalculateDiceMachinings( vCuts, Parameters) local Machinings = {} local bMoveAfterSplit = false local Proc = Parameters.Proc local Part = Parameters.Part local MainFace = Parameters.MainFace local Tool = Parameters.Tool local sChosenBladeType = Parameters.sChosenBladeType local dExtendAfterTail = Parameters.dExtendAfterTail local bReduceBladePath = Parameters.bReduceBladePath local sRestLengthSideForPreSimulation = Parameters.sRestLengthSideForPreSimulation local bCannotSplitRestLength = Parameters.bCannotSplitRestLength local bReduceDiceDepth = Parameters.bReduceDiceDepth -- 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]) -- TODO vedere se questa parte serve ancora; in teoria no perchè il taglio è girato automaticamente nella FaceByBlade -- if ( i % 2) == 1 then -- local vtO = EgtSurfTmFacetNormVersor( vCuts[i][j], 0, GDB_ID.ROOT) -- if MachiningLib.IsFaceZOutOfRange( vtO, Tool) then -- EgtInvertSurf( vCuts[i][j]) -- local vtCurrentFaceNormal = EgtSurfTmFacetNormVersor( vCuts[i][j], 0, GDB_ID.ROOT) -- EgtMove( vCuts[i][j], -vtCurrentFaceNormal * Tool.dThickness, GDB_RT.GLOB) -- 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( MainFace.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 ^ MainFace.vtN local Frame = Frame3d( MainFace.ptCenter, MainFace.ptCenter + asseX, MainFace.ptCenter + asseY) local b3Fac = EgtGetBBoxRef( vCuts[i][1], GDB_BB.STANDARD, Frame) -- se lunghezza inferiore al limite, accetto la direzione if b3Fac:getDimX() < Tool.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) EdgeToMachine = GetEdgeToMachine( Edges, 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, accorpando i tagli -- TODO bIsDicing è da mettere a true? local bCanMergeParallelCuts = ( ( i % 2) == 0) and ( Proc.nFct == 1) local bIsDicingOk = true if bCanMergeParallelCuts then local nAddGrpId = BeamLib.GetAddGroup( Part.id) local nSurfToCut = EgtSurfTmBySewing( nAddGrpId, vCuts[i], false) local ProcTrimesh = FeatureLib.GetProcFromTrimesh( nSurfToCut, Part) local OptionalParametersCutWholeWaste = { nToolIndex = Tool.nIndex, dExtendAfterTail = dExtendAfterTail, bReduceBladePath = bReduceBladePath, bIsDicing = false, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength } 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 -- caso standard (tagli perpendicolari o paralleli non accorpabili) if ( not bCanMergeParallelCuts) or ( not bIsDicingOk) then for j = 1, #vCuts[i] do -- se abilitato, la lama sta sollevata per non rovinare le facce local dExtraCut if bReduceDiceDepth == false then dExtraCut = 0 else dExtraCut = -0.1 end -- 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 = Tool.dThickness -- se ultimo taglio, devo affondare elseif j == #vCuts[i] then dExtraCut = BeamData.CUT_EXTRA end end end local Cutting = {} local ProcTrimesh = FeatureLib.GetProcFromTrimesh( vCuts[i][j], Part) local FaceToMachine = ProcTrimesh.Faces[1] local EdgeToMachine = BeamLib.FindEdgeBestOrientedAsDirection( FaceToMachine.Edges, vtToolDirection) local dDepthToMachine = EdgeToMachine.dElevation + dExtraCut local OptionalParametersFaceByBlade = { dDepthToMachine = dDepthToMachine, nToolIndex = Tool.nIndex, dRadialStepSpan = 0, dExtendAfterTail = dExtendAfterTail, bIsDicing = true, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength, bDisableRealElevationCheck = ( i % 2) > 0 -- se taglio perpendicolare non si deve mai considerare il materiale precedente } 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) -- se un cubetto fallisce è inutile proseguire else Result = FeatureLib.GetStrategyResultNotApplicable( 'Dicing failed') return false, Machinings end if Cutting.sStage == 'AfterTail' then bMoveAfterSplit = true end end end end return true, Machinings, bMoveAfterSplit 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 ~= false) local bReduceBladePath = OptionalParameters.bReduceBladePath or false local sRestLengthSideForPreSimulation = OptionalParameters.sRestLengthSideForPreSimulation or 'Tail' local bCannotSplitRestLength = OptionalParameters.bCannotSplitRestLength or false -- 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 -- angolo tra le facce, se più di una local dAngleBetweenFaces = Proc.AdjacencyMatrix[1][2] or 0 -- 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 {}, Result end -- calcolo dimensione cubetto local dDiceDimension = min( TOOLS[nToolIndex].dMaxMaterial, Part.GeneralParameters.GEN_dMaxDimDice) dDiceDimension = dDiceDimension - BeamData.CUT_EXTRA -- calcolo cubetti local OptionalParametersDiceCut = {} OptionalParametersDiceCut.dOffsetParallel = dDiceDimension OptionalParametersDiceCut.dOffsetOrthogonal = dDiceDimension OptionalParametersDiceCut.b3BoxDicing = b3BoxDicing OptionalParametersDiceCut.bSaveAddedGeometries = bSaveAddedGeometries local vCuts = DiceCut.GetDice( Part, Face1, Face2, OptionalParametersDiceCut) -- se nessun cubetto trovato, si richiama ricorsivamente la BladeToWaste forzando di non fare i cubetti if #vCuts == 0 then OptionalParameters.bDisableDicing = true Machinings, Result = BLADETOWASTE.Make( Proc, Part, OptionalParameters) return Machinings, Result end -- lavorazione cubetti local bIsDicingOk local bMoveAfterSplit local Parameters = { Proc = Proc, Part = Part, MainFace = Face1, Tool = TOOLS[nToolIndex], sChosenBladeType = sChosenBladeType, dExtendAfterTail = dExtendAfterTail, bReduceBladePath = bReduceBladePath, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength, bReduceDiceDepth = ( dAngleBetweenFaces < - 10) -- per facce molto aperte non si riduce l'affondamento della lama nei cubetti (rischio che non si stacchino) } bIsDicingOk, Machinings, bMoveAfterSplit = CalculateDiceMachinings( vCuts, Parameters) -- se i cubetti falliscono si riprova con la dimensione ridotta (ad esempio quando tocca l'asse Z nei cubetti in coda) if not bIsDicingOk and BeamData.SAFE_DIM_DICE then dDiceDimension = BeamData.SAFE_DIM_DICE - BeamData.CUT_EXTRA OptionalParametersDiceCut.dOffsetParallel = dDiceDimension OptionalParametersDiceCut.dOffsetOrthogonal = dDiceDimension vCuts = DiceCut.GetDice( Part, Face1, Face2, OptionalParametersDiceCut) -- se nessun cubetto trovato, si richiama ricorsivamente la BladeToWaste forzando di non fare i cubetti if #vCuts == 0 then OptionalParameters.bDisableDicing = true Machinings, Result = BLADETOWASTE.Make( Proc, Part, OptionalParameters) return Machinings, Result end bIsDicingOk, Machinings, bMoveAfterSplit = CalculateDiceMachinings( vCuts, Parameters) end if not bIsDicingOk then Result = FeatureLib.GetStrategyResultNotApplicable() return {}, Result 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 Result.sStatus = 'Completed' Result.dCompletionPercentage = 100 Result.dQuality = FeatureLib.GetStrategyQuality( TOOLS[nToolIndex].sFamily) Result.dTimeToMachineOriginal = FeatureLib.GetStrategyTimeToMachine( Machinings) Result.dTimeToMachine = Result.dTimeToMachineOriginal * 2 Result.dMRR = ( FeatureInfo.dFeatureVolume / Result.dTimeToMachine) / pow( 10, 6) 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' or Proc.Topology.sName == 'Bevel-2-Blind') 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 = Proc.Faces[1].Edges[nCommonEdge].ptStart, Proc.Faces[1].Edges[nCommonEdge].ptEnd 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 bDisableDicing = OptionalParameters.bDisableDicing or false local bSaveAddedGeometries = ( OptionalParameters.bSaveAddedGeometries ~= false) local sRestLengthSideForPreSimulation = OptionalParameters.sRestLengthSideForPreSimulation or 'Tail' local bCannotSplitRestLength = OptionalParameters.bCannotSplitRestLength or false -- 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 = Proc.dVolume 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 idAddGroup if bSaveAddedGeometries then idAddGroup = BeamLib.GetAddGroup( Part.id) else idAddGroup = Part.idTempGroup end local idFace1 = EgtSurfTmPlaneInBBox( idAddGroup, Proc.Faces[1].ptCenter, Proc.Faces[1].vtN, Part.b3Part, GDB_ID.ROOT) local idFace2 = EgtSurfTmPlaneInBBox( idAddGroup, Proc.Faces[2].ptCenter, Proc.Faces[2].vtN, Part.b3Part, GDB_ID.ROOT) -- 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.dQuality = FeatureLib.GetStrategyQuality( Machinings) Result.dCompletionPercentage = dAreaToMachineTotal / ( EgtSurfArea( idFace1) + EgtSurfArea( idFace2)) * 100 Result.dCompletionIndex = 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, se attivi elseif not ( bDisableDicing or FeatureInfo.bIsFeatureSmall) and ( dAngleBetweenFaces >= -91) then Machinings, Result = CutWithDicing( Proc, Part, OptionalParameters) -- qui arrivano feature concave < 90deg, oppure >= 90deg con cubetti disabilitati: lavorata come 2 facce singole con eventuale accorciamento else -- per ogni faccia si calcola la lavorazione for i = 1, #Proc.Faces do -- ricerca lato in comune da lavorare; se non trovato è una feature splittata (DoubleBevel) e il lato sarà scelto della GetSingleCutStratetegy local nCommonEdgeIndex for j = 1, #Proc.Faces[i].Edges do if Proc.Faces[i].Edges[j].idAdjacentFace > -1 then nCommonEdgeIndex = j end end local EdgeToMachine if nCommonEdgeIndex then EdgeToMachine = Proc.Faces[i].Edges[nCommonEdgeIndex] end local EdgeToMachineList if EdgeToMachine then EdgeToMachineList = { Top = EdgeToMachine, Bottom = EdgeToMachine} end -- scelta lama local nToolIndex = OptionalParameters.nToolIndex if not nToolIndex then local OptionalParametersGetSingleCutStrategy = { bReduceBladePath = false, nFaceToMachineIndex = i, EdgeToMachineList = EdgeToMachineList, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength } nToolIndex, EdgeToMachine = GetSingleCutStrategy( Proc, Part, OptionalParametersGetSingleCutStrategy) end -- se lama non trovata si provano i cubetti if not nToolIndex then if not ( bDisableDicing) and ( dAngleBetweenFaces >= -91) then Machinings, Result = CutWithDicing( Proc, Part, OptionalParameters) end break end -- calcolo accorciamento local dPathShortening = max( 0, TOOLS[nToolIndex].dThickness / tan( 180 + dAngleBetweenFaces)) -- lavorazione faccia local OptionalParametersFaceByBlade = { dDepthToMachine = EdgeToMachine.dElevation - dPathShortening, nToolIndex = nToolIndex, dExtendAfterTail = OptionalParameters.dExtendAfterTail, bReduceBladePath = false, sRestLengthSideForPreSimulation = sRestLengthSideForPreSimulation, bCannotSplitRestLength = bCannotSplitRestLength } local Cutting = FaceByBlade.Make( Proc, Part, Proc.Faces[i], EdgeToMachine, OptionalParametersFaceByBlade) -- aggiunta lavorazione if Cutting and Cutting.bIsApplicable then table.insert( Machinings, Cutting) end end -- calcolo risultati if #Machinings > 0 then local dCompletionPercentage = 0 if #Machinings == 2 and Machinings[1].dCompletionPercentage > 100 - 10 * GEO.EPS_SMALL and Machinings[2].dCompletionPercentage > 100 - 10 * GEO.EPS_SMALL then Result.sStatus = 'Completed' dCompletionPercentage = 100 else Result.sStatus = 'Not-Completed' dCompletionPercentage = ( ( ( Machinings[1] and Machinings[1].dCompletionPercentage) or 0) + ( ( Machinings[2] and Machinings[2].dCompletionPercentage) or 0)) / 2 end Result.dCompletionPercentage = dCompletionPercentage Result.dCompletionIndex = FeatureLib.GetFeatureCompletionIndex( dCompletionPercentage) Result.dQuality = FeatureLib.GetStrategyQuality( Machinings) Result.dTimeToMachine = FeatureLib.GetStrategyTimeToMachine( Machinings) Result.dMRR = ( FeatureInfo.dFeatureVolume / Result.dTimeToMachine) / pow( 10, 6) else Result = FeatureLib.GetStrategyResultNotApplicable() end end end return Machinings, Result end ------------------------------------------------------------------------------------------------------------- return BLADETOWASTE