Files
databeamnew/LuaLibs/BeamLib.lua
T
luca.mazzoleni b8299df247 - in BeamExec.ProcessAlternatives si passano correttamente le info a interfaccia da scrivere sul pezzo
- in BeamLib aggiunta funzione ConvertBitIndexToRotationIndex per convertire da BitIndex a RotationIndex
2026-05-13 18:42:01 +02:00

1154 lines
47 KiB
Lua

-- BeamLib.lua by Egalware s.r.l. 2024/04/02
-- Libreria globale per Travi
-- 2024/04/02 PRIMA VERSIONE CALCOLO LAVORAZIONI CON STRATEGIE
-- Tabella per definizione modulo
local BeamLib = {}
-- Include
require( 'EgtBase')
local BeamData = require( 'BeamDataNew')
EgtOutLog( ' BeamLib started', 1)
--TODO refactoring di queste funzioni
-------------------------------------------------------------------------------------------------------------
function BeamLib.AddPartStartFace( PartId, b3Solid)
-- recupero gruppo per geometria aggiuntiva
local AddGrpId = BeamLib.GetAddGroup( PartId)
if not AddGrpId then
local sErr = 'Error on process StartFace impossible to find AddGroup'
EgtOutLog( sErr)
return false, sErr
end
-- aggiungo nuovo taglio iniziale
local nStmId = EgtSurfTmPlaneInBBox( AddGrpId, b3Solid:getMax(), X_AX(), b3Solid, GDB_RT.GLOB)
if not nStmId then
local sErr = 'Error on process StartFace impossible to create Face'
EgtOutLog( sErr)
return false, sErr
end
-- applico gli opportuni attributi di feature
EgtSetInfo( nStmId, 'HEAD_ADD_CUT', 1)
EgtSetInfo( nStmId, 'GRP', 1)
EgtSetInfo( nStmId, 'PRC', 10) -- ex 340
return true
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.AddPartEndFace( PartId, b3Solid)
-- recupero gruppo per geometria aggiuntiva
local AddGrpId = BeamLib.GetAddGroup( PartId)
if not AddGrpId then
local sErr = 'Error on process EndFace impossible to find AddGroup'
EgtOutLog( sErr)
return false, sErr
end
-- aggiungo nuovo taglio finale
local nStmId = EgtSurfTmPlaneInBBox( AddGrpId, b3Solid:getMin(), -X_AX(), b3Solid, GDB_RT.GLOB)
if not nStmId then
local sErr = 'Error on process EndFace impossible to create Face'
EgtOutLog( sErr)
return false, sErr
end
-- applico gli opportuni attributi di feature
EgtSetInfo( nStmId, 'HEAD_ADD_CUT', 1)
EgtSetInfo( nStmId, 'GRP', 2)
EgtSetInfo( nStmId, 'PRC', 10) -- ex 350
return true
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.GetPartBoxWithHeadTail( Part, sSide)
local _sSide = sSide or ''
-- si può usare il box del grezzo che contiene sempre il sovramateriale di testa
if _sSide == 'Head' then
local b3WithHead = BBox3d( Part.b3Raw)
return b3WithHead
-- se è l'ultimo pezzo della barra si aggiunge il box dell'eventuale grezzo successivo
-- se non lo è, si estende il box del pezzo fino alla fine della barra
elseif _sSide == 'Tail' then
local b3WithTail = BBox3d( Part.b3Part)
if Part.bIsLastPart then
local nNextRawId = EgtGetNextRawPart( Part.idRaw)
if nNextRawId then
local b3Tail = EgtGetRawPartBBox( nNextRawId)
b3WithTail:Add( b3Tail)
end
else
-- per il punto di inizio barra si parte dal box del grezzo perchè contiene già il sovramateriale spessore lama/catena in coda (la restLength non lo comprende)
local ptXMin = Point3d( Part.b3Raw:getMin():getX() - Part.dRestLength, Part.b3Part:getMin():getY(), Part.b3Part:getMin():getZ())
b3WithTail:Add( ptXMin)
end
return b3WithTail
-- per aggiungere entrambi si procede come per la coda ma partendo dal box del grezzo, che già contiene la testa
elseif sSide == 'HeadTail' then
local b3WithHeadTail = BBox3d( Part.b3Raw)
if Part.bIsLastPart then
local nNextRawId = EgtGetNextRawPart( Part.idRaw)
if nNextRawId then
local b3Tail = EgtGetRawPartBBox( nNextRawId)
b3WithHeadTail:Add( b3Tail)
end
else
-- per il punto di inizio barra si parte dal box del grezzo perchè contiene già il sovramateriale spessore lama/catena in coda (la restLength non lo comprende)
local ptXMin = Point3d( Part.b3Raw:getMin():getX() - Part.dRestLength, Part.b3Part:getMin():getY(), Part.b3Part:getMin():getZ())
b3WithHeadTail:Add( ptXMin)
end
return b3WithHeadTail
-- sSide non definito, si restituisce il box del pezzo con l'aggiunta dello spessore lama/catena in coda
else
local b3WithSplit = BBox3d( Part.b3Part)
-- per il punto di inizio barra si parte dal box del grezzo perchè contiene già il sovramateriale spessore lama/catena in coda (la restLength non lo comprende)
local ptXMin = Point3d( Part.b3Raw:getMin():getX(), Part.b3Part:getMin():getY(), Part.b3Part:getMin():getZ())
b3WithSplit:Add( ptXMin)
return b3WithSplit
end
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.GetPartSplittingPoints( Part)
local PartSplittingPoints = {}
local dPartLength = Part.b3Part:getDimX()
local dMaxSegmentLength = BeamData.LONGCUT_MAXLEN
local dMaxSegmentLengthOnEdges = BeamData.LONGCUT_ENDLEN
if dPartLength < dMaxSegmentLength + 10 * GEO.EPS_SMALL then
return {}
elseif dPartLength >= dMaxSegmentLength + 10 * GEO.EPS_SMALL
and dPartLength < 3 * dMaxSegmentLengthOnEdges + 10 * GEO.EPS_SMALL then
local dXPartCentralPoint = ( Part.b3Part:getMax():getX() - Part.b3Part:getMin():getX()) / 2
table.insert( PartSplittingPoints, Point3d( dXPartCentralPoint, 0, 0))
else
-- punto estremo destro
local dSplitXRight = Part.b3Part:getMax():getX() - dMaxSegmentLengthOnEdges
table.insert( PartSplittingPoints, Point3d( dSplitXRight, 0, 0))
-- punti centrali
local dPartCentralLength = dPartLength - 2 * dMaxSegmentLengthOnEdges
local nSplitParts = max( ceil( dPartCentralLength / dMaxSegmentLength + 10 * GEO.EPS_SMALL), 1)
local dSplitPartsLen = dPartCentralLength / nSplitParts
for i = 1, ( nSplitParts - 1) do
local ptOn
local dCurrentPointX = dSplitXRight - i * dSplitPartsLen
ptOn = Point3d( dCurrentPointX, 0, 0)
table.insert( PartSplittingPoints, ptOn)
end
-- punto estremo sinistro
local dSplitXLeft = Part.b3Part:getMin():getX() + dMaxSegmentLengthOnEdges
table.insert( PartSplittingPoints, Point3d( dSplitXLeft, 0, 0))
end
return PartSplittingPoints
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.AddPhaseWithRawParts( idRaw, OriXR, PosXR, dDeltaSucc)
local nPhase = EgtAddPhase()
-- si aprono i limiti tavola per permettere rotazioni di pezzi più larghi della tavola
EgtSetTableAreaOffset( 2000, 2000, 2000, 2000)
local dRawMove = 0
while idRaw do
EgtKeepRawPart( idRaw)
EgtMoveToCornerRawPart( idRaw, OriXR, PosXR)
EgtMoveRawPart( idRaw, Vector3d( - dRawMove, 0, 0))
if dRawMove == 0 then dRawMove = dRawMove + dDeltaSucc end
dRawMove = dRawMove + EgtGetRawPartBBox( idRaw):getDimX()
idRaw = EgtGetNextRawPart( idRaw)
end
-- salvo info nuova fase aggiunta
local MachExtraInfo = { sType = 'DISP',
nPhase = nPhase}
table.insert( DB_MACH_APPLIED, MachExtraInfo)
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.IsPointOnBoxLimit( ptPointToCheck, b3Solid)
local bOnBoxLimit = false
if abs( ptPointToCheck:getX() - b3Solid:getMax():getX()) < 100 * GEO.EPS_SMALL or
abs( ptPointToCheck:getX() - b3Solid:getMin():getX()) < 100 * GEO.EPS_SMALL or
abs( ptPointToCheck:getY() - b3Solid:getMax():getY()) < 100 * GEO.EPS_SMALL or
abs( ptPointToCheck:getY() - b3Solid:getMin():getY()) < 100 * GEO.EPS_SMALL or
abs( ptPointToCheck:getZ() - b3Solid:getMax():getZ()) < 100 * GEO.EPS_SMALL or
abs( ptPointToCheck:getZ() - b3Solid:getMin():getZ()) < 100 * GEO.EPS_SMALL then
bOnBoxLimit = true
end
return bOnBoxLimit
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.SetOpenSide( nPathInt, b3Solid)
-- fondo tra loro le curve compatibili
EgtMergeCurvesInCurveCompo( nPathInt)
-- vettore indici lati aperti
local vOpen = {}
-- ciclo sulle curve elementari della composita
local _, nNumEnt = EgtCurveDomain( nPathInt)
for i = 0, nNumEnt - 1 do
-- se segmento di retta
if EgtCurveCompoRadius( nPathInt, i) == -1 then
-- verifico se giace in uno dei piani limite del pezzo quindi se è un lato aperto
local ptIni = EgtUP( nPathInt, i, GDB_RT.GLOB)
local ptFin = EgtUP( nPathInt, i + 1, GDB_RT.GLOB)
if BeamLib.IsPointOnBoxLimit( ptIni, b3Solid) and BeamLib.IsPointOnBoxLimit( ptFin, b3Solid) then
-- aggiorno il vettore dei lati aperti
table.insert( vOpen, i)
end
end
end
-- assegno gli indici dei lati aperti
EgtSetInfo( nPathInt, 'OPEN', vOpen)
return true
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.ConvertToClosedCurve( Proc, AuxId)
local bCurveModified = false
if not EgtCurveIsClosed( AuxId) then
local NewId, nCount = EgtExtractSurfTmFacetLoops( Proc.id, 0, EgtGetParent( Proc.id))
if NewId then
-- elimino eventuali loop interni (non dovrebbero comunque esserci)
for i = 1, nCount - 1 do
EgtErase( NewId + i)
end
local vtExtr = EgtCurveExtrusion( AuxId, GDB_ID.ROOT)
-- sostituisco il loop esterno alla curva originale
EgtModifyCurveExtrusion( NewId, vtExtr, GDB_ID.ROOT)
EgtRelocate( NewId, AuxId, GDB_IN.AFTER)
EgtErase( AuxId)
EgtChangeId( NewId, AuxId)
bCurveModified = true
-- sistemo i lati aperti
local vFacAdj = EgtSurfTmFacetAdjacencies( Proc.id, 0)[1]
if vFacAdj then
local vOpen = {}
for i = 1, #vFacAdj do
if vFacAdj[i] < 0 then
table.insert( vOpen, i - 1)
end
end
EgtSetInfo( AuxId, 'OPEN', vOpen)
end
end
end
return true, bCurveModified
end
-------------------------------------------------------------------------------------------------------------
--- funzione che inverte il grezzo testa-coda
function BeamLib.InvertRawPart( Part, nNumberOfRotations)
-- se non si deve ruotare, si esce subito
if nNumberOfRotations == 0 then
return
end
-- box del solido
local b3BoxPart = EgtGetBBoxGlob( EgtGetFirstNameInGroup( Part.id or GDB_ID.NULL, 'Box') or GDB_ID.NULL, GDB_BB.EXACT)
-- box esatto del pezzo
local b3BoxExact = EgtGetBBoxGlob( Part.id or GDB_ID.NULL, GDB_BB.EXACT)
-- rotazione
local dAngRot = nNumberOfRotations * 90
EgtRotatePartInRawPart( Part.id, Z_AX(), dAngRot)
-- correggo per eccentricità solido rispetto a geometria complessiva del pezzo
local vtEccOri = b3BoxPart:getCenter() - b3BoxExact:getCenter()
local vtEccRot = Vector3d( vtEccOri)
vtEccRot:rotate( Z_AX(), dAngRot)
EgtMovePartInRawPart( Part.id, ( vtEccOri - vtEccRot))
-- si salva posizione corrente
if Part.bIsInverted then
Part.bIsInverted = false
else
Part.bIsInverted = true
end
end
-------------------------------------------------------------------------------------------------------------
--- funzione che ruota il grezzo
function BeamLib.RotateRawPart( Part, nNumberOfRotations)
-- sistemo numero rotazioni in caso sia negativa
if nNumberOfRotations < 0 then
nNumberOfRotations = nNumberOfRotations + 4
end
-- primo posizionamento
if nNumberOfRotations == 0 then
; -- il pezzo è già in posizione
-- rotazione 90°
elseif nNumberOfRotations == 1 then
local dDeltaYZ = EgtGetRawPartBBox( Part.idRaw):getDimY() - EgtGetRawPartBBox( Part.idRaw):getDimZ()
local vtMove = Vector3d( 0, dDeltaYZ / 2 * EgtIf( BeamData.RIGHT_LOAD, -1, 1), dDeltaYZ / 2)
local bPreMove = dDeltaYZ < 0
-- ruoto le travi della fase corrente
local nRId = Part.idRaw
while nRId do
if bPreMove then EgtMoveRawPart( nRId, vtMove) end
local bIsRotationOK = EgtRotateRawPart( nRId, X_AX(), EgtIf( BeamData.RIGHT_LOAD, -90, 90))
if not bIsRotationOK then error( 'UNEXPECTED ERROR: rotation of the raw not executed') end
if not bPreMove then EgtMoveRawPart( nRId, vtMove) end
nRId = EgtGetNextRawPart( nRId)
end
-- rotazione 180°
elseif nNumberOfRotations == 2 then
-- ribalto le travi della fase corrente
local nRId = Part.idRaw
while nRId do
EgtRotateRawPart( nRId, X_AX(), 180)
nRId = EgtGetNextRawPart( nRId)
end
-- rotazione 270°
elseif nNumberOfRotations == 3 then
local dDeltaYZ = EgtGetRawPartBBox( Part.idRaw):getDimY() - EgtGetRawPartBBox( Part.idRaw):getDimZ()
local vtMove = Vector3d( 0, dDeltaYZ / 2 * EgtIf( BeamData.RIGHT_LOAD, -1, 1), dDeltaYZ / 2)
local bPreMove = dDeltaYZ < 0
-- ruoto le travi della fase corrente
local nRId = Part.idRaw
while nRId do
if bPreMove then EgtMoveRawPart( nRId, vtMove) end
local bIsRotationOK = EgtRotateRawPart( nRId, X_AX(), EgtIf( BeamData.RIGHT_LOAD, 90, -90))
if not bIsRotationOK then error( 'UNEXPECTED ERROR: rotation of the raw not executed') end
if not bPreMove then EgtMoveRawPart( nRId, vtMove) end
nRId = EgtGetNextRawPart( nRId)
end
end
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.CreateAddGroup( PartId, sGroupName)
AddGrpId = EgtGroup( PartId or GDB_ID.NULL)
if not AddGrpId then
return false
end
-- assegno nome, flag di layer per gruppo di lavoro e colore
EgtSetName( AddGrpId, sGroupName)
EgtSetInfo( AddGrpId, GDB_SI.MGRPONLY, EgtGetCurrMachGroup())
EgtSetColor( AddGrpId, Color3d( 80, 160, 160, 25))
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.CreateOrEmptyAddGroup( PartId)
-- recupero i dati del gruppo aggiuntivo
local AddGrpId, sMchGrp = BeamLib.GetAddGroup( PartId)
if not sMchGrp then
return false
end
-- se esiste, aggiorno riferimento al gruppo di lavoro e lo svuoto
if AddGrpId then
EgtSetInfo( AddGrpId, GDB_SI.MGRPONLY, EgtGetCurrMachGroup())
return EgtEmptyGroup( AddGrpId)
end
-- altrimenti lo creo
BeamLib.CreateAddGroup( PartId, sMchGrp)
return true
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.CreateTempGroup()
local idTempGroup = BeamLib.GetTempGroup()
-- se non esiste, lo creo
if not idTempGroup then
idTempGroup = EgtGroup( GDB_ID.ROOT)
EgtSetName( idTempGroup, "#TEMP_GROUP#")
EgtSetColor( idTempGroup, Color3d( 80, 160, 160, 1))
EgtSetLevel( idTempGroup, GDB_LV.TEMP)
EgtSetStatus( idTempGroup, GDB_ST.OFF)
end
return idTempGroup
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.GetTempGroup()
local idTempGroup = EgtGetFirstNameInGroup( GDB_ID.ROOT, "#TEMP_GROUP#") or nil
return idTempGroup
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.GetAddGroup( PartId)
-- recupero il nome del gruppo di lavoro corrente
local sMchGrp = EgtGetMachGroupName( EgtGetCurrMachGroup() or GDB_ID.NULL)
if not sMchGrp then
return nil, nil
end
-- cerco il gruppo aggiuntivo omonimo nel pezzo e se esiste lo restituisco
local AddGrpId = EgtGetFirstNameInGroup( PartId or GDB_ID.NULL, sMchGrp)
-- restituisco Id e Nome
return AddGrpId, sMchGrp
end
-------------------------------------------------------------------------------------------------------------
-- restituisce le facce della parte interessate dalla feature Proc
-- TODO da spostare in FeatureLib???
-- TODO le feature 1 faccia devono essere settate aperte sulla faccia indicata dalla normale
function BeamLib.GetAffectedFaces( Proc, Part)
local vtFacesAffected = { bTop = false, bBottom = false, bFront = false, bBack = false, bLeft = false, bRight = false}
if Proc.b3Box and not Proc.b3Box:isEmpty() then
if Proc.b3Box:getMax():getZ() > Part.b3Part:getMax():getZ() - 500 * GEO.EPS_SMALL then
vtFacesAffected.bTop = true
end
if Proc.b3Box:getMin():getZ() < Part.b3Part:getMin():getZ() + 500 * GEO.EPS_SMALL then
vtFacesAffected.bBottom = true
end
if Proc.b3Box:getMin():getY() < Part.b3Part:getMin():getY() + 500 * GEO.EPS_SMALL then
vtFacesAffected.bFront = true
end
if Proc.b3Box:getMax():getY() > Part.b3Part:getMax():getY() - 500 * GEO.EPS_SMALL then
vtFacesAffected.bBack = true
end
if Proc.b3Box:getMin():getX() < Part.b3Part:getMin():getX() + 500 * GEO.EPS_SMALL then
vtFacesAffected.bLeft = true
end
if Proc.b3Box:getMax():getX() > Part.b3Part:getMax():getX() - 500 * GEO.EPS_SMALL then
vtFacesAffected.bRight = true
end
end
return vtFacesAffected
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.GetNearestOrthoOpposite( vtRef, vtNorm)
-- se definita anche la normale alla faccia, elimino la parte di vtRef parallela a questa
local vtMyRef = Vector3d( vtRef)
if vtNorm then
vtMyRef = vtMyRef - ( vtMyRef * vtNorm) * vtNorm
vtMyRef:normalize()
end
-- se prevalente una componente orizzontale (con piccolissimo vantaggio)
if abs( vtMyRef:getX()) > 0.91 * abs( vtMyRef:getZ()) or abs( vtMyRef:getY()) > 0.91 * abs( vtMyRef:getZ()) then
-- se prevale la componente destra/sinistra
if abs( vtMyRef:getX()) > 0.95 * abs( vtMyRef:getY()) then
if vtMyRef:getX() > -GEO.EPS_SMALL then
return MCH_MILL_FU.ORTHO_LEFT
else
return MCH_MILL_FU.ORTHO_RIGHT
end
else
if vtMyRef:getY() > -GEO.EPS_SMALL then
return MCH_MILL_FU.ORTHO_FRONT
else
return MCH_MILL_FU.ORTHO_BACK
end
end
-- altrimenti prevale la verticale
else
if vtMyRef:getZ() > -GEO.EPS_SMALL then
return MCH_MILL_FU.ORTHO_DOWN
else
return MCH_MILL_FU.ORTHO_TOP
end
end
return nil
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.GetNearestParalOpposite( vtRef, vtNorm)
-- se definita anche la normale alla faccia, elimino la parte di vtRef parallela a questa
local vtMyRef = Vector3d( vtRef)
if vtNorm then
vtMyRef = vtMyRef - ( vtMyRef * vtNorm) * vtNorm
vtMyRef:normalize()
end
-- se prevalente una componente orizzontale (con piccolissimo vantaggio)
if abs( vtMyRef:getX()) > 0.95 * abs( vtMyRef:getZ()) or abs( vtMyRef:getY()) > 0.95 * abs( vtMyRef:getZ()) then
if abs( vtMyRef:getX()) > 0.95 * abs( vtMyRef:getY()) then
if vtMyRef:getX() > -GEO.EPS_SMALL then
return MCH_MILL_FU.PARAL_LEFT
else
return MCH_MILL_FU.PARAL_RIGHT
end
else
if vtMyRef:getY() > -GEO.EPS_SMALL then
return MCH_MILL_FU.PARAL_FRONT
else
return MCH_MILL_FU.PARAL_BACK
end
end
-- altrimenti prevale la verticale
else
if vtMyRef:getZ() > -GEO.EPS_SMALL then
return MCH_MILL_FU.PARAL_DOWN
else
return MCH_MILL_FU.PARAL_TOP
end
end
return nil
end
-------------------------------------------------------------------------------------------------------------
-- TODO gestire anche altri valori
function BeamLib.GetDirectionFromSCC( nSCC)
local vtSCC
if nSCC == MCH_SCC.ADIR_XP then
vtSCC = X_AX()
elseif nSCC == MCH_SCC.ADIR_XM then
vtSCC = -X_AX()
elseif nSCC == MCH_SCC.ADIR_YP then
vtSCC = Y_AX()
elseif nSCC == MCH_SCC.ADIR_YM then
vtSCC = -Y_AX()
elseif nSCC == MCH_SCC.ADIR_ZP then
vtSCC = Z_AX()
elseif nSCC == MCH_SCC.ADIR_ZM then
vtSCC = -Z_AX()
end
return vtSCC
end
-------------------------------------------------------------------------------------------------------------
-- Restituisce una tabella con i punti ai vertici della faccia, in globale
-- ordinati partendo da quello ai valori minimi degli assi e i successivi secondo rotazione destrorsa X+;
-- solo per Proc a 1 faccia
function BeamLib.GetSortedVertices( Proc)
local PtVerticesSorted = {}
-- se più di una faccia si esce subito
if Proc.nFct > 1 then
return
end
local PtVertices = {}
local nFirstIndex
local dMinYZ = GEO.INFINITO
for i = 1, #Proc.Faces[1].Edges do
local Edge = Proc.Faces[1].Edges[i]
table.insert( PtVertices, Edge.ptStart)
if ( Edge.ptStart:getY() + Edge.ptStart:getZ() < dMinYZ) then
nFirstIndex = i
dMinYZ = Edge.ptStart:getY() + Edge.ptStart:getZ()
end
end
table.insert( PtVerticesSorted, PtVertices[nFirstIndex])
local nCurrentIndex = nFirstIndex
-- faccia che guarda verso X+, ordine ok
if Proc.Faces[1].vtN:getX() > GEO.EPS_SMALL then
for _ = 1, #PtVertices - 1 do
_, nCurrentIndex = BeamLib.GetAdjacentIndices( nCurrentIndex, #PtVertices)
table.insert( PtVerticesSorted, PtVertices[nCurrentIndex])
end
-- faccia che guarda verso X-, ordine da invertire
else
for _ = 1, #PtVertices - 1 do
nCurrentIndex = BeamLib.GetAdjacentIndices( nCurrentIndex, #PtVertices)
table.insert( PtVerticesSorted, PtVertices[nCurrentIndex])
end
end
return PtVerticesSorted
end
-------------------------------------------------------------------------------------------------------------
-- restituisce il precedente e prossimo indice 1-based, tenendo conto del massimo indice
function BeamLib.GetAdjacentIndices( nCurrentIndex, nMaxIndex)
local nPreviousIndex, nNextIndex
if ( nCurrentIndex < 1) or ( nCurrentIndex > nMaxIndex) then
return
end
-- circular indexing 1-based
nPreviousIndex = ((nCurrentIndex - 2 + nMaxIndex) % nMaxIndex) + 1
nNextIndex = (nCurrentIndex % nMaxIndex) + 1
return nPreviousIndex, nNextIndex
end
-------------------------------------------------------------------------------------------------------------
-- Funzione per determinare se la faccia ha lati molto corti (trascurabili) ed è quindi approssimabile ad una 3 facce
function BeamLib.Is3EdgesApprox( Proc, idFace, nAddGrpId)
nAddGrpId = nAddGrpId or BeamLib.GetAddGroup( Proc.idPart)
if not nAddGrpId then
local nEdges = #(EgtSurfTmFacetAdjacencies( Proc.id, idFace)[1])
return ( nEdges == 3)
end
local bResult = false
local nContourId, nContourCnt = EgtExtractSurfTmFacetLoops( Proc.id, idFace, nAddGrpId)
if not nContourId then
return false
end
EgtMergeCurvesInCurveCompo( nContourId)
-- recupero il numero effettivo di lati
local _, nEntityCount = EgtCurveDomain( nContourId)
local nEdges = nEntityCount
if nEntityCount and nEntityCount == 3 then
bResult = true
-- rimuovo i lati molto corti dal conteggio totale
elseif nEntityCount then
for i = 1, nEntityCount do
local dLength = EgtCurveCompoLength( nContourId, i - 1)
if dLength < 5 then nEdges = nEdges - 1 end
end
end
if nEdges == 3 then bResult = true end
-- cancello tutti i contorni appena creati
EgtErase( EgtTableFill( nContourId, nContourCnt))
return bResult
end
-------------------------------------------------------------------------------------------------------------
-- funzione con valore di default in caso la GetSetupInfo della testa della macchina non avesse la funzione definita
function BeamLib.GetMinNzDownUpDefault( b3Raw, vtNFace, vtToolDirection, Tool)
return -2
end
-------------------------------------------------------------------------------------------------------------
-- funzione con valore di default in caso la GetSetupInfo della testa della macchina non avesse la funzione definita
function BeamLib.GetMinNzDefault( vtNFace, Tool)
return 0
end
-------------------------------------------------------------------------------------------------------------
-- funzione con valore di default in caso la GetSetupInfo della testa della macchina non avesse la funzione definita
function BeamLib.GetMaxNzDefault( vtNFace, Tool)
return 0
end
-- TODO bisogna recuperare il nome del parametro NGE, perchè in questo caso è forzato
-------------------------------------------------------------------------------------------------------------
function BeamLib.GetPieceGeneralParameters( Part, DefaultGeneralParamList)
local GeneralParameters = {}
-- si salvano sul pezzo le note forzate, altrimenti si leggeranno dopo, quando si prendono i dati della strategia
for i = 1, #DefaultGeneralParamList do
local sParamNameNGE = DefaultGeneralParamList[i].sNameNge
local xParameterValue = EgtGetInfo( Part.id, sParamNameNGE, 's') or nil
-- se il paraemtro esiste, salvataggio dato su lista con accesso diretto
if xParameterValue then
if DefaultGeneralParamList[i].sType == 'b' then
GeneralParameters[DefaultGeneralParamList[i].sName] = xParameterValue == 'true' or xParameterValue == '1' or xParameterValue == true
elseif DefaultGeneralParamList[i].sType == 'd' then
if #DefaultGeneralParamList[i].sValue > 0 then
GeneralParameters[DefaultGeneralParamList[i].sName] = tonumber( xParameterValue)
-- stringa vuota equivale a non passare alcun valore (deciderà la strategia)
else
GeneralParameters[DefaultGeneralParamList[i].sName] = nil
end
else --DefaultGeneralParamList[i].sType == 's' or DefaultGeneralParamList[i].sType == 'combo'
GeneralParameters[DefaultGeneralParamList[i].sName] = xParameterValue
end
else
if GENERAL_PARAMETERS.BTL and GENERAL_PARAMETERS.BTL[Part.sBTLInfo] and GENERAL_PARAMETERS.BTL[Part.sBTLInfo][DefaultGeneralParamList[i].sName] ~= nil then
GeneralParameters[DefaultGeneralParamList[i].sName] = GENERAL_PARAMETERS.BTL[Part.sBTLInfo][DefaultGeneralParamList[i].sName]
elseif GENERAL_PARAMETERS.PROJECT[DefaultGeneralParamList[i].sName] ~= nil then
GeneralParameters[DefaultGeneralParamList[i].sName] = GENERAL_PARAMETERS.PROJECT[DefaultGeneralParamList[i].sName]
end
end
end
return GeneralParameters
end
-------------------------------------------------------------------------------------------------------------
local function GetInheritedParameter( Part, sSourceParamName)
local sValue = nil
if sSourceParamName and #sSourceParamName > 0 then
if Part.GeneralParameters[sSourceParamName] ~= nil then
sValue = Part.GeneralParameters[sSourceParamName]
elseif GENERAL_PARAMETERS.BTL and GENERAL_PARAMETERS.BTL[Part.sBTLInfo] and GENERAL_PARAMETERS.BTL[Part.sBTLInfo][sSourceParamName] ~= nil then
sValue = GENERAL_PARAMETERS.BTL[Part.sBTLInfo][sSourceParamName]
elseif GENERAL_PARAMETERS.PROJECT[sSourceParamName] ~= nil then
sValue = GENERAL_PARAMETERS.PROJECT[sSourceParamName]
end
end
return sValue
end
-------------------------------------------------------------------------------------------------------------
-- si traduce la tabella dei parametri con tutte le informazioni in una lista contenente i parametri utilizzabili con accesso diretto
function BeamLib.LoadCustomParametersInStrategy( Proc, Part, CustomParameters, DefaultStrategyParamList)
local UpdatedParameters = {}
-- TODO serve controllare se campo 'sStrategyId' congruente tra il Config (DefaultStrategyParamList) e i parametri custom (CustomParameters)?
if DefaultStrategyParamList and DefaultStrategyParamList.ParameterList and #DefaultStrategyParamList.ParameterList > 0 then
-- lettura e settaggio parametri finali per la strategia (customizzati o default)
for i = 1, #DefaultStrategyParamList.ParameterList do
local xParameterValue = nil
-- se strategia forzata, leggo eventuali parametri nelle info
if CustomParameters and CustomParameters.bForcedStrategy and DefaultStrategyParamList.ParameterList[i].sNameNge then
local sParameterToRead = DefaultStrategyParamList.sStrategyId .. '_' .. DefaultStrategyParamList.ParameterList[i].sNameNge
local sForcedParameterForProc = EgtGetInfo( Proc.id, sParameterToRead, 's')
-- se ho trovato il valore, lo sovrascrivo al default
if sForcedParameterForProc then
xParameterValue = sForcedParameterForProc
end
-- altrimenti i parametri custom si trovano già su questa lista
elseif CustomParameters and CustomParameters.ParameterList then
for j = 1, #CustomParameters.ParameterList do
if CustomParameters.ParameterList[j].sName == DefaultStrategyParamList.ParameterList[i].sName or
CustomParameters.ParameterList[j].sName == DefaultStrategyParamList.ParameterList[i].sNameNge then
xParameterValue = CustomParameters.ParameterList[j].sValue
break
end
end
end
-- se non arriva forzato da strategia, oppure se non è stato configurato da cliente, si prende default
if xParameterValue == nil then
-- se il parametro strategia è da ereditare da un parametro globale
if DefaultStrategyParamList.ParameterList[i].sSource and #DefaultStrategyParamList.ParameterList[i].sSource > 0 then
xParameterValue = GetInheritedParameter( Part, DefaultStrategyParamList.ParameterList[i].sSource)
else
xParameterValue = DefaultStrategyParamList.ParameterList[i].sValue
end
end
if DefaultStrategyParamList.ParameterList[i].sType == 'b' then
UpdatedParameters[DefaultStrategyParamList.ParameterList[i].sName] = xParameterValue == 'true' or xParameterValue == '1' or xParameterValue == true
elseif DefaultStrategyParamList.ParameterList[i].sType == 'd' then
if #DefaultStrategyParamList.ParameterList[i].sValue > 0 then
UpdatedParameters[DefaultStrategyParamList.ParameterList[i].sName] = tonumber( xParameterValue)
-- stringa vuota equivale a non passare alcun valore (deciderà la strategia)
else
UpdatedParameters[DefaultStrategyParamList.ParameterList[i].sName] = nil
end
else --DefaultStrategyParamList[i].sType == 's' or DefaultStrategyParamList[i].sType == 'combo'
UpdatedParameters[DefaultStrategyParamList.ParameterList[i].sName] = xParameterValue
end
end
end
return UpdatedParameters
end
-------------------------------------------------------------------------------------------------------------
-- si traduce la tabella dei parametri con tutte le informazioni in una lista contenente i parametri utilizzabili con accesso diretto
function BeamLib.LoadGeneralParametersInList( DefaultGeneralParamList)
local UpdatedParameters = { PROJECT = {}, BTL = {}}
-- aggiornamento parametri PROJECT
if DefaultGeneralParamList then
local idProjectInfo = EgtGetFirstNameInGroup( GDB_ID.ROOT, 'ProjectInfo') or GDB_ID.NULL
UpdatedParameters.PROJECT.sAISetupConfig = EgtGetInfo( idProjectInfo, 'AISETUP', 's') or nil
for i = 1, #DefaultGeneralParamList do
local sParamNameNGE = DefaultGeneralParamList[i].sNameNge
local xParameterValue = EgtGetInfo( idProjectInfo, sParamNameNGE, 's') or DefaultGeneralParamList[i].sValue
-- se parametro non trovato si salta tutto. N.B. = I parametri progetto non dovrebbero mai essere nil
if xParameterValue then
-- salvataggio dato su lista con accesso diretto
if DefaultGeneralParamList[i].sType == 'b' then
UpdatedParameters.PROJECT[DefaultGeneralParamList[i].sName] = xParameterValue == 'true' or xParameterValue == '1' or xParameterValue == true
elseif DefaultGeneralParamList[i].sType == 'd' then
if #DefaultGeneralParamList[i].sValue > 0 then
UpdatedParameters.PROJECT[DefaultGeneralParamList[i].sName] = tonumber( xParameterValue)
-- stringa vuota equivale a non passare alcun valore (deciderà la strategia)
else
UpdatedParameters.PROJECT[DefaultGeneralParamList[i].sName] = nil
end
else --DefaultGeneralParamList[i].sType == 's' or DefaultGeneralParamList[i].sType == 'combo'
UpdatedParameters.PROJECT[DefaultGeneralParamList[i].sName] = xParameterValue
end
end
end
-- aggiornamento parametri BTL
local BTLInfo = EgtGetNameInGroup( GDB_ID.ROOT, 'BtlInfo') or GDB_ID.NULL
for j = 1, #BTLInfo do
local BTLName = EgtGetInfo( BTLInfo[j], 'PROJ', 's')
if BTLName then
UpdatedParameters.BTL[BTLName] = {}
UpdatedParameters.BTL[BTLName].sAISetupConfig = EgtGetInfo( BTLInfo[j], 'AISETUP', 's') or nil
for i = 1, #DefaultGeneralParamList do
local sParamNameNGE = DefaultGeneralParamList[i].sNameNge
local xParameterValue = EgtGetInfo( BTLInfo[j], sParamNameNGE, 's')
-- se parametro non trovato si salta tutto
if xParameterValue then
-- salvataggio dato su lista con accesso diretto
if DefaultGeneralParamList[i].sType == 'b' then
UpdatedParameters.BTL[BTLName][DefaultGeneralParamList[i].sName] = xParameterValue == 'true' or xParameterValue == '1' or xParameterValue == true
elseif DefaultGeneralParamList[i].sType == 'd' then
if #DefaultGeneralParamList[i].sValue > 0 then
UpdatedParameters.BTL[BTLName][DefaultGeneralParamList[i].sName] = tonumber( xParameterValue)
-- stringa vuota equivale a non passare alcun valore (deciderà la strategia)
else
UpdatedParameters.BTL[BTLName][DefaultGeneralParamList[i].sName] = nil
end
else --DefaultGeneralParamList[i].sType == 's' or DefaultGeneralParamList[i].sType == 'combo'
UpdatedParameters.BTL[BTLName][DefaultGeneralParamList[i].sName] = xParameterValue
end
end
end
end
end
end
return UpdatedParameters
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.GetChainSawInitAngs( vtN, vtO, nInd)
if BeamData.GetChainSawInitAngs then
return BeamData.GetChainSawInitAngs( vtN, vtO, nInd)
else
if BeamData.C_SIMM then
return EgtIf( vtN:getY() > 0, 'C=180', 'C=-180')
else
if nInd == 1 then
return ''
else
return EgtIf( vtN:getY() > 0, 'C=180', 'C=-180')
end
end
end
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.GetBlockedAxis( nToolIndex, sBlockedAxis, b3Raw, vtTool, vtOut)
-- se presente funzione specifica nella macchina, la richiamo
if BeamData.GetBlockedAxis then
return BeamData.GetBlockedAxis( TOOLS[nToolIndex].sHead, TOOLS[nToolIndex].nTypeId, sBlockedAxis, b3Raw, vtTool, vtOut) or ''
-- sezione mantenuta per retrocompatibilità con GetChainSawBlockedAxis
elseif TOOLS[nToolIndex].nTypeId == MCH_TY.MORTISE_STD then
local nInd = EgtIf( sBlockedAxis == 'parallel', 0, 1)
if BeamData.GetChainSawBlockedAxis then
return BeamData.GetChainSawBlockedAxis( nInd)
else
if nInd == 1 then
return EgtIf( BeamData.C_SIMM, 'A=90', 'A=90')
else
return EgtIf( BeamData.C_SIMM, 'A=0', 'A=0')
end
end
end
return ''
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.StringReplaceChar( sOriginal, nPosition, sChar)
return sOriginal:sub( 1, nPosition-1) .. sChar .. sOriginal:sub( nPosition+1)
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.BinaryToDecimal( dNumber)
local sNumberToConvert = tostring( dNumber)
local dResult = 0
local k = 0
for i = #sNumberToConvert, 1, -1 do
k = k + 1
local n = string.sub(sNumberToConvert, k, k)
dResult = dResult + n*(2^(i-1))
end
return dResult
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.DecimalToBinary( dNumber)
local sNumberToConvert = tostring( dNumber)
local n = sNumberToConvert
local tmp = {}
local sResult = ""
for i = sNumberToConvert, 0, -1 do
local q = math.modf(n)
n = n/2
local b = q%2
table.insert(tmp, b)
if (q == 1) then
break
end
end
for i = #tmp, 1, -1 do
sResult = sResult..tmp[i]
end
return tonumber( sResult)
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.CalculateStringBinaryFormat( dNumber, CharNumber)
local NumberString = tostring( dNumber)
while #NumberString < CharNumber do
NumberString = '0' .. NumberString
end
return NumberString
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.ConvertBitIndexToRotationIndex( sBitIndexCombination)
local nRotationIndex
if sBitIndexCombination == '1000' then
return 1
elseif sBitIndexCombination == '0100' then
return 2
elseif sBitIndexCombination == '0010' then
return 3
elseif sBitIndexCombination == '0001' then
return 4
elseif sBitIndexCombination == '1000_INV' then
return 5
elseif sBitIndexCombination == '0100_INV' then
return 6
elseif sBitIndexCombination == '0010_INV' then
return 7
elseif sBitIndexCombination == '0001_INV' then
return 8
end
return nRotationIndex
end
-------------------------------------------------------------------------------------------------------------
-- reindicizza una tabella passata ripartendo dall'indice nStartIndex e mantenendo l'ordine
function BeamLib.RotateTableFromIndex( Table, nStartIndex)
local RotatedTable = {}
for i = 1, #Table do
RotatedTable[#RotatedTable + 1] = Table[((RotatedTable + i - 2) % #Table) + 1]
end
return RotatedTable
end
-------------------------------------------------------------------------------------------------------------
--- copia una tabella lua in modo ricorsivo, ossia mantiene indipendenti anche tutte le sottotabelle
--- ATTENZIONE: in caso di modifiche vanno gestiti anche i tipi custom; sarebbe meglio metterla nel LuaLibs
function BeamLib.TableCopyDeep( OriginalTable)
-- controllo se oggetto passato è valido, altrimenti errore. Non deve mai succedere
if not OriginalTable then
error( "TableCopyDeep : can't copy nil object")
end
local CopiedTable = {}
for key, value in pairs( OriginalTable) do
if type( value) == "table" then
if isBBox3d( value) then
CopiedTable[ key] = BBox3d( value)
elseif isColor3d( value) then
CopiedTable[ key] = Color3d( value)
elseif isFrame3d( value) then
CopiedTable[ key] = Frame3d( value)
elseif isPoint3d( value) then
CopiedTable[ key] = Point3d( value)
elseif isQuaternion( value) then
CopiedTable[ key] = Quaternion( value)
elseif isVector3d( value) then
CopiedTable[ key] = Vector3d( value)
else
CopiedTable[ key] = BeamLib.TableCopyDeep( value)
end
else
CopiedTable[ key] = value
end
end
return CopiedTable
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.MergeTables( OriginalTable, InputTable)
local result = {}
for k, v in pairs( OriginalTable) do result[k] = v end
for k, v in pairs( InputTable) do result[k] = v end
return result
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.FindEdgeBestOrientedAsDirection( Edges, vtDirection)
local BestEdge = {}
local dMaxDotProduct = GEO.INFINITO
for i = 1, #Edges do
local dCurrentDotProduct = ( Edges[i].vtN * vtDirection)
if ( i == 1) or ( dCurrentDotProduct > dMaxDotProduct + 10 * GEO.EPS_SMALL) then
dMaxDotProduct = dCurrentDotProduct
BestEdge = Edges[i]
end
end
return BestEdge
end
-------------------------------------------------------------------------------------------------------------
-- nNearSide : 3=Z+, -3=Z-
function BeamLib.PutStartNearestToEdge( nCrvId, b3Raw, dMaxDist, nNearSide)
-- verifico che la curva sia chiusa
if not EgtCurveIsClosed( nCrvId) then return false end
-- recupero il versore normale al piano di lavoro o estrusione
local vtN = EgtCurveExtrusion( nCrvId, GDB_ID.ROOT)
-- coefficienti per riportare la distanza nel piano di lavoro
local dCoeffY = 0
if abs( vtN:getY()) < 0.999 then dCoeffY= 1 / ( sqrt( 1 - vtN:getY() * vtN:getY())) end
local dCoeffZ = 0
if abs( vtN:getZ()) < 0.999 then dCoeffZ = 1 / ( sqrt( 1 - vtN:getZ() * vtN:getZ())) end
-- cerco l'estremo più vicino al box e lo imposto come inizio (se da sotto escludo Zmax e viceversa)
local dUopt = 0
local dDopt = GEO.INFINITO
local dSopt = GEO.INFINITO
local dUi, dUf = EgtCurveDomain( nCrvId)
for dU = dUi, dUf, 0.5 do
local ptP = EgtUP( nCrvId, dU, GDB_ID.ROOT)
local vtDp = EgtUV( nCrvId, dU, -1, GDB_ID.ROOT)
local vtDs = EgtUV( nCrvId, dU, 1, GDB_ID.ROOT)
local bTg = ( vtDp and vtDs and vtDp * vtDs > 0.96)
if ptP and bTg then
local vtMin = ptP - b3Raw:getMin()
local vtMax = ptP - b3Raw:getMax()
local dD = abs( vtMin:getY()) * dCoeffY
dD = min( abs( vtMax:getY()) * dCoeffY, dD)
dD = min( abs( vtMin:getZ()) * dCoeffZ, dD)
dD = min( abs( vtMax:getZ()) * dCoeffZ, dD)
local dS
if nNearSide == -3 then
dS = abs( vtMin:getZ() * dCoeffZ)
else --nNearSide == 3
dS = abs( vtMax:getZ() * dCoeffZ)
end
if dD < dMaxDist and dS < dSopt + GEO.EPS_SMALL then
dUopt = dU
dDopt = dD
dSopt = dS
end
end
end
if abs( dUopt - dUi) > GEO.EPS_ZERO and abs( dUopt - dUf) > GEO.EPS_ZERO then
-- TODO. Non utilizzare la funzione EgtChangeClosedCurveStart perchè cambia proprietà della geometria.
-- dopo aver trovato l'entità di partenza, calcolare l'offset da aggiungere alla lavorazione per poter cominciare da questa entità (senza modifica della geometria)
EgtChangeClosedCurveStart( nCrvId, dUopt)
end
return true
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.ImportFileJSON( sFileToImport)
local JSON = require( 'JSON')
local function readAll( sFileToImport)
local f = io.open( sFileToImport, "rb")
local content = f:read( "*all")
f:close()
return content
end
local content = readAll( sFileToImport)
return JSON:decode( content)
end
-------------------------------------------------------------------------------------------------------------
function BeamLib.GetStrategiesFromList( Proc, StrategyList)
-- cerco tra le feature
for i = 1, #StrategyList.FEATURE do
-- se trovo la feature
if Proc.nPrc == StrategyList.FEATURE[i].nPrc then
local bGroupIsCompatible = false
-- nel JSON il gruppo è sempre 0/1, mente da BTL possono arrivare altri valori.
if StrategyList.FEATURE[i].nGrp == 0 then
if Proc.nGrp == 0 or Proc.nGrp == 3 or Proc.nGrp == 4 then
bGroupIsCompatible = true
end
elseif StrategyList.FEATURE[i].nGrp == 1 then
if Proc.nGrp == 1 or Proc.nGrp == 2 then
bGroupIsCompatible = true
end
end
-- si controlla gruppo
if bGroupIsCompatible then
-- cerco tra le topologie
for j = 1, #StrategyList.FEATURE[i].TopologyList do
-- non si possono mischiare topologie specifiche con topologia 'Feature'. Se presente topologia 'Feature' allora deve essere esclusiva
if StrategyList.FEATURE[i].TopologyList[j].sName == 'Feature' and #StrategyList.FEATURE[i].TopologyList > 1 then
error( "UNEXPECTED ERROR: topology 'Feature' can't be mixed with others")
end
-- se trovo la topologia oppure se è 'Feature'
if Proc.Topology.sName == StrategyList.FEATURE[i].TopologyList[j].sName or StrategyList.FEATURE[i].TopologyList[j].sName == 'Feature' then
local ActiveStrategyList = {}
-- aggiungo in lista solo le strategie attive
if StrategyList.FEATURE[i].TopologyList[j].StrategyList then
for k = 1, #StrategyList.FEATURE[i].TopologyList[j].StrategyList do
if ( not StrategyList.FEATURE[i].TopologyList[j].StrategyList[k].nIndexInList) or ( StrategyList.FEATURE[i].TopologyList[j].StrategyList[k].nIndexInList) >= 0 then
table.insert( ActiveStrategyList, BeamLib.TableCopyDeep( StrategyList.FEATURE[i].TopologyList[j].StrategyList[k]))
end
end
end
-- ritorno le strategie disponibili per la feature che sto analizzando, altrimenti nil se non ho trovato nulla
return EgtIf( #ActiveStrategyList > 0, ActiveStrategyList, nil)
end
end
end
end
end
return nil
end
-------------------------------------------------------------------------------------------------------------
-- Merge sorting - algoritmo di sorting stabile, ossia che mantiene l'ordine relativo se gli elementi sono equivalenti
-- TODO vedere come riordinare (tutto in tabella MergeSort??)
-- TODO libreria Sorting??
do
-- unisce due liste ordinate
local function MergeHalves(Left, Right, Compare)
local result = {}
local i, j = 1, 1
while i <= #Left and j <= #Right do
if Compare( Left[i], Right[j]) then
table.insert( result, Left[i])
i = i + 1
else
table.insert( result, Right[j])
j = j + 1
end
end
-- Append degli elementi rimanenti a sinistra o destra
while i <= #Left do
table.insert( result, Left[i])
i = i + 1
end
while j <= #Right do
table.insert( result, Right[j])
j = j + 1
end
return result
end
-- Merge sort
local function MergeSort( List, Compare)
if #List <= 1 then
return List
end
local mid = floor( #List / 2)
local Left = MergeSort( { table.unpack( List, 1, mid)}, Compare)
local Right = MergeSort( { table.unpack( List, mid + 1)}, Compare)
return MergeHalves( Left, Right, Compare)
end
-- chiamata accessibile dall'esterno
function BeamLib.StableSort( List, Compare)
return MergeSort( List, Compare)
end
end
-------------------------------------------------------------------------------------------------------------
return BeamLib