a40cc026c9
- in ANTISPLINTONFACE piccole modifiche
457 lines
18 KiB
Lua
457 lines
18 KiB
Lua
-- BLADEKEEPWASTE.lua by Egalware s.r.l. 2025/03/17
|
|
-- Libreria di supporto a strategie con funzioni comune a strategie diverse.
|
|
|
|
-- Tabella per definizione modulo
|
|
local BLADEKEEPWASTE = {}
|
|
|
|
-- Include
|
|
require( 'EgtBase')
|
|
|
|
-- Carico i dati globali
|
|
local FeatureLib = require( 'FeatureLib')
|
|
local FaceData = require( 'FaceData')
|
|
local MachiningLib = require( 'MachiningLib')
|
|
local BeamLib = require('BeamLib')
|
|
-- strategie di base
|
|
local FaceByBlade = require('FACEBYBLADE')
|
|
local FaceByMill = require( 'FACEBYMILL')
|
|
local AntiSplintOnFace = require( 'ANTISPLINTONFACE')
|
|
|
|
-- tabelle per definizione modulo
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
local function CompareEdgesLongestTop( EdgeA, EdgeB)
|
|
-- si preferiscono i lati più lunghi
|
|
if EdgeA.dLength > EdgeB.dLength + 10 * GEO.EPS_SMALL then
|
|
return true
|
|
elseif EdgeA.dLength < EdgeB.dLength - 10 * GEO.EPS_SMALL then
|
|
return false
|
|
-- se stessa lunghezza si preferiscono i lati più in basso
|
|
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 GetLongEdgeToMachine( Face, bHeadType)
|
|
local Edge = {}
|
|
|
|
local EdgesSorted = {}
|
|
for i = 1, #Face.Edges do
|
|
table.insert( EdgesSorted, Face.Edges[i])
|
|
end
|
|
table.sort( EdgesSorted, CompareEdgesLongestTop)
|
|
|
|
-- se il lato migliore è accessibile si sceglie questo, altrimenti il lato opposto; se entrambi non accessibili (faccia chiusa da due lati) la lavorazione non è applicabile
|
|
Edge = EdgesSorted[1]
|
|
local EdgeOpposite = BeamLib.FindEdgeBestOrientedAsDirection( Face.Edges, -Edge.vtN)
|
|
if not EdgeOpposite.bIsOpen then
|
|
if Edge.bIsOpen then
|
|
Edge = EdgeOpposite
|
|
-- entrambi i lati non accessibili: codolo non applicabile
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
return Edge
|
|
end
|
|
|
|
|
|
local function SortMachiningsBySegment( MachiningA, MachiningB)
|
|
if MachiningA.nFeatureSegment > MachiningB.nFeatureSegment then
|
|
return false
|
|
elseif MachiningB.nFeatureSegment > MachiningA.nFeatureSegment then
|
|
return true
|
|
-- se segmento uguale, si guarda la priorità
|
|
else
|
|
if MachiningA.nInternalSortingPriority > MachiningB.nInternalSortingPriority then
|
|
return false
|
|
elseif MachiningB.nInternalSortingPriority > MachiningA.nInternalSortingPriority then
|
|
return true
|
|
-- se priorità uguale, si minimizzano i cambi di lato
|
|
else
|
|
local bIsOddSegment = ( MachiningA.nFeatureSegment % 2 ~= 0)
|
|
if MachiningA.vtToolDirection:getY() < MachiningB.vtToolDirection:getY() - 10 * GEO.EPS_SMALL then
|
|
if bIsOddSegment then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
elseif MachiningA.vtToolDirection:getY() > MachiningB.vtToolDirection:getY() + 10 * GEO.EPS_SMALL then
|
|
if bIsOddSegment then
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
local function GetStrategyCompletionPercentage( Machinings)
|
|
local dCompletionPercentage = 0
|
|
|
|
local dCompletionPercentageNumerator = 0
|
|
local dCompletionPercentageDenominator = 0
|
|
local nWeightsCount = 0
|
|
for i = 1, #Machinings do
|
|
local Machining = Machinings[i]
|
|
local dWeight = Machining.dResultWeight
|
|
if not dWeight or ( dWeight < 10 * GEO.EPS_SMALL) then
|
|
dWeight = 1
|
|
else
|
|
nWeightsCount = nWeightsCount + 1
|
|
end
|
|
-- il peso deve essere settato in tutte le lavorazioni o in nessuna
|
|
if nWeightsCount ~= 0 and nWeightsCount ~= i then
|
|
error( 'GetWeightedCompletionPercentage : inconsistent weights')
|
|
end
|
|
local dWeightedCompletionPercentage = ( Machining.dCompletionPercentage or 0) / 100 * dWeight
|
|
if Machining.bIsApplicable then
|
|
dCompletionPercentageNumerator = dCompletionPercentageNumerator + dWeightedCompletionPercentage
|
|
end
|
|
dCompletionPercentageDenominator = dCompletionPercentageDenominator + dWeight
|
|
end
|
|
|
|
dCompletionPercentage = min( 100 * dCompletionPercentageNumerator / dCompletionPercentageDenominator, 100)
|
|
|
|
return dCompletionPercentage
|
|
end
|
|
|
|
|
|
local function MakeBottomFace( Proc, Part, BottomFace, EdgeToMachine, Parameters)
|
|
local Cuttings = {}
|
|
local Cutting1 = {}
|
|
local Cutting2 = {}
|
|
|
|
-- parametri dal chiamante
|
|
local bIsSplitFeature = Parameters.bIsSplitFeature
|
|
local dExtendAfterTail = Parameters.dExtendAfterTail
|
|
local nToolIndex = Parameters.nToolIndex
|
|
local dStripWidth = Parameters.dStripWidth
|
|
local OtherBottomFace = Parameters.OtherBottomFace
|
|
|
|
local dDepthToMachine = EdgeToMachine.dElevation / 2 - dStripWidth / 2
|
|
local OptionalParametersFaceByBlade1 = { dDepthToMachine = dDepthToMachine, bIsSplitFeature = bIsSplitFeature, dExtendAfterTail = dExtendAfterTail, nToolIndex = nToolIndex}
|
|
local EdgeToMachineOpposite = BeamLib.FindEdgeBestOrientedAsDirection( BottomFace.Edges, -EdgeToMachine.vtN)
|
|
|
|
-- primo lato
|
|
if EdgeToMachineOpposite.bIsOpen then
|
|
Cutting1 = FaceByBlade.Make( Proc, Part, BottomFace, EdgeToMachine, OptionalParametersFaceByBlade1)
|
|
end
|
|
Cutting1.nInternalSortingPriority = 2
|
|
Cutting1.dResultWeight = 0.3
|
|
|
|
-- secondo lato
|
|
local OptionalParametersFaceByBlade2 = BeamLib.TableCopyDeep( OptionalParametersFaceByBlade1)
|
|
OptionalParametersFaceByBlade2.OppositeToolDirectionMode = 'Enabled'
|
|
if EdgeToMachine.bIsOpen then
|
|
Cutting2 = FaceByBlade.Make( Proc, Part, BottomFace, EdgeToMachine, OptionalParametersFaceByBlade2)
|
|
end
|
|
Cutting2.nInternalSortingPriority = 2
|
|
Cutting2.dResultWeight = 0.3
|
|
|
|
-- se uno dei due lati non è riuscito, si estende il più possibile il lato rimasto
|
|
if not Cutting1.bIsApplicable and Cutting2.bIsApplicable then
|
|
|
|
-- se si lavora il lato in comune con l'altra BottomFace significa ci si deve fermare piú indietro
|
|
if OtherBottomFace and ( EdgeToMachine.idAdjacentFace == OtherBottomFace.id) then
|
|
dStripWidth = TOOLS[Cutting2.nToolIndex].dThickness + 2 * dStripWidth
|
|
end
|
|
dDepthToMachine = min( TOOLS[Cutting2.nToolIndex].dMaxMaterial, EdgeToMachine.dElevation - dStripWidth)
|
|
OptionalParametersFaceByBlade2.dDepthToMachine = dDepthToMachine
|
|
|
|
Cutting2 = FaceByBlade.Make( Proc, Part, BottomFace, EdgeToMachine, OptionalParametersFaceByBlade2)
|
|
Cutting2.nInternalSortingPriority = 2
|
|
Cutting2.dResultWeight = 0.3
|
|
table.insert( Cuttings, Cutting2)
|
|
|
|
elseif not Cutting2.bIsApplicable and Cutting1.bIsApplicable then
|
|
|
|
-- se si lavora il lato in comune con l'altra BottomFace significa ci si deve fermare piú indietro
|
|
if OtherBottomFace and ( EdgeToMachine.idAdjacentFace == OtherBottomFace.id) then
|
|
dStripWidth = TOOLS[Cutting1.nToolIndex].dThickness + 2 * dStripWidth
|
|
end
|
|
dDepthToMachine = min( TOOLS[Cutting1.nToolIndex].dMaxMaterial, EdgeToMachine.dElevation - dStripWidth)
|
|
OptionalParametersFaceByBlade1.dDepthToMachine = dDepthToMachine
|
|
|
|
Cutting1 = FaceByBlade.Make( Proc, Part, BottomFace, EdgeToMachine, OptionalParametersFaceByBlade1)
|
|
Cutting1.nInternalSortingPriority = 2
|
|
Cutting1.dResultWeight = 0.3
|
|
table.insert( Cuttings, Cutting1)
|
|
|
|
else
|
|
table.insert( Cuttings, Cutting1)
|
|
table.insert( Cuttings, Cutting2)
|
|
end
|
|
|
|
return Cuttings
|
|
end
|
|
|
|
|
|
function BLADEKEEPWASTE.Make( Proc, Part, OptionalParameters)
|
|
-- TODO verificare funzionamento con lama da sotto
|
|
-- TODO scelta utensile è corretto lasciarla a FaceByBlade?
|
|
-- TODO aggiungere accorciamento se angolo < 90
|
|
|
|
local Result = {}
|
|
local Machinings = {}
|
|
local CalculatedMachinings = {}
|
|
|
|
-- controlli preventivi
|
|
if Proc.nFct > 3 and ( not Proc.Topology.sFamily == 'DoubleBevel') then
|
|
Result = FeatureLib.GetStrategyResultNotApplicable( 'BladeKeepWaste : max 3 faces supported')
|
|
return Machinings, Result
|
|
elseif Proc.nFct == 2 then
|
|
-- per angolo tra le facce >= 90deg (feature convessa) non applicabile
|
|
if Proc.AdjacencyMatrix[1][2] > 10 * GEO.EPS_SMALL or Proc.AdjacencyMatrix[1][2] < -91 then
|
|
Result = FeatureLib.GetStrategyResultNotApplicable( 'BladeKeepWaste : angle between faces must be concave and >= 90deg')
|
|
return Machinings, Result
|
|
end
|
|
elseif Proc.nFct == 3 then
|
|
-- caso speciale RidgeLap - per angolo tra le facce >= 90deg (feature convessa) non applicabile
|
|
if Proc.AdjacencyMatrix[1][2] > 10 * GEO.EPS_SMALL or Proc.AdjacencyMatrix[1][2] < -91 then
|
|
Result = FeatureLib.GetStrategyResultNotApplicable( 'BladeKeepWaste : angle between faces must be concave and >= 90deg')
|
|
return Machinings, Result
|
|
end
|
|
if Proc.AdjacencyMatrix[1][3] > 10 * GEO.EPS_SMALL or Proc.AdjacencyMatrix[1][3] < -91 then
|
|
Result = FeatureLib.GetStrategyResultNotApplicable( 'BladeKeepWaste : angle between faces must be concave and >= 90deg')
|
|
return Machinings, Result
|
|
end
|
|
if Proc.AdjacencyMatrix[2][3] > 10 * GEO.EPS_SMALL or Proc.AdjacencyMatrix[2][3] < -91 then
|
|
Result = FeatureLib.GetStrategyResultNotApplicable( 'BladeKeepWaste : angle between faces must be concave and >= 90deg')
|
|
return Machinings, Result
|
|
end
|
|
end
|
|
|
|
-- parametri opzionali e default
|
|
if not OptionalParameters then
|
|
OptionalParameters = {}
|
|
end
|
|
local nToolIndex = OptionalParameters.nToolIndex
|
|
local dExtendAfterTail = OptionalParameters.dExtendAfterTail or 10000
|
|
local bFinishWithMill = ( OptionalParameters.bFinishWithMill ~= false)
|
|
local dMillingOffsetFromSide = OptionalParameters.dMillingOffsetFromSide or 1
|
|
local dStripWidth = OptionalParameters.dStripWidth or 5
|
|
local bForced = OptionalParameters.bForced or false
|
|
|
|
-- volume della feature
|
|
local dFeatureVolume = Proc.dVolume
|
|
|
|
-- si trovano le facce da lavorare (solo 4 lati esatti)
|
|
local BottomFace1
|
|
local BottomFace2
|
|
if Proc.nFct == 1 then
|
|
BottomFace1 = Proc.Faces[1]
|
|
else
|
|
if not Proc.MainFaces then
|
|
Proc.MainFaces = FaceData.GetMainFaces( Proc, Part)
|
|
end
|
|
BottomFace1 = Proc.MainFaces.BottomFaces[1]
|
|
BottomFace2 = Proc.MainFaces.BottomFaces[2]
|
|
end
|
|
if #BottomFace1.Edges ~= 4 then
|
|
Result = FeatureLib.GetStrategyResultNotApplicable()
|
|
return Machinings, Result
|
|
end
|
|
local bConvexAngle
|
|
if BottomFace2 then
|
|
bConvexAngle = ( Proc.AdjacencyMatrix[BottomFace1.id + 1][BottomFace2.id + 1]) > 0
|
|
end
|
|
|
|
-- eventuali punti di spezzatura
|
|
local FeatureSplittingPoints = FeatureLib.GetFeatureSplittingPoints( Proc, Part)
|
|
local bIsSplitFeature = false
|
|
if #FeatureSplittingPoints > 0 then
|
|
bIsSplitFeature = true
|
|
end
|
|
|
|
-- calcolo lavorazioni faccia principale
|
|
-- ricerca lato da lavorare
|
|
local BottomEdgeToMachine1 = GetLongEdgeToMachine( BottomFace1, { bTop = true})
|
|
if not BottomEdgeToMachine1 then
|
|
Result = FeatureLib.GetStrategyResultNotApplicable()
|
|
return Machinings, Result
|
|
end
|
|
-- calcolo lavorazione
|
|
local Parameters1 = {
|
|
bIsSplitFeature = bIsSplitFeature,
|
|
dExtendAfterTail = dExtendAfterTail,
|
|
nToolIndex = nToolIndex,
|
|
dStripWidth = dStripWidth,
|
|
OtherBottomFace = BottomFace2
|
|
}
|
|
local Cuttings1 = MakeBottomFace( Proc, Part, BottomFace1, BottomEdgeToMachine1, Parameters1)
|
|
-- aggiunta lavorazioni alla lista principale
|
|
for i = 1, #Cuttings1 do
|
|
table.insert( CalculatedMachinings, Cuttings1[i])
|
|
end
|
|
|
|
-- calcolo lavorazioni faccia secondaria, solo se lato convesso; se concavo, sarà lavorato come antisplint
|
|
local Cuttings2
|
|
local BottomEdgeToMachine2
|
|
if BottomFace2 then
|
|
if bConvexAngle then
|
|
-- ricerca lato da lavorare
|
|
BottomEdgeToMachine2 = GetLongEdgeToMachine( BottomFace2, { bTop = true})
|
|
if BottomEdgeToMachine2 then
|
|
-- calcolo lavorazione
|
|
local Parameters2 = BeamLib.TableCopyDeep( Parameters1)
|
|
Parameters2.OtherBottomFace = BottomFace1
|
|
Cuttings2 = MakeBottomFace( Proc, Part, BottomFace2, BottomEdgeToMachine2, Parameters2)
|
|
for i = 1, #Cuttings2 do
|
|
table.insert( CalculatedMachinings, Cuttings2[i])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- antischeggia sulle facce di chiusura delle facce lavorate
|
|
local OptionalParametersAntiSplint = {
|
|
bIsSplitFeature = bIsSplitFeature,
|
|
dExtendAfterTail = dExtendAfterTail,
|
|
nInternalSortingPriority = 1,
|
|
dResultWeight = 0.15,
|
|
bMachineAllClosedEdges = true
|
|
}
|
|
local AntiSplints1 = AntiSplintOnFace.Make( Proc, Part, BottomFace1, OptionalParametersAntiSplint)
|
|
for i = 1, #AntiSplints1 do
|
|
table.insert( CalculatedMachinings, AntiSplints1[i])
|
|
end
|
|
if BottomFace2 and bConvexAngle then
|
|
OptionalParametersAntiSplint.bMachineAllClosedEdges = false
|
|
local AntiSplints2 = AntiSplintOnFace.Make( Proc, Part, BottomFace2, OptionalParametersAntiSplint)
|
|
for i = 1, #AntiSplints2 do
|
|
table.insert( CalculatedMachinings, AntiSplints2[i])
|
|
end
|
|
end
|
|
|
|
-- pulitura con fresa dei lati chiusi non lavorati
|
|
-- TODO funzione
|
|
if bFinishWithMill then
|
|
|
|
if Cuttings1 then
|
|
-- si recuperano i lati chiusi non lavorati
|
|
local EdgesClosedNotMachined = {}
|
|
for i = 1, #BottomFace1.Edges do
|
|
if not( ( BottomFace1.Edges[i].id == BottomEdgeToMachine1.id) or BottomFace1.Edges[i].bIsOpen) then
|
|
table.insert( EdgesClosedNotMachined, BottomFace1.Edges[i])
|
|
end
|
|
end
|
|
-- su ognuno si fa la fresatura di pulizia
|
|
for i = 1, #EdgesClosedNotMachined do
|
|
local dDepthToMachine = EdgesClosedNotMachined[i].dElevation - dMillingOffsetFromSide
|
|
local dToolMarkLength = 0
|
|
-- si prende l'impronta dell'utensile più grande
|
|
for j = 1, #Cuttings1 do
|
|
if Cuttings1[j].dToolMarkLength > dToolMarkLength + 10 * GEO.EPS_SMALL then
|
|
dToolMarkLength = Cuttings1[j].dToolMarkLength
|
|
end
|
|
end
|
|
local OptionalParametersMilling = {
|
|
bIsSplitFeature = bIsSplitFeature,
|
|
dExtendAfterTail = dExtendAfterTail,
|
|
dRadialStepSpan = dToolMarkLength,
|
|
dDepthToMachine = dDepthToMachine
|
|
}
|
|
local Milling = FaceByMill.Make( Proc, Part, BottomFace1, EdgesClosedNotMachined[i], OptionalParametersMilling)
|
|
Milling.nInternalSortingPriority = 3
|
|
Milling.dResultWeight = 0.05
|
|
table.insert( CalculatedMachinings, Milling)
|
|
end
|
|
end
|
|
if Cuttings2 and BottomEdgeToMachine2 then
|
|
-- si recuperano i lati chiusi non lavorati
|
|
local EdgesClosedNotMachined = {}
|
|
for i = 1, #BottomFace2.Edges do
|
|
if not( ( BottomFace2.Edges[i].id == BottomEdgeToMachine2.id) or BottomFace2.Edges[i].bIsOpen) then
|
|
table.insert( EdgesClosedNotMachined, BottomFace2.Edges[i])
|
|
end
|
|
end
|
|
-- su ognuno si fa la fresatura di pulizia
|
|
for i = 1, #EdgesClosedNotMachined do
|
|
local dDepthToMachine = EdgesClosedNotMachined[i].dElevation - dMillingOffsetFromSide
|
|
local dToolMarkLength = 0
|
|
-- si prende l'impronta dell'utensile più grande
|
|
for j = 1, #Cuttings2 do
|
|
if Cuttings2[j].dToolMarkLength > dToolMarkLength + 10 * GEO.EPS_SMALL then
|
|
dToolMarkLength = Cuttings2[j].dToolMarkLength
|
|
end
|
|
end
|
|
local OptionalParametersMilling = {
|
|
bIsSplitFeature = bIsSplitFeature,
|
|
dExtendAfterTail = dExtendAfterTail,
|
|
dRadialStepSpan = dToolMarkLength,
|
|
dDepthToMachine = dDepthToMachine
|
|
}
|
|
local Milling = FaceByMill.Make( Proc, Part, BottomFace2, EdgesClosedNotMachined[i], OptionalParametersMilling)
|
|
Milling.nInternalSortingPriority = 3
|
|
Milling.dResultWeight = 0.05
|
|
table.insert( CalculatedMachinings, Milling)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- lavorazioni da applicare spostate in lista finale
|
|
for i = 1, #CalculatedMachinings do
|
|
if CalculatedMachinings[i].bIsApplicable then
|
|
table.insert( Machinings, CalculatedMachinings[i])
|
|
end
|
|
end
|
|
|
|
-- calcolo completamento (non applicabili devono essere incluse)
|
|
-- se codolo non forzato si ritorna completa max al 94% (completion index 4)
|
|
local dCalculatedCompletionPercentage = GetStrategyCompletionPercentage( CalculatedMachinings)
|
|
Result.dCompletionPercentage = bForced and dCalculatedCompletionPercentage or min( 94, dCalculatedCompletionPercentage)
|
|
Result.dCompletionIndex = FeatureLib.GetFeatureCompletionIndex( Result.dCompletionPercentage)
|
|
|
|
-- aggiunta eventuali lavorazioni splittate
|
|
if bIsSplitFeature then
|
|
Machinings = MachiningLib.GetSplitMachinings( Machinings, FeatureSplittingPoints, Part)
|
|
end
|
|
|
|
-- ordinamento
|
|
table.sort( Machinings, SortMachiningsBySegment)
|
|
|
|
-- calcolo risultati
|
|
if ( Cuttings1 and ( ( Cuttings1[1] and Cuttings1[1].bIsApplicable) or ( Cuttings1[2] and Cuttings1[2].bIsApplicable)))
|
|
or ( Cuttings2 and ( ( Cuttings2[1] and Cuttings2[1].bIsApplicable) or ( Cuttings2[2] and Cuttings2[2].bIsApplicable))) then
|
|
|
|
Result.dQuality = FeatureLib.GetStrategyQuality( Machinings)
|
|
Result.dTimeToMachine = FeatureLib.GetStrategyTimeToMachine( Machinings)
|
|
Result.dMRR = ( dFeatureVolume / Result.dTimeToMachine) / pow( 10, 6)
|
|
if Result.dCompletionPercentage > 100 - 10 * GEO.EPS_SMALL then
|
|
Result.sStatus = 'Completed'
|
|
if bForced then
|
|
Result.sInfo = 'Waste attached. Remove manually.'
|
|
end
|
|
else
|
|
Result.sStatus = 'Not-Completed'
|
|
end
|
|
else
|
|
Result = FeatureLib.GetStrategyResultNotApplicable()
|
|
end
|
|
|
|
return Machinings, Result
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------
|
|
|
|
return BLADEKEEPWASTE |