236 lines
6.6 KiB
Lua
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 |