Files
databeamnew/StrategyLibs/BLADETOWASTE.lua
T
luca.mazzoleni 5977a4c6db - create BLADEKEEPWASTE per taglio con codolo; STR0005 modificata di conseguenza
- a BLADETOWASTE si passa la dExtendAfterTail opzionale
2025-03-17 17:39:46 +01:00

560 lines
22 KiB
Lua

-- 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 Machinings = {}
local FeatureInfo = {}
-------------------------------------------------------------------------------------------------------------
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 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, CompareEdgesBottomHead)
EdgeToMachine = EdgesSorted[1]
elseif sBladeType == 'Bottom' then
table.sort( EdgesSorted, CompareEdgesTopHead)
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)
else
table.sort( EdgesSorted, CompareEdgesNoPreference)
EdgeToMachine = EdgesSorted[1]
end
return EdgeToMachine
end
local function GetBestBlade( Proc, Part, Face, OptionalParameters)
local nChosenToolIndex
local sChosenBladeType
-- 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
local dElevationTop = OptionalParameters.dElevationTopBlade
local dElevationBottom = OptionalParameters.dElevationBottomBlade
local dElevationTopDownUp = OptionalParameters.dElevationTopBladeDownUp or 0
-- ricerca lama testa sopra
local ToolSearchParameters = {}
ToolSearchParameters.vtN = Face.vtN
ToolSearchParameters.bAllowTopHead = true
ToolSearchParameters.bAllowBottomHead = false
ToolSearchParameters.bForceLongcutBlade = false
ToolSearchParameters.dElevation = dElevationTop
ToolInfo = MachiningLib.FindBlade( Proc, ToolSearchParameters)
local nToolIndexTop
if ToolInfo.dResidualDepth < 10 * GEO.EPS_SMALL then
nToolIndexTop = ToolInfo.nToolIndex
end
-- ricerca lama testa sotto
ToolSearchParameters = {}
ToolSearchParameters.vtN = Face.vtN
ToolSearchParameters.bAllowTopHead = false
ToolSearchParameters.bAllowBottomHead = true
ToolSearchParameters.bForceLongcutBlade = false
ToolSearchParameters.dElevation = dElevationBottom
ToolInfo = MachiningLib.FindBlade( Proc, ToolSearchParameters)
local nToolIndexBottom
if ToolInfo.dResidualDepth < 10 * GEO.EPS_SMALL then
nToolIndexBottom = ToolInfo.nToolIndex
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) / 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) / 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)
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 CutWholeWaste( Proc, Part, OptionalParameters)
local Cutting = {}
local Result = {}
local EdgeToMachine = {}
local nToolIndex
local sChosenBladeType = ''
local dDepthToMachine = 0
local dCompletionPercentage = 0
-- lato da lavorare in base al tipo di lama
local EdgeToMachineTopBlade = GetEdgeToMachine( Proc.Faces[1].Edges, Proc.Faces[1].vtN, 'Top')
local EdgeToMachineBottomBlade = GetEdgeToMachine( Proc.Faces[1].Edges, Proc.Faces[1].vtN, 'Bottom')
local EdgeToMachineTopBladeDownUp = GetEdgeToMachine( Proc.Faces[1].Edges, Proc.Faces[1].vtN, 'TopDownUp')
-- scelta lama da sopra o da sotto
if OptionalParameters.nToolIndex then
nToolIndex = OptionalParameters.nToolIndex
else
local OptionalParametersGetBestBlade = { dElevationTop = EdgeToMachineTopBlade.dElevation + BeamData.CUT_EXTRA,
dElevationBottom = EdgeToMachineBottomBlade.dElevation + BeamData.CUT_EXTRA,
dElevationTopDownUp = EdgeToMachineTopBladeDownUp.dElevation + BeamData.CUT_EXTRA
}
nToolIndex, sChosenBladeType = GetBestBlade( Proc, Part, Proc.Faces[1], OptionalParametersGetBestBlade)
end
-- utensile non trovato
if not nToolIndex then
Result.sStatus = 'Not-Applicable'
Result.dCompletionPercentage = 0
return Machinings, Result
end
-- lato da lavorare definitivo
if sChosenBladeType == 'Top' then
EdgeToMachine = EdgeToMachineTopBlade
elseif sChosenBladeType == 'Bottom' then
EdgeToMachine = EdgeToMachineBottomBlade
elseif sChosenBladeType == 'TopDownUp' then
EdgeToMachine = EdgeToMachineTopBladeDownUp
end
dDepthToMachine = EdgeToMachine.dElevation + BeamData.CUT_EXTRA
local dResidualDepth = dDepthToMachine - TOOLS[nToolIndex].dMaxMaterial
-- TODO qui gestire il caso in cui si può tagliare da due lati (inizialmente solo se vtN:Y è ~= 0?). Andranno ricercati gli utensili di nuovo con l'elevazione a metà??
if dResidualDepth < 10 * GEO.EPS_SMALL then
local OptionalParametersFaceByBlade = { dDepthToMachine = dDepthToMachine, nToolIndex = nToolIndex, dExtendAfterTail = OptionalParameters.dExtendAfterTail}
Cutting = FaceByBlade.Make( Proc, Part, Proc.Faces[1], EdgeToMachine, OptionalParametersFaceByBlade)
end
if Cutting.bIsApplicable 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 = FeatureInfo.dFeatureVolume / ( EdgeToMachine.dLength / TOOLS[nToolIndex].Feeds.dFeed)
Result.dMRR = dMRR / pow( 10, 6)
return Machinings, Result
end
function BLADETOWASTE.Make( ProcOrId, Part, OptionalParameters)
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.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 dMaxWasteVolume = OptionalParameters.dMaxWasteVolume or 0
local dMaxWasteLength = OptionalParameters.dMaxWasteLength or 0
local dExtendAfterTail = OptionalParameters.dExtendAfterTail or 10000
local bSaveAddedGeometries = OptionalParameters.bSaveAddedGeometries
if bSaveAddedGeometries == nil then
bSaveAddedGeometries = true
end
-- dimensioni feature
FeatureInfo.dFeatureVolume = FeatureLib.GetFeatureVolume( Proc, Part)
FeatureInfo.dFeatureMaxDimension = max( Proc.b3Box:getDimX(), Proc.b3Box:getDimY())
FeatureInfo.bIsFeatureSmall = FeatureInfo.dFeatureVolume < dMaxWasteVolume + 10 * GEO.EPS_SMALL
and FeatureInfo.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 FeatureInfo.bIsFeatureSmall then
Machinings, Result = CutWholeWaste( Proc, Part, OptionalParameters)
if Result.sStatus == 'Completed' then
return Machinings, Result
end
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
local sChosenBladeType = ''
if not nToolIndex then
nToolIndex, sChosenBladeType = 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
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
local vCuts = DiceCut.GetDice( Part, Face1, Face2, OptionalParametersDiceCut)
-- 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])
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
for j = 1, #vCuts[i] do
local Cutting = {}
local _, Edges = FaceData.GetEdgesInfo( vCuts[i][1], 0)
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 EdgeToMachine = BeamLib.FindEdgeBestOrientedAsDirection( Edges, vtToolDirectionNew)
local dDepthToMachine = EdgeToMachine.dElevation + BeamData.CUT_EXTRA
local OptionalParametersFaceByBlade = { dDepthToMachine = dDepthToMachine,
nToolIndex = nToolIndex,
bDisableHorizontalSteps = true,
dExtendAfterTail = dExtendAfterTail
}
Cutting = FaceByBlade.Make( ProcTrimesh, Part, FaceToMachine, EdgeToMachine, OptionalParametersFaceByBlade)
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 altrimenti tagli ortogonali invertiti, devo approfondire dello spessore lama
elseif bAreOrthogonalCutsInverted then
dExtraCut = TOOLS[nToolIndex].dThickness
-- se ultimo taglio, devo affondare
elseif j == #vCuts[i] then
dExtraCut = BeamData.CUT_EXTRA
end
end
local ProcTrimesh = FeatureLib.GetProcFromTrimesh( vCuts[i][j], Part)
local EdgeToMachine = BeamLib.FindEdgeBestOrientedAsDirection( Edges, vtToolDirection)
local EdgeToMachineAlternative = {}
if ( i % 2 == 0) and ( Proc.Fct == 1) and bNoPerpCuts then
EdgeToMachineAlternative = BeamLib.FindEdgeBestOrientedAsDirection( Edges, -EdgeToMachine.vtN)
end
local dDepthToMachine = EdgeToMachine.dElevation + dExtraCut
local OptionalParametersFaceByBlade = { dDepthToMachine = dDepthToMachine,
nToolIndex = nToolIndex,
EdgeToMachineAlternative = EdgeToMachineAlternative,
bDisableHorizontalSteps = true,
dExtendAfterTail = dExtendAfterTail
}
Cutting = FaceByBlade.Make( ProcTrimesh, Part, FaceToMachine, EdgeToMachine, OptionalParametersFaceByBlade)
if Cutting.bIsApplicable then
table.insert( Machinings, Cutting)
else
bIsDicingOk = false
end
if Cutting.sStage == 'AfterTail' then
bMoveAfterSplit = true
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
-- TODO contemplare il caso di lavorazione incompleta per i cubetti?????
if bIsDicingOk then
Result.sStatus = 'Completed'
Result.dCompletionPercentage = 100
else
Result.sStatus = 'Not-Applicable'
Result.dCompletionPercentage = 0
end
Result.nQuality = FeatureLib.GetFeatureQuality( 'Blade')
local dMRR = FeatureInfo.dFeatureVolume / ( dDiceDimension / TOOLS[nToolIndex].Feeds.dFeed)
Result.dMRR = dMRR / pow( 10, 6)
-- restituire tabella contenente lavorazioni, già con cloni se necessari
return Machinings, Result
end
-------------------------------------------------------------------------------------------------------------
return BLADETOWASTE