- in NestProcess loop completo, alcune migliorie possibili per prestazioni; manca la parte che effettivamente crea i MachGroup
This commit is contained in:
+235
-6
@@ -7,6 +7,33 @@ _ENV = EgtProtectGlobal()
|
||||
EgtEnableDebug( true)
|
||||
|
||||
-- Include
|
||||
local BeamData = require( 'BeamDataNew')
|
||||
|
||||
|
||||
----------------------------------------------------------------------------------------------------------
|
||||
-- Parametri di configurazione Nesting
|
||||
local CONFIG = {
|
||||
-- Strategia di ricerca
|
||||
NUM_LONGEST_CANDIDATES = 3,
|
||||
|
||||
-- Punteggi
|
||||
BONUS_PERFECT_FIT = 500, -- Se lo scarto è quasi zero (es: < 5mm)
|
||||
BONUS_SHARED_CUT = 100, -- Se le inclinazioni combaciano perfettamente
|
||||
PENALTY_TOO_SHORT = 1000, -- Se lo scarto è troppo piccolo per essere utile ma non zero
|
||||
PENALTY_BAD_FIT_END_PHASE = 1000, -- Se siamo alla fine (>80%) e si utilizza un pezzo corto con fit mediocre
|
||||
|
||||
-- Macchina
|
||||
MIN_USABLE_REMNANT = BeamData.MINRAW_S, -- Sotto questa misura lo scarto è considerato "sfrido"
|
||||
MAX_REMNANT_PERFECT_FIT = 20,
|
||||
BLADE_THICKNESS = 5.4 -- TODO questo deve arrivare da interfaccia o da automatismo!!!!!
|
||||
}
|
||||
|
||||
----------------------------------------------------------------------------------------------------------
|
||||
-- variabili per tutto il modulo
|
||||
local dMaxJobLength = 0
|
||||
local dMaxFillerLength = 0
|
||||
local dGlobalProgress = 0
|
||||
local t_insert = table.insert -- si mette via il riferimento locale per evitare continui lookup
|
||||
|
||||
----------------------------------------------------------------------------------------------------------
|
||||
-- inventario grezzi
|
||||
@@ -18,9 +45,10 @@ local RawInventory = {
|
||||
function RawInventory:GetNewBeam( dLength)
|
||||
local NewBeam = {
|
||||
dTotalLength = dLength,
|
||||
dResidual = dLength - NEST.STARTOFFSET,
|
||||
dResidualLength = dLength - ( NEST.STARTOFFSET or 0),
|
||||
LastOffsetX = { 0, 0, 0, 0},
|
||||
LastVtN = Vector3d( 1, 0, 0),
|
||||
vtNXabs = 1,
|
||||
dLastHeadRecess = 0,
|
||||
NestedParts = {}
|
||||
}
|
||||
@@ -55,7 +83,7 @@ function RawInventory:AddActiveBeam( nStockIndex)
|
||||
-- aggiungo una nuova barra attiva
|
||||
local NewBeam = self:GetNewBeam( CurrentStock.dLength)
|
||||
|
||||
table.insert( self.ActiveBeams, NewBeam)
|
||||
t_insert( self.ActiveBeams, NewBeam)
|
||||
return NewBeam
|
||||
end
|
||||
|
||||
@@ -94,6 +122,11 @@ function PartTemplates:AddPart( id)
|
||||
self[id].States = {}
|
||||
self[id].dMaxGlobalTailRecess = 0
|
||||
|
||||
-- si tiene via la lunghezza del pezzo massimo
|
||||
if self[id].dLength > dMaxJobLength + 10 * GEO.EPS_SMALL then
|
||||
dMaxJobLength = self[id].dLength
|
||||
end
|
||||
|
||||
local States = { '1000', '0010', '1000_INV', '0010_INV' }
|
||||
|
||||
for _, sState in ipairs(States) do
|
||||
@@ -116,11 +149,13 @@ function PartTemplates:AddPart( id)
|
||||
|
||||
State.Head = {
|
||||
OffsetX = OffsetXHead,
|
||||
vtN = vtNHead
|
||||
vtN = vtNHead,
|
||||
vtNXabs = abs( vtN:getX())
|
||||
}
|
||||
State.Tail = {
|
||||
OffsetX = OffsetXTail,
|
||||
vtN = vtNTail
|
||||
vtN = vtNTail,
|
||||
vtNXabs = abs( vtN:getX())
|
||||
}
|
||||
|
||||
-- overlap massimi in testa e in coda per questo pezzo
|
||||
@@ -170,7 +205,7 @@ local function BuildPartTemplatesAndJobPool()
|
||||
for id, nCount in pairs( PART) do
|
||||
PartTemplates:AddPart( id)
|
||||
for _ = 1, nCount do
|
||||
table.insert( JobPool, { id = id, bNested = false})
|
||||
t_insert( JobPool, { id = id, bNested = false})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -178,10 +213,185 @@ local function BuildPartTemplatesAndJobPool()
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------------------------------------
|
||||
-- calcolo singola Move, ossia combinazione barra-pezzo-stato, con relativo punteggio
|
||||
local function CalculateMove( Beam, dPartLength, sState, State)
|
||||
local Move = {}
|
||||
|
||||
-- calcolo overlap pezzi (riduzione di lunghezza occupata)
|
||||
local dSafeOverlap = GEO.INFINITO
|
||||
for i = 1, 4 do
|
||||
local dCornerDistance = Beam.LastOffsetX[i] - State.Tail.OffsetX[i]
|
||||
if dCornerDistance < dSafeOverlap then
|
||||
dSafeOverlap = dCornerDistance
|
||||
end
|
||||
end
|
||||
|
||||
-- il massimo overlap va ridotto dello spessore lama
|
||||
local dProjectedBlade = CONFIG.BLADE_THICKNESS / min( Beam.vtNXabs, State.Tail.vtNXabs)
|
||||
dSafeOverlap = dSafeOverlap - dProjectedBlade
|
||||
|
||||
-- lunghezza barra rimasta (se negativo non ci sta)
|
||||
local dFutureResidualLength = Beam.dResidualLength + dSafeOverlap - dPartLength
|
||||
if dFutureResidualLength < -10 * GEO.EPS_SMALL then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Calcolo punteggio
|
||||
-- All'inizio (Ratio=1) si dà vantaggio ai pezzi più lunghi. Alla fine (Ratio=0) si dà vantaggio ai best fit.
|
||||
local dNormalizedLength = dPartLength / dMaxJobLength
|
||||
local dEfficiency = dPartLength / ( dPartLength + dFutureResidualLength)
|
||||
local dLengthScore = dNormalizedLength * ( 1 - GlobalProgress) * 1000
|
||||
local dEfficiencyScore = dEfficiency * GlobalProgress * 1000
|
||||
|
||||
local dScore = dLengthScore + dEfficiencyScore
|
||||
|
||||
-- Bonus Perfect Fit
|
||||
if dFutureResidualLength < CONFIG.MAX_REMNANT_PERFECT_FIT then
|
||||
dScore = dScore + CONFIG.BONUS_PERFECT_FIT
|
||||
|
||||
-- Penalità Sliver (si evitano sfridi non riutilizzabili)
|
||||
elseif dFutureResidualLength < CONFIG.MIN_USABLE_REMNANT then
|
||||
dScore = dScore - CONFIG.PENALTY_TOO_SHORT
|
||||
end
|
||||
|
||||
-- Bonus Shared Cut: se le normali sono opposte, si risparmia un taglio/posizionamento
|
||||
if AreOppositeVectorApprox( Beam.LastVtN, State.Tail.vtN) then
|
||||
dScore = dScore + CONFIG.BONUS_SHARED_CUT
|
||||
end
|
||||
|
||||
-- Protezione finale pezzi corti: se siamo alla fine e il pezzo è corto, penalizziamo se non è un fit quasi perfetto
|
||||
if ( dGlobalProgress >= 0.8) and ( dPartLength < dMaxFillerLength) and ( dFutureResidualLength > CONFIG.MAX_REMNANT_PERFECT_FIT) then
|
||||
dScore = dScore - CONFIG.PENALTY_BAD_FIT_END_PHASE
|
||||
end
|
||||
|
||||
Move = {
|
||||
sState = sState,
|
||||
dScore = dScore,
|
||||
dSafeOverlap = dSafeOverlap,
|
||||
dFutureResidualLength = dFutureResidualLength
|
||||
}
|
||||
return Move
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------------------------------------
|
||||
-- trova i migliori pezzi da inserire nella trave (N pezzi più lunghi e 2 pezzi di lunghezza più adeguata al restante della barra) e i migliori stati in cui metterli
|
||||
local function FindBestPartForBeam( Beam)
|
||||
local nLongestParts = 0
|
||||
local Candidates = {}
|
||||
|
||||
local BestFitJob1 = { Job = nil, dGap = GEO.INFINITO }
|
||||
local BestFitJob2 = { Job = nil, dGap = GEO.INFINITO }
|
||||
|
||||
-- 1 Scelta candidati --
|
||||
for i = 1, #JobPool do
|
||||
local Job = JobPool[i]
|
||||
if not Job.bNested then
|
||||
local PartTemplate = PartTemplates[Job.id]
|
||||
|
||||
local dGap = Beam.dResidualLength + Beam.dLastHeadRecess + PartTemplate.dMaxGlobalTailRecess - PartTemplate.dLength
|
||||
|
||||
if dGap > - 10 * GEO.EPS_SMALL then
|
||||
|
||||
-- JobPool è già ordinata: i primi N pezzi che entrano sono i più lunghi
|
||||
if nLongestParts < CONFIG.NUM_LONGEST_CANDIDATES then
|
||||
t_insert( Candidates, Job)
|
||||
nLongestParts = nLongestParts + 1
|
||||
end
|
||||
|
||||
-- si cercano i due pezzi con il best fit nella barra restante
|
||||
if dGap < BestFitJob1.dGap then
|
||||
-- il bestfit1 è il nuovo bestfit 2
|
||||
BestFitJob2.Job = BestFitJob1.Job
|
||||
BestFitJob2.dGap = BestFitJob1.dGap
|
||||
-- la job corrente è la nuova bestfit1
|
||||
BestFitJob1.dGap = dGap
|
||||
BestFitJob1.Job = Job
|
||||
elseif dGap < BestFitJob2.dGap then
|
||||
BestFitJob2.Job = Job
|
||||
BestFitJob2.dGap = dGap
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- i pezzi bestfit si aggiungono solo se non corrispondono a pezzi già inseriti
|
||||
local function AddIfUnique( JobToAdd)
|
||||
if not JobToAdd then return end
|
||||
for i = 1, #Candidates do
|
||||
if Candidates[i] == JobToAdd then return end
|
||||
end
|
||||
t_insert( Candidates, JobToAdd)
|
||||
end
|
||||
AddIfUnique( BestFitJob1.Job)
|
||||
AddIfUnique( BestFitJob2.Job)
|
||||
|
||||
-- 2 Scelta miglior candidato --
|
||||
local dHighestScore = -GEO.INFINITO
|
||||
local BestMove
|
||||
for i = 1, #Candidates do
|
||||
local Candidate = Candidates[i]
|
||||
local Template = PartTemplates[Candidate.id]
|
||||
local dPartLength = Template.dLength
|
||||
local dHighestCandidateScore = -GEO.INFINITO
|
||||
local BestCandidateMove
|
||||
|
||||
-- si trova la Move migliore del singolo candidato (a CalculateMove si passano gli argomenti precalcolati per evitare di rallentare il calcolo)
|
||||
for sState, State in pairs( Template.States) do
|
||||
local Move = CalculateMove( Beam, dPartLength, sState, State)
|
||||
if Move and Move.dScore > dHighestCandidateScore + 10 * GEO.EPS_SMALL then
|
||||
BestCandidateMove = Move
|
||||
dHighestCandidateScore = Move.dScore
|
||||
BestCandidateMove.Job = Candidate
|
||||
end
|
||||
end
|
||||
|
||||
-- si trova la Move migliore in assoluto
|
||||
if dHighestCandidateScore > dHighestScore + 10 * GEO.EPS_SMALL then
|
||||
BestMove = BestCandidateMove
|
||||
dHighestScore = dHighestCandidateScore
|
||||
end
|
||||
end
|
||||
|
||||
return BestMove
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------------------------------------
|
||||
-- Esegue la mossa scelta: aggiorna lo stato della trave e segna il pezzo come nestato
|
||||
local function CommitBestMove( BestMove)
|
||||
local Beam
|
||||
|
||||
-- recupero o creazione della barra
|
||||
if BestMove.nActiveBeamIndex then
|
||||
Beam = RawInventory.ActiveBeams[BestMove.nActiveBeamIndex]
|
||||
elseif BestMove.nStockIndex then
|
||||
Beam = RawInventory:AddActiveBeam(BestMove.nStockIndex)
|
||||
end
|
||||
|
||||
if not Beam then return end
|
||||
|
||||
-- recupero dati pezzo e stato
|
||||
local Job = BestMove.Job
|
||||
local Template = PartTemplates[Job.id]
|
||||
local State = Template.States[BestMove.sState]
|
||||
|
||||
-- update geometria barra
|
||||
-- la nuova faccia della barra è ora la testa (Head) del pezzo appena inserito
|
||||
Beam.dResidualLength = BestMove.dFutureResidualLength
|
||||
Beam.LastOffsetX = State.Head.OffsetX
|
||||
Beam.LastVtN = State.Head.vtN
|
||||
Beam.vtNXabs = abs( State.Head.vtN:getX())
|
||||
Beam.dLastHeadRecess = State.dMaxHeadRecess
|
||||
|
||||
-- registrazione pezzo
|
||||
t_insert( Beam.NestedParts, {
|
||||
id = Job.id,
|
||||
sState = BestMove.sState,
|
||||
dSafeOverlap = BestMove.dSafeOverlap,
|
||||
dLength = Template.dLength
|
||||
})
|
||||
|
||||
-- chiusura job
|
||||
Job.bNested = true
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------------------------------------
|
||||
@@ -192,12 +402,29 @@ RawInventory:BuildStock()
|
||||
BuildPartTemplatesAndJobPool()
|
||||
JobPool:Sort()
|
||||
|
||||
-- calcolo lunghezza massima pezzi "filler"
|
||||
local nTotalParts = #JobPool
|
||||
local nFillerIndex = floor( nTotalParts * 0.8) + 1
|
||||
if nFillerIndex > nTotalParts then nFillerIndex = nTotalParts end
|
||||
dMaxFillerLength = PartTemplates[JobPool[nFillerIndex].id].dLength
|
||||
dMaxFillerLength = EgtClamp( dMaxFillerLength, ( BeamData.MINRAW_S + BeamData.MINRAW_L) / 2, BeamData.LEN_SHORT_PART)
|
||||
|
||||
-- loop principale: scorre le barre, sia già attive che a stock, e le riempie nel modo migliore possibile
|
||||
-- per ogni giro sceglie la soluzione con punteggio più alto
|
||||
local nDoneParts = 0
|
||||
local VirtualBeam = RawInventory:GetNewBeam( 0)
|
||||
while true do
|
||||
local BestMove
|
||||
local dHighestScore = -GEO.INFINITO
|
||||
|
||||
-- progresso calcolo
|
||||
if nTotalParts > 0 then
|
||||
dGlobalProgress = nDoneParts / nTotalParts
|
||||
-- se non ci sono pezzi il calcolo è già finito
|
||||
else
|
||||
dGlobalProgress = 1
|
||||
end
|
||||
|
||||
-- 1 Si provano le barre già attive
|
||||
for i = 1, #RawInventory.ActiveBeams do
|
||||
local CurrentMove = FindBestPartForBeam( RawInventory.ActiveBeams[i])
|
||||
@@ -211,7 +438,8 @@ while true do
|
||||
-- 2 Si provano le barre ancora a stock
|
||||
for i = 1, #RawInventory.Stock do
|
||||
if RawInventory.Stock[i].nCount > 0 then
|
||||
local VirtualBeam = RawInventory:GetNewBeam( RawInventory.Stock[i].dLength)
|
||||
VirtualBeam.dTotalLength = RawInventory.Stock[i].dLength
|
||||
VirtualBeam.dResidualLength = RawInventory.Stock[i].dLength - ( NEST.STARTOFFSET or 0)
|
||||
local CurrentMove = FindBestPartForBeam( VirtualBeam)
|
||||
if CurrentMove and CurrentMove.dScore > dHighestScore then
|
||||
dHighestScore = CurrentMove.dScore
|
||||
@@ -224,6 +452,7 @@ while true do
|
||||
-- 3 Se BestMove trovata si aggiornano lista pezzi e barre
|
||||
if BestMove then
|
||||
CommitBestMove( BestMove)
|
||||
nDoneParts = nDoneParts + 1
|
||||
-- se non c'è più niente di compatibile si esce
|
||||
else
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user