Files
databeamnew/NestProcess.lua
T
2026-05-15 10:26:30 +02:00

236 lines
6.6 KiB
Lua

-- BeamNestProcess.lua by Egalware s.r.l. 2026/05/11
-- Gestione nesting automatico travi anche oblique
-- Intestazioni
require( 'EgtBase')
_ENV = EgtProtectGlobal()
EgtEnableDebug( true)
-- Include
----------------------------------------------------------------------------------------------------------
-- inventario grezzi
local RawInventory = {
Stock = {},
ActiveBeams = {}
}
function RawInventory:GetNewBeam( dLength)
local NewBeam = {
dTotalLength = dLength,
dResidual = dLength - NEST.STARTOFFSET,
LastOffsetX = { 0, 0, 0, 0},
LastVtN = Vector3d( 1, 0, 0),
dLastHeadRecess = 0,
NestedParts = {}
}
return NewBeam
end
function RawInventory:BuildStock()
if #LEN ~= #QTY then
error( 'NestProcess: invalid stock data')
end
for i = 1, #LEN do
self.Stock[#self.Stock + 1] = {
dLength = LEN[i],
nCount = QTY[i]
}
end
return RawInventory
end
-- aggiunge una nuova barra attiva rimuovendo la corrispondente dalla lista stock disponibili
function RawInventory:AddActiveBeam( nStockIndex)
local CurrentStock = self.Stock[nStockIndex]
-- se barra disponibile posso aggiungerla a quelle attive
if CurrentStock and CurrentStock.nCount > 0 then
-- update quantità a stock
CurrentStock.nCount = CurrentStock.nCount - 1
-- aggiungo una nuova barra attiva
local NewBeam = self:GetNewBeam( CurrentStock.dLength)
table.insert( self.ActiveBeams, NewBeam)
return NewBeam
end
return nil
end
----------------------------------------------------------------------------------------------------------
-- PartTemplates (informazioni geometriche pezzi univoci)
-- JobPool (lista di tutti i singoli pezzi, multipli compresi, da nestare)
local PartTemplates = {}
local JobPool = {}
function PartTemplates:GetInfoFromPart( id, sStateWithSide)
local OffsetX = {}
local vtN
local sInfo = EgtGetInfo( id, sStateWithSide)
if not sInfo then
return
end
local Info = EgtSplitString( sInfo, ';')
OffsetX = EgtSplitString( Info[1], ',')
vtN = VectorFromString( Info[2])
-- si convertono gli offset in numeri
for i = 1, #OffsetX do
OffsetX[i] = tonumber( OffsetX[i]) or 0
end
return OffsetX, vtN
end
function PartTemplates:AddPart( id)
self[id] = {}
self[id].dLength = EgtGetInfo( id, 'L', 'd')
self[id].States = {}
self[id].dMaxGlobalTailRecess = 0
local States = { '1000', '0010', '1000_INV', '0010_INV' }
for _, sState in ipairs(States) do
local OffsetXHead, vtNHead = self:GetInfoFromPart( id, 'ALT' .. sState .. '_H')
local OffsetXTail, vtNTail = self:GetInfoFromPart( id, 'ALT' .. sState .. '_T')
if OffsetXHead or OffsetXTail then
if not OffsetXHead then
OffsetXHead = { 0, 0, 0, 0}
vtNHead = Vector3d( 1, 0, 0)
end
if not OffsetXTail then
OffsetXTail = { 0, 0, 0, 0}
vtNTail = Vector3d( -1, 0, 0)
end
self[id].States[sState] = {}
local State = self[id].States[sState]
State.Head = {
OffsetX = OffsetXHead,
vtN = vtNHead
}
State.Tail = {
OffsetX = OffsetXTail,
vtN = vtNTail
}
-- overlap massimi in testa e in coda per questo pezzo
local dMaxHeadRecess = 0
for i = 1, 4 do
if State.Head.OffsetX[i] > dMaxHeadRecess + 10 * GEO.EPS_SMALL then
dMaxHeadRecess = State.Head.OffsetX[i]
end
end
State.dMaxHeadRecess = dMaxHeadRecess
local dMaxTailRecess = 0
for i = 1, 4 do
if abs( State.Tail.OffsetX[i]) > dMaxTailRecess + 10 * GEO.EPS_SMALL then
dMaxTailRecess = abs( State.Tail.OffsetX[i])
end
end
State.dMaxTailRecess = dMaxTailRecess
if dMaxTailRecess > self[id].dMaxGlobalTailRecess + 10 * GEO.EPS_SMALL then
self[id].dMaxGlobalTailRecess = dMaxTailRecess
end
end
end
end
-- ordinamento JobPool per lunghezza decrescente dei pezzi
function JobPool:Sort()
local function SortByLengthDescending( Part1, Part2)
local dLength1 = PartTemplates[Part1.id].dLength
local dLength2 = PartTemplates[Part2.id].dLength
if dLength1 > dLength2 + 10 * GEO.EPS_SMALL then
return true
elseif dLength2 > dLength1 + 10 * GEO.EPS_SMALL then
return false
end
-- tie breaker
return Part1.id < Part2.id
end
table.sort( self, SortByLengthDescending)
end
-- creazione combinata (si cicla una sola volta) di entrambe le tabelle
local function BuildPartTemplatesAndJobPool()
for id, nCount in pairs( PART) do
PartTemplates:AddPart( id)
for _ = 1, nCount do
table.insert( JobPool, { id = id, bNested = false})
end
end
return PartTemplates, JobPool
end
----------------------------------------------------------------------------------------------------------
local function FindBestPartForBeam( Beam)
end
----------------------------------------------------------------------------------------------------------
-- script principale
-- costruzione lista grezzi a stock disponibili
RawInventory:BuildStock()
-- costruzione lista pezzi template con proprietà geometriche e lista pezzi fisici da nestare
BuildPartTemplatesAndJobPool()
-- ordinamento pezzi per lunghezza decrescente
JobPool:Sort()
-- 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
while true do
local BestMove
local dHighestScore = -GEO.INFINITO
-- 1 Si provano le barre già attive
for i = 1, #RawInventory.ActiveBeams do
local CurrentMove = FindBestPartForBeam( RawInventory.ActiveBeams[i])
if CurrentMove and CurrentMove.dScore > dHighestScore then
dHighestScore = CurrentMove.dScore
BestMove = CurrentMove
BestMove.nActiveBeamIndex = i
end
end
-- 2 Si provano le barre ancora a stock
for i = 1, #RawInventory.Stock do
local VirtualBeam = RawInventory:GetNewBeam( RawInventory.Stock[i].dLength)
if RawInventory.Stock[i].nCount > 0 then
local CurrentMove = FindBestPartForBeam( VirtualBeam)
if CurrentMove and CurrentMove.dScore > dHighestScore then
dHighestScore = CurrentMove.dScore
BestMove = CurrentMove
BestMove.nStockIndex = i
end
end
end
-- 3 Se BestMove trovata si aggiornano lista pezzi e barre
if BestMove then
CommitBestMove( BestMove)
-- se non c'è più niente di compatibile si esce
else
break
end
end