-- NestProcess.lua by Egaltech s.r.l. 2022/11/24 -- Gestione nesting automatico pareti -- Intestazioni require( 'EgtBase') _ENV = EgtProtectGlobal() EgtEnableDebug( false) -- Per test --NEST = {} --NEST.FILE = 'c:\\TechnoEssetre7\\EgtData\\Prods\\0010\\Bar_10_1.btl' --NEST.MACHINE = 'Essetre-90480019_MW' --NEST.FLAG = 3 --NEST.MIN_ANGLE_PNT = 60 --NEST.DRILL_MACH_AREA = 0 NEST.LAP_JOINT_U_MACH_AREA = 1 -- 0 NEST.MACH_AREA_USE_OTHER_DIAM = 1 -- 0 NEST.MACH_AREA_OTHER_DIAM = 200 NEST.MACH_AREA_IGNORE_3rdFACE = 1 -- 0 if NEST.FLAG ~= 11 then local sLog = 'NestProcess : ' .. NEST.FILE .. ', ' .. NEST.MACHINE .. ', ' .. LEN[1] .. ', ' .. WIDTH[1] EgtOutLog( sLog) -- Cancello file di log specifico local sLogFile = EgtChangePathExtension( NEST.FILE, '.txt') EgtEraseFile( sLogFile) end -- Imposto direttorio libreria specializzata per Travi EgtAddToPackagePath( NEST.BASEDIR .. '\\LuaLibs\\?.lua') -- Verifico che la macchina corrente sia abilitata per la lavorazione delle Pareti local sMachDir = EgtGetCurrMachineDir() if not EgtExistsFile( sMachDir .. '\\Wall\\WallData.lua') then NEST.ERR = 12 NEST.MSG = 'Error not configured for walls machine : ' .. sMachine WriteErrToLogFile( NEST.ERR, NEST.MSG) PostErrView( NEST.ERR, NEST.MSG) return end -- Elimino direttori altre macchine e imposto direttorio macchina corrente per ricerca librerie EgtRemoveBaseMachineDirFromPackagePath() EgtAddToPackagePath( sMachDir .. '\\Wall\\?.lua') -- Carico le librerie _G.package.loaded.WallExec = nil local WE = require( 'WallExec') _G.package.loaded.WallLib = nil local WL = require( 'WallLib') _G.package.loaded.WProcessLapJoint = nil local LapJoint = require( 'WProcessLapJoint') _G.package.loaded.WProcessDrill = nil local Drill = require( 'WProcessDrill') _G.package.loaded.WProcessDoubleCut = nil local DoubleCut = require( 'WProcessDoubleCut') _G.package.loaded.WProcessFreeContour = nil local FreeContour = require( 'WProcessFreeContour') _G.package.loaded.PanelSaw = nil local PanelSaw = require( 'PanelSaw') -- Carico i dati globali local WD = require( 'WallData') -- Recupero alcuni dati dal WallData o li setto a valori di default local s_dSideMillDiamDown = WD.SIDEMILL_DIAM_DOWN or 350 local s_dSideMillDiamUp = WD.SIDEMILL_DIAM_UP or 65 local s_dHorDrillLen = WD.HOR_DRILL_LEN or 1780 local s_dHorDrillDiam = WD.HOR_DRILL_DIAM or 35 local s_dHorDrillDiam5Axes = WD.HOR_DRILL_DIAM_5AX or 0 local s_sOrigCorner = WD.ORIG_CORNER or 'TL' local s_dIntRulli = WD.INTRULLI or 1200 local s_dMinRawYHorDrill = WD.MINRAWY_HOR_DRILL or 2800 local s_dNestHoleMinArea = WD.NEST_HOLE_MIN_AREA or 200000 local s_dInsideRawTol = WD.INSIDE_RAW_TOL or 30 -- lista dei pezzi con flip o rotazione local PartStates = {} ------------------------------------------------------------------ -- Funzioni per scrittura su file di log specifico local function WriteErrToLogFile( nErr, sMsg, nRot, nCutId, nTaskId) local hFile = io.open( sLogFile, 'a') hFile:write( 'ERR=' .. tostring( nErr) .. '\n') hFile:write( sMsg .. '\n') hFile:write( 'ROT=' .. tostring( nRot or 0) .. '\n') hFile:write( 'CUTID=' .. tostring( nCutId or 0) .. '\n') hFile:write( 'TASKID=' .. tostring( nTaskId or 0) .. '\n') hFile:close() end ------------------------------------------------------------------ local function WriteTimeToLogFile( dTime) local hFile = io.open( sLogFile, 'a') hFile:write( 'TIME=' .. EgtNumToString( dTime) .. '\n') hFile:close() end ------------------------------------------------------------------ -- Funzione per gestire visualizzazione dopo errore local function PostErrView( nErr, sMsg) if nErr ~= 0 and ( NEST.FLAG == 1 or NEST.FLAG == 2 or NEST.FLAG == 5) then EgtSetView( SCE_VD.ISO_SW, false) EgtZoom( SCE_ZM.ALL) EgtOutBox( sMsg, 'BatchProcess (err=' .. tostring( nErr) .. ')', 'ERRORS') end end ------------------------------------------------------------------ -- Funzione per gestire visualizzazione dopo warning local function PostWarnView( nWarn, sMsg) if nWarn ~= 0 and ( NEST.FLAG == 1 or NEST.FLAG == 2 or NEST.FLAG == 5) then EgtSetView( SCE_VD.ISO_SW, false) EgtZoom( SCE_ZM.ALL) EgtOutBox( sMsg, 'BatchProcess (wrn=' .. tostring( nWarn) .. ')', 'WARNINGS') end end ------------------------------------------------------------------ -- Funzione per aggiornare dati ausiliari local function UpdateAuxData( sAuxFile) local bModif = false -- Se definito LOAD90, aggiorno local sLoad90 = EgtGetStringFromIni( 'AuxData', 'LOAD90', '', sAuxFile) if sLoad90 ~= '' then local BtlInfoId = EgtGetFirstNameInGroup( GDB_ID.ROOT, 'BtlInfo') or GDB_ID.NULL EgtSetInfo( BtlInfoId, 'LOAD90', sLoad90) bModif = true end return bModif end ------------------------------------------------------------------ -- Funzione per trovare nome MachGroup local function NewMachGroupName() local nMachGroupId = EgtGetFirstMachGroup() if not nMachGroupId then return 1 end local nMaxMachGroup = 0 while nMachGroupId do sMachGroupName = EgtGetMachGroupName(nMachGroupId) local nMachGroupName = tonumber(sMachGroupName) if nMachGroupName > nMaxMachGroup then nMaxMachGroup = nMachGroupName end nMachGroupId = EgtGetNextMachGroup(nMachGroupId) end return nMaxMachGroup + 1 end ------------------------------------------------------------------ -- Funzione che crea il rettangolo della lavorazione local function CreateToolRectangle( ptP1, dValP1, ptP2, dValP2, vtFace, dValFace, nOutlineGrp) local dExtra = 5 local vtX = ptP2 - ptP1 vtX:normalize() -- creo il rettangolo della lavorazione ptP1 = ptP1 - vtX * ( dValP1 + dExtra) ptP2 = ptP2 + vtX * ( dValP2 + dExtra) ptP2 = ptP2 + vtFace * ( dValFace + dExtra) --local nId = EgtRectangle2P( nOutlineGrp, Point3d(ptP2:getX(), ptP2:getY(), 0), Point3d( ptP1:getX(), ptP1:getY(), 0)) local nId = EgtRectangle2P( nOutlineGrp, ptP2, ptP1) return nId end ------------------------------------------------------------------ -- Funzione che verifica se la lavorazione è lap joint dal basso ed eventualmente ne calcola l'area di lavorazione local function IdentifyLJFromBottom( Proc, bCompute, nOutlineGrp) local bLJFromBottom = false local nRectId local vtFace local dMinCompZ = 0.95 if Proc.Fct == 2 then local vtN = {} vtN[1] = EgtSurfTmFacetNormVersor( Proc.Id, 0, GDB_ID.ROOT) vtN[2] = EgtSurfTmFacetNormVersor( Proc.Id, 1, GDB_ID.ROOT) -- se rivolto verso il basso if ( vtN[1]:getZ() < - dMinCompZ or vtN[2]:getZ() < - dMinCompZ) then vtFace = EgtIf( vtN[1]:getZ() < - dMinCompZ, vtN[2], vtN[1]) if not ( vtFace:getZ() > dMinCompZ or vtFace:getZ() < - dMinCompZ) then bLJFromBottom = true if bCompute then local bAdj, ptP1, ptP2, dAng = EgtSurfTmFacetsContact( Proc.Id, 0, 1, GDB_ID.ROOT) -- creo il rettangolo della lavorazione nRectId = CreateToolRectangle( ptP1, s_dSideMillDiamDown / 2, ptP2, s_dSideMillDiamDown / 2, vtFace, s_dSideMillDiamDown, nOutlineGrp) end end end elseif Proc.Fct == 3 then local nFacInd, dElev, nFacInd2 = WL.GetFaceWithMostAdj( Proc.Id, Proc.PartId) -- se nel mezzo di una faccia if not nFacInd2 then if nFacInd ~= -2 and nFacInd ~= GDB_ID.NULL then vtFace = EgtSurfTmFacetNormVersor( Proc.Id, nFacInd, GDB_ID.ROOT) if not ( vtFace:getZ() > dMinCompZ or vtFace:getZ() < - dMinCompZ) then local nOtherFace = EgtIf( nFacInd == 0, 1, 0) local bAdj, ptP1, ptP2, dAng = EgtSurfTmFacetsContact( Proc.Id, nFacInd, nOtherFace, GDB_ID.ROOT) if abs( ptP1:getZ() - ptP2:getZ()) < GEO.EPS_SMALL then bLJFromBottom = true if bCompute then nRectId = CreateToolRectangle( ptP1, s_dSideMillDiamDown / 2, ptP2, s_dSideMillDiamDown / 2, vtFace, s_dSideMillDiamDown, nOutlineGrp) end end end end -- se dal basso else local nFaceZ = -1 for nIdx = 0, 2 do local vtN2 = EgtSurfTmFacetNormVersor( Proc.Id, nIdx, GDB_ID.ROOT) if vtN2:getZ() < - dMinCompZ then nFaceZ = nIdx break end end if nFaceZ ~= -1 then local nFace = EgtIf( nFaceZ == nFacInd, nFacInd2, nFacInd) local nOtherFace = EgtIf( nFaceZ + nFace == 3, 0, EgtIf( nFaceZ + nFace == 2, 1, 2)) vtFace = EgtSurfTmFacetNormVersor( Proc.Id, nFace, GDB_ID.ROOT) --vtFace[2] = EgtSurfTmFacetNormVersor( Proc.Id, nOtherFace, GDB_ID.ROOT) bLJFromBottom = true if bCompute then local bAdj, ptP1, ptP2 = EgtSurfTmFacetsContact( Proc.Id, nFace, nFaceZ, GDB_ID.ROOT) local bAdj2, ptP3, ptP4 = EgtSurfTmFacetsContact( Proc.Id, nFace, nOtherFace, GDB_ID.ROOT) if AreSamePointApprox( ptP2, ptP3) or AreSamePointApprox( ptP2, ptP4) then ptP1, ptP2 = ptP2, ptP1 end local dVal = EgtIf( NEST.MACH_AREA_IGNORE_3rdFACE == 1, s_dSideMillDiamDown / 2, 0) nRectId = CreateToolRectangle( ptP1, dVal, ptP2, s_dSideMillDiamDown / 2, vtFace, s_dSideMillDiamDown, nOutlineGrp) end end end elseif Proc.Fct == 4 then local nFacInd, dElev, nFacInd2, dElev2 = WL.GetFaceWithMostAdj( Proc.Id, Proc.PartId) if nFacInd ~= -2 and nFacInd ~= GDB_ID.NULL then vtFace = EgtSurfTmFacetNormVersor( Proc.Id, nFacInd, GDB_ID.ROOT) if not ( vtFace:getZ() > dMinCompZ or vtFace:getZ() < - dMinCompZ) then local nOtherFace = -1 for nIdx = 0, 3 do local vtN2 = EgtSurfTmFacetNormVersor( Proc.Id, nIdx, GDB_ID.ROOT) if vtN2:getZ() > dMinCompZ or vtN2:getZ() < - dMinCompZ then nOtherFace = nIdx break end end if nOtherFace ~= -1 and nOtherFace ~= nFacInd and nOtherFace ~= nFacInd2 then bLJFromBottom = true if bCompute then local bAdj, ptP1, ptP2 = EgtSurfTmFacetsContact( Proc.Id, nFacInd, nOtherFace, GDB_ID.ROOT) local bAdj2, ptP3, ptP4 = EgtSurfTmFacetsContact( Proc.Id, nFacInd, nFacInd2, GDB_ID.ROOT) if AreSamePointApprox( ptP2, ptP3) or AreSamePointApprox( ptP2, ptP4) then ptP1, ptP2 = ptP2, ptP1 end nRectId = CreateToolRectangle( ptP1, 0, ptP2, s_dSideMillDiamDown / 2, vtFace, s_dSideMillDiamDown, nOutlineGrp) end end end if nFacInd2 and nFacInd2 ~= GDB_ID.NULL then local vtFace2 = EgtSurfTmFacetNormVersor( Proc.Id, nFacInd2, GDB_ID.ROOT) if ( vtFace2:getZ() < - dMinCompZ or vtFace:getZ() < - dMinCompZ) then bLJFromBottom = true if vtFace:getZ() < -dMinCompZ then vtFace = vtFace2 end if bCompute then local bAdj, ptP1, ptP2 = EgtSurfTmFacetsContact( Proc.Id, nFacInd, nFacInd2, GDB_ID.ROOT) nRectId = CreateToolRectangle( ptP1, 0, ptP2, 0, vtFace, s_dSideMillDiamDown, nOutlineGrp) end end end end end if bLJFromBottom then return vtFace, nRectId else return end end ------------------------------------------------------------------ -- Funzione che verifica se la lavorazione è lap joint da sopra ed eventualmente ne calcola l'area di lavorazione local function IdentifyLJFromTop( Proc, bCompute, nOutlineGrp) local bLJFromTop = false local vtFace, nRectId if s_dSideMillDiamUp < GEO.EPS_SMALL then return end local dMinComp = 0.95 if Proc.Fct == 2 then local vtN = {} vtN[1] = EgtSurfTmFacetNormVersor( Proc.Id, 0, GDB_ID.ROOT) vtN[2] = EgtSurfTmFacetNormVersor( Proc.Id, 1, GDB_ID.ROOT) -- se rivolto verso l'alto if (vtN[1]:getZ() > dMinComp or vtN[2]:getZ() > dMinComp) then bLJFromTop = true vtFace = EgtIf( vtN[1]:getZ() > dMinComp, vtN[2], vtN[1]) if bCompute then local bAdj, ptP1, ptP2, dAng = EgtSurfTmFacetsContact( Proc.Id, 0, 1, GDB_ID.ROOT) local b3Face = EgtSurfTmGetFacetBBoxGlob( Proc.Id, EgtIf( vtN[1]:getZ() > dMinComp, 0, 1), GDB_BB.STANDARD) local dDim = 0 if vtFace:getX() > dMinComp or vtFace:getX() < - dMinComp then dDim = b3Face:getDimX() elseif vtFace:getY() > dMinComp or vtFace:getY() < - dMinComp then dDim = b3Face:getDimY() end local dValFace = max( dDim + s_dSideMillDiamUp / 2, s_dSideMillDiamUp) local dVal = s_dSideMillDiamUp / 2 if NEST.MACH_AREA_USE_OTHER_DIAM == 1 then dVal = NEST.MACH_AREA_OTHER_DIAM / 2 end nRectId = CreateToolRectangle( ptP1, dVal, ptP2, dVal, vtFace, dValFace, nOutlineGrp) end end -- caso 3 facce elseif Proc.Fct == 3 then local nFacInd, dElev, nFacInd2 = WL.GetFaceWithMostAdj( Proc.Id, Proc.PartId) -- forma ad U if not nFacInd2 and nFacInd ~= GDB_ID.NULL and NEST.LAP_JOINT_U_MACH_AREA == 1 then local vtFace = EgtSurfTmFacetNormVersor( Proc.Id, nFacInd, GDB_ID.ROOT) if vtFace:getZ() > dMinComp then bLJFromTop = true if bCompute then local b3Face = EgtSurfTmGetFacetBBoxGlob( Proc.Id, nFacInd, GDB_BB.STANDARD) local nOtherFace = EgtIf( nFacInd == 0, 1, 0) local vtNOther = EgtSurfTmFacetNormVersor( Proc.Id, nOtherFace, GDB_ID.ROOT) local vtDir = EgtIf( AreSameOrOppositeVectorApprox( vtNOther, X_AX()), Y_AX(), X_AX()) local dVal = s_dSideMillDiamUp / 2 + 5 local ptMin = b3Face:getMin() - dVal * vtDir local ptMax = b3Face:getMax() + dVal * vtDir -- nRectId = EgtRectangle2P( nOutlineGrp, Point3d(ptMax:getX(), ptMax:getY(), 0), Point3d( ptMin:getX(), ptMin:getY(), 0)) nRectId = EgtRectangle2P( nOutlineGrp, ptMax, ptMin) end end elseif nFacInd2 then local nFaceZ = -1 for nIdx = 0, 2 do local vtN2 = EgtSurfTmFacetNormVersor( Proc.Id, nIdx, GDB_ID.ROOT) if vtN2:getZ() > dMinComp then nFaceZ = nIdx break end end if nFaceZ ~= -1 then local nFace = EgtIf( nFaceZ == nFacInd, nFacInd2, nFacInd) local nOtherFace = EgtIf( nFaceZ + nFace == 3, 0, EgtIf( nFaceZ + nFace == 2, 1, 2)) local vtFace = {} vtFace = EgtSurfTmFacetNormVersor( Proc.Id, nFace, GDB_ID.ROOT) --vtFace = EgtSurfTmFacetNormVersor( Proc.Id, nOtherFace, GDB_ID.ROOT) bLJFromTop = true if bCompute then local bAdj, ptP1, ptP2 = EgtSurfTmFacetsContact( Proc.Id, nFace, nFaceZ, GDB_ID.ROOT) local bAdj2, ptP3, ptP4 = EgtSurfTmFacetsContact( Proc.Id, nFace, nOtherFace, GDB_ID.ROOT) if AreSamePointApprox( ptP2, ptP3) or AreSamePointApprox( ptP2, ptP4) then ptP1, ptP2 = ptP2, ptP1 end local b3Face = EgtSurfTmGetFacetBBoxGlob( Proc.Id, nFaceZ, GDB_BB.STANDARD) local dDim = 0 if vtFace:getX() > dMinComp or vtFace:getX() < - dMinComp then dDim = b3Face:getDimX() elseif vtFace:getY() > dMinComp or vtFace:getY() < - dMinComp then dDim = b3Face:getDimY() end local dValFace = max( dDim + s_dSideMillDiamUp / 2, s_dSideMillDiamUp) local dVal = s_dSideMillDiamUp / 2 if NEST.MACH_AREA_USE_OTHER_DIAM == 1 then dVal = NEST.MACH_AREA_OTHER_DIAM / 2 end nRectId = CreateToolRectangle( ptP1, 0, ptP2, dVal, vtFace, dValFace, nOutlineGrp) end end end end if bLJFromTop then return vtFace, nRectId else return end end ------------------------------------------------------------------ -- Funzione che crea le regioni occupate dalle lavorazioni local function ComputeToolOutlines( nPartId) local ToolOutlineId = {} EgtRemoveInfo( nPartId, "ToolOutlines") -- recupero il gruppo con gli outlines degli utensili local nOutlineGrp = EgtGetFirstNameInGroup(GDB_ID.ROOT, "ToolOutlines") if not nOutlineGrp or nOutlineGrp == GDB_ID.NULL then nOutlineGrp = EgtGroup( GDB_ID.ROOT) EgtSetName( nOutlineGrp, "ToolOutlines") EgtSetStatus( nOutlineGrp, GDB_ST.OFF) end local vPartProc = WE.CollectFeatures( nPartId, b3Raw) for nInd = 1, #vPartProc do local Proc = vPartProc[nInd] -- lap joint if LapJoint.Identify( Proc) then -- verifico se dal basso local _ , nRectId = IdentifyLJFromBottom( Proc, true, nOutlineGrp) if nRectId then EgtSetColor( nRectId, EgtStdColor("BLUE")) table.insert( ToolOutlineId, nRectId) else -- verifico se dall'alto local _ , nRectId = IdentifyLJFromTop( Proc, true, nOutlineGrp) if nRectId then EgtSetColor( nRectId, EgtStdColor("AQUA")) local nPrId = EgtGetInfo( Proc.Id, "PRID", 'i') EgtSetInfo( nRectId, "PRID", nPrId) table.insert( ToolOutlineId, nRectId) end end end -- fori if NEST.DRILL_MACH_AREA == 1 and Drill.Identify( Proc) then -- recupero dati del foro local AuxId = EgtGetInfo( Proc.Id, 'AUXID', 'i') or 0 if AuxId then AuxId = AuxId + Proc.Id end local dLen = abs( EgtCurveThickness( AuxId)) local vtExtr = EgtCurveExtrusion( AuxId, GDB_RT.GLOB) local bOpen = ( Proc.Fcs ~= 0 and Proc.Fce ~= 0) local dDiam = 2 * EgtArcRadius( AuxId) -- verifico se la lunghezza del foro è maggiore della lunghezza della punta if bOpen and AreSameOrOppositeVectorApprox( vtExtr, Y_AX()) and dLen > s_dHorDrillLen - 1 then local ptP1 = Proc.Box:getMin() local ptP2 = Proc.Box:getMax() local dExtra = 10 ptP1 = ptP1 - ( WD.MAX_WIDTH - dLen + dExtra) * Y_AX() ptP2 = ptP2 + ( WD.MAX_WIDTH - dLen + dExtra) * Y_AX() --local nId = EgtRectangle2P( nOutlineGrp, Point3d(ptP2:getX(), ptP2:getY(), 0), Point3d( ptP1:getX(), ptP1:getY(), 0)) local nId = EgtRectangle2P( nOutlineGrp, ptP2, ptP1) EgtSetColor( nId, EgtStdColor("AQUA")) if nId ~= GDB_ID.NULL then table.insert(ToolOutlineId, nId) end end end -- lati inclinati free contour if FreeContour.Identify( Proc) then local bPocket = EgtGetInfo( Proc.Id, 'PCKT', 'b') if not bPocket then for nInd = 0, Proc.Fct - 1 do local vtN = EgtSurfTmFacetNormVersor( Proc.Id, nInd, GDB_ID.ROOT) -- se inclinato if abs( vtN:getZ()) > GEO.EPS_SMALL then local bUnderCut = vtN:getZ() < - GEO.EPS_SMALL local dCosAlpha = (vtN ^ Z_AX()):len() local dVal if bUnderCut then dVal = ( NEST.OFFSET + 0.1) / dCosAlpha else dVal = ( NEST.OFFSET + 0.1) * dCosAlpha end local frLoc, dDimX, dDimY = EgtSurfTmFacetMinAreaRectangle( Proc.Id, nInd, GDB_ID.ROOT) local b3Face = EgtSurfTmGetFacetBBoxRef( Proc.Id, nInd, GDB_BB.STANDARD, frLoc) local ptA = b3Face:getMin() local ptC = b3Face:getMax() local ptB = ptA + dDimY * Y_AX() local ptD = ptA + dDimX * X_AX() ptA:toGlob( frLoc) ptB:toGlob( frLoc) ptC:toGlob( frLoc) ptD:toGlob( frLoc) local ptP1, ptP2 if bUnderCut then ptP1 = EgtIf( ptA:getZ() > ptC:getZ(), ptA, ptC) ptP2 = EgtIf( ptB:getZ() > ptD:getZ(), ptB, ptD) else ptP1 = EgtIf( ptA:getZ() < ptC:getZ(), ptA, ptC) ptP2 = EgtIf( ptB:getZ() < ptD:getZ(), ptB, ptD) end local vtDir = ptP2 - ptP1 vtDir:normalize() ptP1 = ptP1 - vtDir * 0.5 * NEST.OFFSET ptP2 = ptP2 + vtDir * 0.5 * NEST.OFFSET local vtNxy = Vector3d( vtN:getX(), vtN:getY(), 0) vtNxy:normalize() local ptP4 = ptP2 + dVal * vtNxy local nRectId = EgtRectangle3P( nOutlineGrp, ptP1, ptP4, ptP2) EgtSetColor( nRectId, EgtStdColor("AQUA")) if nRectId ~= GDB_ID.NULL then table.insert( ToolOutlineId, nRectId) end end end end end end return ToolOutlineId end ------------------------------------------------------------------ local function ClassifyDrillsOnLateralFaces( nPartId, dMinSheetWidth) local dDeltaRaw = WD.MAX_WIDTH - dMinSheetWidth + NEST.KERF local DrillClassif local DrillOnFaces = { F = {nbr = 0, closed = false, long = false, OnlyOnRef = false}, B = {nbr = 0, closed = false, long = false, OnlyOnRef = false}, L = {nbr = 0, closed = false, long = false, OnlyOnRef = false}, R = {nbr = 0, closed = false, long = false, OnlyOnRef = false}} local nBoxLayerId = EgtGetFirstNameInGroup( nPartId, "Box") local nBoxId = EgtGetFirstNameInGroup( nBoxLayerId, "Box") local b3Part = EgtGetBBoxGlob(nBoxId, GDB_BB.STANDARD) local ptPartMin = b3Part:getMin() local ptPartMax = b3Part:getMax() local vPartProc = WE.CollectFeatures( nPartId) for nInd = 1, #vPartProc do repeat if Drill.Identify( vPartProc[nInd]) then -- identifico su quali facce si trova il foro local bOpen = ( vPartProc[nInd].Fcs ~= 0 and vPartProc[nInd].Fce ~= 0) local AuxId = EgtGetInfo( vPartProc[nInd].Id, 'AUXID', 'i') or 0 if AuxId then AuxId = AuxId + vPartProc[nInd].Id end if not AuxId or EgtGetType( AuxId) ~= GDB_TY.CRV_ARC then break end -- verifico se diametro compatibile con la punta local dDiam = 2 * EgtArcRadius( AuxId) if ( abs( dDiam - s_dHorDrillDiam5Axes) < WD.DRILL_TOL + GEO.EPS_SMALL) or ( abs( dDiam - s_dHorDrillDiam) < WD.DRILL_TOL + GEO.EPS_SMALL) then local ptDrill = EgtCP( AuxId, GDB_RT.GLOB) local dLen = abs( EgtCurveThickness( AuxId)) local bLong = dLen > s_dHorDrillLen - 1 local bOnlyOnRef = dLen + dDeltaRaw > s_dHorDrillLen - GEO.EPS_SMALL and not bLong -- faccia Front local dTol = GEO.EPS_SMALL if ( abs( ptDrill:getY() - ptPartMin:getY()) < dTol) then DrillOnFaces.F.nbr = DrillOnFaces.F.nbr + 1 if bOnlyOnRef then DrillOnFaces.F.OnlyOnRef = true end if bOpen then DrillOnFaces.B.nbr = DrillOnFaces.B.nbr + 1 if bOnlyOnRef then DrillOnFaces.B.OnlyOnRef = true end else DrillOnFaces.F.closed = true end if bLong then DrillOnFaces.F.long = true if bOpen then DrillOnFaces.B.long = true end end -- faccia Back elseif ( abs( ptDrill:getY() - ptPartMax:getY()) < dTol) then DrillOnFaces.B.nbr = DrillOnFaces.B.nbr + 1 if bOnlyOnRef then DrillOnFaces.B.OnlyOnRef = true end if bOpen then DrillOnFaces.F.nbr = DrillOnFaces.F.nbr + 1 if bOnlyOnRef then DrillOnFaces.F.OnlyOnRef = true end else DrillOnFaces.B.closed = true end if bLong then DrillOnFaces.B.long = true if bOpen then DrillOnFaces.F.long = true end end -- faccia Left elseif ( abs( ptDrill:getX() - ptPartMin:getX()) < dTol) then DrillOnFaces.L.nbr = DrillOnFaces.L.nbr + 1 if bOnlyOnRef then DrillOnFaces.L.OnlyOnRef = true end if bOpen then DrillOnFaces.R.nbr = DrillOnFaces.R.nbr + 1 if bOnlyOnRef then DrillOnFaces.R.OnlyOnRef = true end else DrillOnFaces.L.closed = true end if bLong then DrillOnFaces.L.long = true if bOpen then DrillOnFaces.R.long = true end end -- faccia Right elseif ( abs( ptDrill:getX() - ptPartMax:getX()) < dTol) then DrillOnFaces.R.nbr = DrillOnFaces.R.nbr + 1 if bOnlyOnRef then DrillOnFaces.R.OnlyOnRef = true end if bOpen then DrillOnFaces.L.nbr = DrillOnFaces.L.nbr + 1 if bOnlyOnRef then DrillOnFaces.L.OnlyOnRef = true end else DrillOnFaces.R.closed = true end if bLong then DrillOnFaces.R.long = true if bOpen then DrillOnFaces.L.long = true end end end end end until true end -- identifico la faccia da mettere sul lato del grezzo local sDrillFace, nMax = '', -100 for k, v in pairs( DrillOnFaces) do if v.nbr > nMax or ( v.nbr == nMax and not DrillOnFaces[sDrillFace].OnlyOnRef and v.OnlyOnRef) then sDrillFace, nMax = k, v.nbr end end local sDrillFace2 if nMax > 0 then if sDrillFace == 'F' or sDrillFace == 'B' then -- cerco chi vince tra faccia left e right if DrillOnFaces.L.nbr > DrillOnFaces.R.nbr then sDrillFace2 = 'L' elseif DrillOnFaces.L.nbr < DrillOnFaces.R.nbr then sDrillFace2 = 'R' elseif DrillOnFaces.L.nbr == DrillOnFaces.R.nbr and DrillOnFaces.L.nbr > 0 then if DrillOnFaces.L.OnlyOnRef then sDrillFace2 = 'L' else sDrillFace2 = 'R' end end else -- cerco chi vince tra faccia front e back if DrillOnFaces.F.nbr > DrillOnFaces.B.nbr then sDrillFace2 = 'F' elseif DrillOnFaces.F.nbr < DrillOnFaces.B.nbr then sDrillFace2 = 'B' elseif DrillOnFaces.F.nbr == DrillOnFaces.B.nbr and DrillOnFaces.F.nbr > 0 then if DrillOnFaces.F.OnlyOnRef then sDrillFace2 = 'F' else sDrillFace2 = 'B' end end end end if NEST.DRILL_MACH_AREA == 0 then DrillOnFaces[sDrillFace].long = false end if nMax > 0 then DrillClassif = { sCase = sDrillFace, bClosed = DrillOnFaces[sDrillFace].closed, bLong = DrillOnFaces[sDrillFace].long, bOnlyOnRef = DrillOnFaces[sDrillFace].OnlyOnRef} end local DrillClassif2 if sDrillFace2 then if NEST.DRILL_MACH_AREA == 0 then DrillOnFaces[sDrillFace2].long = false end DrillClassif2 = { sCase = sDrillFace2, bClosed = DrillOnFaces[sDrillFace2].closed, bLong = DrillOnFaces[sDrillFace2].long, bOnlyOnRef = DrillOnFaces[sDrillFace2].OnlyOnRef} end return DrillClassif, DrillClassif2 end ------------------------------------------------------------------ local function ClassifyLapJointsFromBottom( nPartId) local res = { Nbr = 0, sCase = '', bParall = false} local b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) local ptPartMin = b3Part:getMin() local ptPartMax = b3Part:getMax() local vtLapJoints = {} local dMinComponentVal = 0.95 local vPartProc = WE.CollectFeatures( nPartId) for nInd = 1, #vPartProc do if LapJoint.Identify( vPartProc[nInd]) then -- verifico se LJ da sotto local vtFace = IdentifyLJFromBottom( vPartProc[nInd], false) if vtFace then if vtFace:getX() > dMinComponentVal then vtLapJoints["R"] = 1 elseif vtFace:getX() < - dMinComponentVal then vtLapJoints["L"] = 1 elseif vtFace:getY() > dMinComponentVal then vtLapJoints["B"] = 1 elseif vtFace:getY() < - dMinComponentVal then vtLapJoints["F"] = 1 end end end end local nCnt = 0 for _ in pairs(vtLapJoints) do nCnt = nCnt + 1 end if nCnt == 1 then res.Nbr = 1 for k, _ in pairs( vtLapJoints) do res.sCase = k end elseif nCnt == 2 then res.Nbr = 2 if vtLapJoints["F"] and vtLapJoints["B"] then res.bParall = true res.sCase = "F" elseif vtLapJoints["R"] and vtLapJoints["L"] then res.bParall = true res.sCase = "R" else res.bParall = false if vtLapJoints["B"] and vtLapJoints["R"] then res.sCase = 'BR' elseif vtLapJoints["B"] and vtLapJoints["L"] then res.sCase = 'BL' elseif vtLapJoints["F"] and vtLapJoints["R"] then res.sCase = 'FR' elseif vtLapJoints["F"] and vtLapJoints["L"] then res.sCase = 'FL' end end elseif nCnt == 3 then res.Nbr = 3 if not vtLapJoints["F"] then res.sCase = "F" elseif not vtLapJoints["B"] then res.sCase = "B" elseif not vtLapJoints["R"] then res.sCase = "R" elseif not vtLapJoints["L"] then res.sCase = "L" end elseif nCnt == 4 then res.Nbr = 4 elseif nCnt ~= 0 then return end return res end ------------------------------------------------------------------ local function CreateDefectOnAngle( nPartId, RawPart, bDrillLong, bOnOppositeAng) local PartTab = {} -- recupero il gruppo dove salvo i defects local nOutlineGrp = EgtGetFirstNameInGroup(GDB_ID.ROOT, "SheetDefects") if not nOutlineGrp or nOutlineGrp == GDB_ID.NULL then nOutlineGrp = EgtGroup( GDB_ID.ROOT) EgtSetName( nOutlineGrp, "SheetDefects") EgtSetStatus( nOutlineGrp, GDB_ST.OFF) end local dExtraMachArea = s_dSideMillDiamDown -- per tenere conto dell'area di lavorazione local dExtra = NEST.KERF -- local b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) local nBoxLayerId = EgtGetFirstNameInGroup( nPartId, "Box") local nBoxId = EgtGetFirstNameInGroup( nBoxLayerId, "Box") local b3Part = EgtGetBBoxGlob(nBoxId, GDB_BB.STANDARD) local b3PartX = b3Part:getDimX() local b3PartY = b3Part:getDimY() -- se ho fori lunghi devo riservare tutto lo spazio if bDrillLong then b3PartY = RawPart.Width end -- calcolo i punti che definiscono il rettangolo local ptP1, ptP2 if ( s_sOrigCorner == 'TL' and not bOnOppositeAng) or ( s_sOrigCorner == 'BL' and bOnOppositeAng) then -- angolo TL ptP1 = Point3d( 0, RawPart.Width - b3PartY - dExtra, 0) ptP2 = Point3d( b3PartX + dExtra, RawPart.Width, 0) PartTab.posX = NEST.KERF PartTab.posY = RawPart.Width - b3Part:getDimY() - NEST.KERF elseif ( s_sOrigCorner == 'BL' and not bOnOppositeAng) or ( s_sOrigCorner == 'TL' and bOnOppositeAng) then -- angolo BL ptP1 = Point3d( 0, 0, 0) ptP2 = Point3d( b3PartX + dExtra, b3PartY + dExtra, 0) PartTab.posX = NEST.KERF PartTab.posY = NEST.KERF elseif ( s_sOrigCorner == 'TR' and not bOnOppositeAng) or ( s_sOrigCorner == 'BR' and bOnOppositeAng) then -- angolo TR ptP1 = Point3d( RawPart.Len - b3PartX - dExtra, RawPart.Width - b3PartY - dExtra, 0) ptP2 = Point3d( RawPart.Len, RawPart.Width, 0) PartTab.posX = RawPart.Len - b3Part:getDimX() - NEST.KERF PartTab.posY = RawPart.Width - b3Part:getDimY() - NEST.KERF elseif ( s_sOrigCorner == 'BR' and not bOnOppositeAng) or ( s_sOrigCorner == 'TR' and bOnOppositeAng) then -- angolo BR ptP1 = Point3d( RawPart.Len - b3PartX - dExtra, 0, 0) ptP2 = Point3d( RawPart.Len, b3PartY + dExtra, 0) PartTab.posX = RawPart.Len - b3Part:getDimX() - NEST.KERF PartTab.posY = NEST.KERF end PartTab.Id = tonumber(nPartId) PartTab.DefectId = EgtRectangle2P( nOutlineGrp, Point3d(ptP2:getX() + dExtraMachArea, ptP2:getY() + dExtraMachArea , 0), Point3d( ptP1:getX() - dExtraMachArea , ptP1:getY() - dExtraMachArea, 0)) EgtSetName( PartTab.DefectId, "Defect") return PartTab end ------------------------------------------------------------------ local function RotateOptimalCase( nPartId, nAngle, nAngleValidRot, RawPart, nPnt1, nPnt2) local bOptimal = false local b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) EgtRotate( nPartId, b3Part:getCenter(), Z_AX(), nAngle, GDB_RT.GLOB) local nRotate = nAngle local nAnglePnt local nBoxLayerId = EgtGetFirstNameInGroup( nPartId, "Box") local nBoxId = EgtGetFirstNameInGroup( nBoxLayerId, "Box") b3Part = EgtGetBBoxGlob(nBoxId, GDB_BB.STANDARD) -- b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) local bIsValidRotation = b3Part:getDimX() < RawPart.Len - 2 * NEST.KERF + GEO.EPS_SMALL and b3Part:getDimY() < RawPart.Width - 2 * NEST.KERF + GEO.EPS_SMALL if bIsValidRotation then bOptimal = true nAnglePnt = nPnt1 else bOptimal = false -- ruoto nella migliore posizione ammissibile EgtRotate( nPartId, b3Part:getCenter(), Z_AX(), nAngleValidRot, GDB_RT.GLOB) nRotate = nRotate + nAngleValidRot nAnglePnt = nPnt2 end if nRotate >= 360 then nRotate = nRotate - 360 end return nAnglePnt, nRotate, bOptimal end ------------------------------------------------------------------ local function RotateOptimalCaseDrill( nPartId, bCanRotate, nPnt1, nPnt2) local nRotate = 0 local nAnglePnt if bCanRotate then local b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) EgtRotate( nPartId, b3Part:getCenter(), Z_AX(), 180, GDB_RT.GLOB) nRotate = 180 nAnglePnt = nPnt1 else nAnglePnt = nPnt2 end return nAnglePnt, nRotate end ------------------------------------------------------------------ -- Funzione che classifica i pezzi in base alla loro necessità di stare negli angoli local function ClassifyAngles( nPartId, RawPart, sRefOrig, bLockedRot, dMinSheetWidth) -- elimino eventuali info EgtSetInfo( nPartId, "NestOnEdge", 0) EgtSetInfo( nPartId, "NestOnAngle", 0) EgtSetInfo( nPartId, "OnlyOnRefSide", 0) local nAngle local nRotate = 0 local bOptimal local bManual = false local bOnly180Rot = EgtGetInfo( nPartId, "HasGrainDirection", 'b') or false local b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) -- fori local DrillClassif, DrillClassif2 = ClassifyDrillsOnLateralFaces( nPartId, dMinSheetWidth) local bDrill = false local bDrillOpen = false local bLongDrill = false if DrillClassif then bDrill = true bDrillOpen = not DrillClassif.bClosed bLongDrill = DrillClassif.bLong if DrillClassif.bOnlyOnRef then EgtSetInfo( nPartId, "OnlyOnRefSide", 1) end end -- oriento il pezzo in base al foro if bDrill and not bLockedRot then local RotationAngles if sRefOrig == 'TL' or sRefOrig == 'TR' then RotationAngles = { F = 180, B = 0, L = 270, R = 90} else RotationAngles = { F = 0, B = 180, L = 90, R = 270} end -- se rotazione è libera if not bOnly180Rot then EgtRotate( nPartId, b3Part:getCenter(), Z_AX(), RotationAngles[DrillClassif.sCase], GDB_RT.GLOB) nRotate = nRotate + RotationAngles[DrillClassif.sCase] -- verifico se la rotazione è valida local nBoxLayerId = EgtGetFirstNameInGroup( nPartId, "Box") local nBoxId = EgtGetFirstNameInGroup( nBoxLayerId, "Box") local b3Part = EgtGetBBoxGlob(nBoxId, GDB_BB.STANDARD) -- b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) local bIsValidRotation = b3Part:getDimX() < RawPart.Len - 2 * NEST.KERF + GEO.EPS_SMALL and b3Part:getDimY() < RawPart.Width - 2 * NEST.KERF + GEO.EPS_SMALL if not bIsValidRotation then -- ritorno nella posizione originaria EgtRotate( nPartId, b3Part:getCenter(), Z_AX(), - RotationAngles[DrillClassif.sCase], GDB_RT.GLOB) nRotate = nRotate - RotationAngles[DrillClassif.sCase] -- se ho un'altra posizione possibile ruoto in quella if DrillClassif2 then EgtRotate( nPartId, b3Part:getCenter(), Z_AX(), RotationAngles[DrillClassif2.sCase], GDB_RT.GLOB) nRotate = nRotate + RotationAngles[DrillClassif2.sCase] bDrillOpen = not DrillClassif2.bClosed bLongDrill = DrillClassif2.bLong if DrillClassif2.bOnlyOnRef then EgtSetInfo( nPartId, "OnlyOnRefSide", 1) end else -- dimentico di avere il foro bDrill = false end end -- se posso ruotare solo di 180° per venatura else if RotationAngles[DrillClassif.sCase] == 0 or RotationAngles[DrillClassif.sCase] == 180 then EgtRotate( nPartId, b3Part:getCenter(), Z_AX(), RotationAngles[DrillClassif.sCase], GDB_RT.GLOB) nRotate = nRotate + RotationAngles[DrillClassif.sCase] else -- vedo se ho un'altra posizione possibile if DrillClassif2 and ( RotationAngles[DrillClassif2.sCase] == 0 or RotationAngles[DrillClassif2.sCase] == 180) then EgtRotate( nPartId, b3Part:getCenter(), Z_AX(), RotationAngles[DrillClassif2.sCase], GDB_RT.GLOB) nRotate = nRotate + RotationAngles[DrillClassif2.sCase] bDrillOpen = not DrillClassif2.bClosed bLongDrill = DrillClassif2.bLong if DrillClassif2.bOnlyOnRef then EgtSetInfo( nPartId, "OnlyOnRefSide", 1) end else -- dimentico di avere il foro bDrill = false end end end end -- se non ho venature e fori scelgo una posizione che non faccia cadere il pezzo if not bDrill and not bOnly180Rot and not bLockedRot then b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) local bIsValid = b3Part:getDimX() > s_dIntRulli local bRotatedIsValid = b3Part:getDimY() > s_dIntRulli if not bIsValid and not bRotatedIsValid then EgtSetInfo( nPartId, "REDUCECUT", 1) elseif not bIsValid or not bRotatedIsValid then bOnly180Rot = true EgtSetInfo( nPartId, "NestStepRot", 180) end if not bIsValid and bRotatedIsValid then EgtRotate( nPartId, b3Part:getCenter(), Z_AX(), 90, GDB_RT.GLOB) nRotate = nRotate + 90 end end -- lap joints local ResLapJoints = ClassifyLapJointsFromBottom( nPartId) b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) local bAdd = true if ResLapJoints.Nbr > 0 and bLockedRot then b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) if b3Part:getDimX() > s_dIntRulli then EgtSetInfo( nPartId, "REDUCECUT", 1) end local PartAngleClassification = { PartId = tostring( nPartId), Angle = 100, nRotate = 0, bDrill = true, bLongDrill = true} return PartAngleClassification end -- se anche fori oppure possibile solo rotazione di 180° per non far cadere il pezzo if bDrill or bOnly180Rot then local bCanRotate = not bDrill or ( bDrill and bDrillOpen) if ResLapJoints.Nbr == 0 then nAngle = 0 if not bDrill then bAdd = false end elseif ResLapJoints.Nbr == 1 then if (( sRefOrig == 'TL' or sRefOrig == 'TR') and ResLapJoints.sCase == 'B' ) or (( sRefOrig == 'BL' or sRefOrig == 'BR') and ResLapJoints.sCase == 'F' ) then nAngle = 20 elseif (( sRefOrig == 'TL' or sRefOrig == 'TR') and ResLapJoints.sCase == 'F' ) or (( sRefOrig == 'BL' or sRefOrig == 'BR') and ResLapJoints.sCase == 'B' ) then nAngle, nRotate2 = RotateOptimalCaseDrill( nPartId, bCanRotate, 20, 100) nRotate = nRotate + nRotate2 elseif ResLapJoints.sCase == 'R' then if bCanRotate then bManual = true end if sRefOrig == 'TL' or sRefOrig == 'BL'then nAngle, nRotate2 = RotateOptimalCaseDrill( nPartId, bCanRotate, 40, 0) nRotate = nRotate + nRotate2 else nAngle = 40 end elseif ResLapJoints.sCase == 'L' then if bCanRotate then bManual = true end if sRefOrig == 'TR' or sRefOrig == 'BR' then nAngle, nRotate2 = RotateOptimalCaseDrill( nPartId, bCanRotate, 40, 0) nRotate = nRotate + nRotate2 else nAngle = 40 end end elseif ResLapJoints.Nbr == 2 then if ResLapJoints.bParall then if ResLapJoints.sCase == 'F' then nAngle = 100 else nAngle = 80 end else if (( sRefOrig == 'TL' or sRefOrig == 'TR') and (ResLapJoints.sCase == 'BL' or ResLapJoints.sCase == 'BR')) or (( sRefOrig == 'BL' or sRefOrig == 'BR') and (ResLapJoints.sCase == 'FL' or ResLapJoints.sCase == 'FR')) then nAngle = 40 else nAngle, nRotate2 = RotateOptimalCaseDrill( nPartId, bCanRotate, 40, 100) nRotate = nRotate + nRotate2 end end elseif ResLapJoints.Nbr == 3 then if (( sRefOrig == 'TL' or sRefOrig == 'TR') and ResLapJoints.sCase == 'F' ) or (( sRefOrig == 'BL' or sRefOrig == 'BR') and ResLapJoints.sCase == 'B' ) then nAngle = 80 elseif (( sRefOrig == 'TL' or sRefOrig == 'TR') and ResLapJoints.sCase == 'B' ) or (( sRefOrig == 'BL' or sRefOrig == 'BR') and ResLapJoints.sCase == 'F' ) then nAngle, nRotate2 = RotateOptimalCaseDrill( nPartId, bCanRotate, 80, 100) nRotate = nRotate + nRotate2 elseif ResLapJoints.sCase == 'R' then if bCanRotate then bManual = true end if sRefOrig == 'TR' or sRefOrig == 'BR' then nAngle, nRotate2 = RotateOptimalCaseDrill( nPartId, bCanRotate, 100, 100) nRotate = nRotate + nRotate2 end nAngle = 100 elseif ResLapJoints.sCase == 'L' then if bCanRotate then bManual = true end if sRefOrig == 'TL' or sRefOrig == 'BL' then nAngle, nRotate2 = RotateOptimalCaseDrill( nPartId, bCanRotate, 100, 100) nRotate = nRotate + nRotate2 end nAngle = 100 end elseif ResLapJoints.Nbr == 4 then nAngle = 100 end -- no fori else if ResLapJoints.Nbr == 0 then bAdd = false elseif ResLapJoints.Nbr == 1 then if sRefOrig == 'TL' or sRefOrig == 'TR' then RotationAngles = { F = 180, B = 0, R = 90, L = 270} elseif sRefOrig == 'BL'or sRefOrig == 'BR' then RotationAngles = { F = 0, B = 180, R = 270, L = 90} end local nAngleValidRot = EgtIf( sRefOrig == 'TL' or sRefOrig == 'BR', 90, 270) nAngle, nRotate2, bOptimal = RotateOptimalCase( nPartId, RotationAngles[ResLapJoints.sCase], nAngleValidRot, RawPart, 20, 40) nRotate = nRotate + nRotate2 if not bOptimal then bManual = true end elseif ResLapJoints.Nbr == 2 then if ResLapJoints.bParall then -- se lap joints su lati paralleli RotationAngles = { F = 90, R = 0} nAngle, nRotate2 = RotateOptimalCase( nPartId, RotationAngles[ResLapJoints.sCase], 90, RawPart, 80, 100) nRotate = nRotate + nRotate2 else -- se lap joints su lati adiacenti if sRefOrig == 'TR' then RotationAngles = { FR = 90, BR = 0, BL = 270, FL = 180} elseif sRefOrig == 'TL' then RotationAngles = { FR = 180, BR = 90, BL = 0, FL = 270} elseif sRefOrig == 'BR' then RotationAngles = { FR = 0, BR = 270, BL = 180, FL = 90} elseif sRefOrig == 'BL' then RotationAngles = { FR = 270, BR = 180, BL = 90, FL = 0} end local nAngleValidRot = EgtIf( sRefOrig == 'TL' or sRefOrig == 'BR', 270, 90) nAngle, nRotate2, bOptimal = RotateOptimalCase( nPartId, RotationAngles[ResLapJoints.sCase], nAngleValidRot, RawPart, 40, 40) nRotate = nRotate + nRotate2 local b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) local bCanRotate = b3Part:getDimX() < RawPart.Width if bOptimal and bCanRotate then bManual = true end end elseif ResLapJoints.Nbr == 3 then if sRefOrig == 'TL' or sRefOrig == 'TR' then RotationAngles = { F = 0, B = 180, R = 270, L = 90} elseif sRefOrig == 'BL' or sRefOrig == 'BR' then RotationAngles = { F = 180, B = 0, R = 90, L = 270} end local nAngleValidRot = EgtIf( sRefOrig == 'TL' or sRefOrig == 'BR', 90, 270) nAngle, nRotate2, bOptimal = RotateOptimalCase( nPartId, RotationAngles[ResLapJoints.sCase], nAngleValidRot, RawPart, 80, 100) nRotate = nRotate + nRotate2 if not bOptimal then bManual = true end elseif ResLapJoints.Nbr == 4 then nAngle, nRotate2 = RotateOptimalCase( nPartId, 0, 90, RawPart, 100, 100) nRotate = nRotate + nRotate2 end end -- aggiorno le info di rotazione del pezzo if nRotate >= 360 then nRotate = nRotate - 360 end if nRotate > 0 then local bPartFlip = ( ( EgtGetInfo( nPartId, "INVERTED", 'i') or 0) == 180) local nPartRot = EgtGetInfo( nPartId, "ROTATED", 'i') or 0 local nTotRot = nPartRot - EgtIf( bPartFlip, -nRotate, nRotate) nTotRot = EgtIf( nTotRot < 0, nTotRot + 360, nTotRot) EgtSetInfo( nPartId, "ROTATED", nTotRot) EgtSetInfo( nPartId, "MODIFIEDFORNEST", 1) end if not bAdd then return end local PartAngleClassification = { PartId = tostring( nPartId), Angle = nAngle, nRotate = nRotate, bDrill = bDrill, bLongDrill = bLongDrill, bManual = bManual} local bCanBeOnOtherEdge = not bDrill or ( bDrill and RawPart.Width > s_dMinRawYHorDrill - GEO.EPS_SMALL) if bOnly180Rot then if bCanBeOnOtherEdge then EgtSetInfo( nPartId, "NestAllowRot", 1) EgtSetInfo( nPartId, "NestStepRot", 180) else EgtSetInfo( nPartId, "NestAllowRot", 0) EgtSetInfo( nPartId, "NestStepRot", 0) end end -- verifico se il pezzo cade (caso di foro) b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) if b3Part:getDimX() > s_dIntRulli then EgtSetInfo( nPartId, "REDUCECUT", 1) end return PartAngleClassification end ------------------------------------------------------------------ local function ComputeRestrictedZones( RawParts) local dTotArea = 0 -- area totale dei pezzi for nPartId, nCount in pairs( PART) do local b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) local dPartArea = b3Part:getDimX() * b3Part:getDimY() dTotArea = dTotArea + dPartArea * nCount end -- stimo il numero di angoli disponibili local vSheetNbrEstimate = {} local nSheetNbrEstimate = 0 local nSheetNbrCorrection = - 2 local nAnglesNbrEstimate = 0 local nAngelsNbrCorrection = 0 local vRawDistrib = {} if #RawParts == 1 then vRawDistrib = {1} elseif #RawParts == 2 then vRawDistrib = {0.7, 0.3} else for nInd = 1, #RawParts do table.insert( vRawDistrib, 1 / #RawParts) end end local dMaxSheetWidth = -100 local nMaxSheet = 1 local dMinSheetWidth = WD.MAX_WIDTH for nInd = 1, #RawParts do local dSheetArea = RawParts[nInd].Len * RawParts[nInd].Width if RawParts[nInd].Width > dMaxSheetWidth + GEO.EPS_SMALL then dMaxSheetWidth = RawParts[nInd].Width nMaxSheet = nInd elseif abs( RawParts[nInd].Width - dMaxSheetWidth) < GEO.EPS_SMALL then if RawParts[nInd].Len > RawParts[nMaxSheet].Len then nMaxSheet = nInd end end if RawParts[nInd].Width < dMinSheetWidth then dMinSheetWidth = RawParts[nInd].Width end vSheetNbrEstimate[nInd] = min( ceil( dTotArea * vRawDistrib[nInd] / dSheetArea) + nSheetNbrCorrection, RawParts[nInd].Qty) if vSheetNbrEstimate[nInd] <= 0 then vSheetNbrEstimate[nInd] = min( RawParts[nInd].Qty, 1) end nSheetNbrEstimate = nSheetNbrEstimate + vSheetNbrEstimate[nInd] nAnglesNbrEstimate = nAnglesNbrEstimate + vSheetNbrEstimate[nInd] * 2 end nAnglesNbrEstimate = nAnglesNbrEstimate + nAngelsNbrCorrection -- classificazione dei pezzi in base alla loro necessità di stare negli angoli local nTotParts = 0 for _ in pairs( PART) do nTotParts = nTotParts + 1 end local nPartIndex = 0 local AngleClassification = {} for nPartId, nCount in pairs( PART) do nPartIndex = nPartIndex + 1 local bManualRot = EgtGetInfo( nPartId, "MANUALROT", 'b') local PartClassif = ClassifyAngles(tonumber(nPartId), RawParts[nMaxSheet], s_sOrigCorner, bManualRot, dMinSheetWidth) if PartClassif then for nI = 1, nCount do local newTab = {PartId = PartClassif.PartId, Angle = PartClassif.Angle, nRotate = PartClassif.nRotate, bDrill = PartClassif.bDrill, bLongDrill = PartClassif.bLongDrill, bManual = PartClassif.bManual} table.insert( AngleClassification, newTab) end end if EgtProcessEvents( nPartIndex / nTotParts * 100, 0) == 1 then return false end end table.sort( AngleClassification, function(a, b) return a.Angle > b.Angle end) local vManuallyDone = {} local vPartsManuallyDone = {} for nInd = 1, #vSheetNbrEstimate do for nInd2 = 1, vSheetNbrEstimate[nInd] do table.insert( vManuallyDone, { RawPartId = nInd, Done = 0, Parts = {}}) end end local nSheetNbr = #vManuallyDone local vFilledSheets = {} for nInd = 1, #vSheetNbrEstimate do for nInd2 = 1, vSheetNbrEstimate[nInd] do table.insert( vFilledSheets, { Width = RawParts[nInd].Width, OrigWidth = RawParts[nInd].Width}) end end for nInd = 1, #AngleClassification do if AngleClassification[nInd].Angle >= NEST.MIN_ANGLE_PNT and AngleClassification[nInd].Angle > 0 then -- verifico di avere ancora angoli a disposizione secondo la stima dei grezzi local bFound = false local nBoxLayerId = EgtGetFirstNameInGroup( AngleClassification[nInd].PartId, "Box") local nBoxId = EgtGetFirstNameInGroup( nBoxLayerId, "Box") local b3Part = EgtGetBBoxGlob(nBoxId, GDB_BB.STANDARD) local dPartWidth = b3Part:getDimY() + NEST.KERF for nInd2 = 1, #vFilledSheets do if dPartWidth < vFilledSheets[nInd2].Width then -- verifico se il grezzo aveva già un pezzo in un angolo if abs( vFilledSheets[nInd2].Width - vFilledSheets[nInd2].OrigWidth) < GEO.EPS_SMALL then vFilledSheets[nInd2].Width = vFilledSheets[nInd2].Width - dPartWidth - s_dSideMillDiamDown else -- se il grezzo aveva già un pezzo nell'angolo, dopo il pezzo corrente non ne posso mettere altri vFilledSheets[nInd2].Width = 0 end bFound = true break end end -- se ho ancora angoli a disposizione oppure il pezzo deve stare necessariamente sull'angolo per essere lavorato if bFound or AngleClassification[nInd].Angle == 100 then -- verifico se va nestato manualmente if AngleClassification[nInd].bManual then -- trovo il punto della lista dove inserirlo local nIdx = -1 local bOnOppositeSide = false for nInd2 = 1, #vManuallyDone do -- verifico se il pezzo può stare sull'altro angolo local bOnlyOnRef = EgtGetInfo( AngleClassification[nInd].PartId, "OnlyOnRefSide", 'b') local bCanBeOnOtherAngle = not bOnlyOnRef and ( not AngleClassification[nInd].bDrill or ( AngleClassification[nInd].bDrill and not AngleClassification[nInd].bLongDrill and RawParts[vManuallyDone[nInd2].RawPartId].Width > s_dMinRawYHorDrill - GEO.EPS_SMALL)) if #(vManuallyDone[nInd2].Parts) == 0 then -- verifico che il pezzo stia in questo grezzo local b3Part = EgtGetBBoxGlob( AngleClassification[nInd].PartId, GDB_BB.STANDARD) if b3Part:getDimX() < RawParts[vManuallyDone[nInd2].RawPartId].Len and b3Part:getDimY() < RawParts[vManuallyDone[nInd2].RawPartId].Width then nIdx = nInd2 break end elseif #(vManuallyDone[nInd2].Parts) == 1 and bCanBeOnOtherAngle then -- verifico se il pezzo già posizionato nell'angolo non ha fori lunghi if not vManuallyDone[nInd2].Parts[1].bLongDrill then -- oriento il pezzo in modo ottimale per andare su quell'angolo local sRefOrig if s_sOrigCorner == 'TL' then sRefOrig = 'BL' end if s_sOrigCorner == 'BL' then sRefOrig = 'TL' end if s_sOrigCorner == 'TR' then sRefOrig = 'BR' end if s_sOrigCorner == 'BR' then sRefOrig = 'TR' end local bManTmp local res = ClassifyAngles( AngleClassification[nInd].PartId, RawParts[vManuallyDone[nInd2].RawPartId], sRefOrig, bManTmp, dMinSheetWidth) -- verifico sia compatibile con pezzo già inserito local b3OtherPart = EgtGetBBoxGlob( vManuallyDone[nInd2].Parts[1].Id, GDB_BB.STANDARD) local b3Part = EgtGetBBoxGlob( AngleClassification[nInd].PartId, GDB_BB.STANDARD) local dExtra = 2 * NEST.KERF + s_dSideMillDiamDown + 1 -- se è compatibile assegno questa rotazione al pezzo if b3Part:getDimY() + b3OtherPart:getDimY() + dExtra < RawParts[vManuallyDone[nInd2].RawPartId].Width then nIdx = nInd2 bOnOppositeSide = true -- aggiorno le info di rotazione if res.nRotate > 0 then local bPartFlip = ( ( EgtGetInfo( AngleClassification[nInd].PartId, "INVERTED", 'i') or 0) == 180) local nPartRot = EgtGetInfo( AngleClassification[nInd].PartId, "ROTATED", 'i') or 0 local nTotRot = nPartRot - EgtIf( bPartFlip, -res.nRotate, res.nRotate) nTotRot = EgtIf( nTotRot < 0, nTotRot + 360, nTotRot) EgtSetInfo( AngleClassification[nInd].PartId, "ROTATED", nTotRot) EgtSetInfo( AngleClassification[nInd].PartId, "MODIFIEDFORNEST", 1) end break end -- altrimenti lo riporto nella sua posizione originaria EgtRotate( AngleClassification[nInd].PartId, b3Part:getCenter(), Z_AX(), - res.nRotate, GDB_RT.GLOB) end end end -- nesto il pezzo a mano if nIdx ~= -1 then -- aree di lavorazione local nToolOutlineIds = ComputeToolOutlines( AngleClassification[nInd].PartId) local sInfo = "" for nInd = 1, #nToolOutlineIds do sInfo = sInfo .. tostring(nToolOutlineIds[nInd]) .. "," end -- salvo tra le info del pezzo gli id delle sue aree di lavorazione if sInfo ~= "" then EgtSetInfo( AngleClassification[nInd].PartId, "ToolOutlines", sInfo) end -- calcolo defect corrispondente al pezzo local PartTab = CreateDefectOnAngle( AngleClassification[nInd].PartId, RawParts[vManuallyDone[nIdx].RawPartId], AngleClassification[nInd].bLongDrill, bOnOppositeSide) PartTab.bLongDrill = AngleClassification[nInd].bLongDrill table.insert( vManuallyDone[nIdx].Parts, PartTab) table.insert( vPartsManuallyDone, tonumber( AngleClassification[nInd].PartId)) else -- aggiungo info per il nesting per ricordare che andrà su angolo EgtSetInfo( AngleClassification[nInd].PartId, "NestOnAngle", 1) end else -- se non va nestato manualmente segno info per nesting EgtSetInfo( AngleClassification[nInd].PartId, "NestOnAngle", 1) end else -- se non ho più angoli a disposizione lo metto sul lato EgtSetInfo(AngleClassification[nInd].PartId, "NestOnEdge", 1) end else EgtSetInfo(AngleClassification[nInd].PartId, "NestOnEdge", 1) end end local nInd = 1 while nInd <= #vManuallyDone do if #(vManuallyDone[nInd].Parts) == 0 then table.remove( vManuallyDone, nInd) else nInd = nInd + 1 end end return vManuallyDone, vPartsManuallyDone end ------------------------------------------------------------------ -- Funzione che sposta e aggiunge pezzi per nesting local function AddRawParts(RawParts, vDoneManually) local OUTLINE = "Outline" for nIndex = 1, #RawParts do RawParts[nIndex].PartId = {} -- pannelli con pezzi inseriti manualmente local nTotSheetWithDefects = 0 for nInd = 1, #vDoneManually do if vDoneManually[nInd].RawPartId == nIndex then -- creo pannello del materiale local SheetPartId = EgtGroup(GDB_ID.ROOT) table.insert( RawParts[nIndex].PartId, SheetPartId) EgtSetName(SheetPartId, "Sheet") local SheetLayerId = EgtGroup(SheetPartId) EgtSetName(SheetLayerId, OUTLINE) local SheetOutlineId = EgtRectangle2P(SheetLayerId, Point3d(0,0,0), Point3d(RawParts[nIndex].Len + 0.1, RawParts[nIndex].Width + 0.1, 0), GDB_RT.GLOB) EgtSetName(SheetOutlineId, OUTLINE) -- creo foglio per nesting EgtAutoNestAddSheet( SheetPartId, SheetOutlineId, NEST.KERF, 0, 1) -- aggiungo defects for nInd2 = 1, #(vDoneManually[nInd].Parts) do EgtAutoNestAddDefectToSheet( SheetPartId, vDoneManually[nInd].Parts[nInd2].DefectId) end vDoneManually[nInd].SheetId = SheetPartId nTotSheetWithDefects = nTotSheetWithDefects + 1 end end -- pannelli senza pezzi inseriti manualmente local SheetPartId = EgtGroup(GDB_ID.ROOT) table.insert( RawParts[nIndex].PartId, SheetPartId) EgtSetName(SheetPartId, "Sheet") local SheetLayerId = EgtGroup(SheetPartId) EgtSetName(SheetLayerId, OUTLINE) local SheetOutlineId = EgtRectangle2P(SheetLayerId, Point3d(0,0,0), Point3d(RawParts[nIndex].Len + 0.1, RawParts[nIndex].Width + 0.1, 0), GDB_RT.GLOB) -- EgtModifyCurveThickness(SheetOutlineId, -Material.T_mm) EgtSetName(SheetOutlineId, OUTLINE) -- creo foglio per nesting EgtAutoNestAddSheet( SheetPartId, SheetOutlineId, NEST.KERF, 1, RawParts[nIndex].Qty - nTotSheetWithDefects) -- EgtAutoNestAddSheet( SheetId, OutlineId, dKerf, nPriority, nCount) end end ------------------------------------------------------------------ local function AddParts(RawParts, vPartsDoneManually) -- cerco la width massima dei grezzi local dRawMaxWidth = - 100 for nIndex = 1, #RawParts do if RawParts[nIndex].Width > dRawMaxWidth then dRawMaxWidth = RawParts[nIndex].Width end end -- valori correttivi per vincoli StripX e StripY local dStripYCorr = GEO.EPS_SMALL local dStripXCorr = GEO.EPS_SMALL if NEST.CORNER == NST_CORNER.TL or NEST.CORNER == NST_CORNER.TR then dStripYCorr = 0.1 - GEO.EPS_SMALL end if NEST.CORNER == NST_CORNER.BR or NEST.CORNER == NST_CORNER.TR then dStripXCorr = 0.1 - GEO.EPS_SMALL end local nTotParts = 0 for _ in pairs( PART) do nTotParts = nTotParts + 1 end local nPartIndex = 0 -- ciclo su pezzi per aggiungerli al nesting for nPartId, nCount in pairs( PART) do repeat -- conto quanti pezzi di questo tipo sono già stati posizionati a mano nManuallyDone = 0 for nJ = 1, #vPartsDoneManually do if vPartsDoneManually[nJ] == tonumber( nPartId) then nManuallyDone = nManuallyDone + 1 end end if nManuallyDone == nCount then -- se tutti posizionati passo al pezzo successivo break else -- posiziono con il nesting solo quelli mancanti nCount = nCount - nManuallyDone end nPartIndex = nPartIndex + 1 -- calcolo bbox pezzo per rotazioni local b3Part = EgtGetBBoxGlob( nPartId, GDB_BB.STANDARD) -- recupero eventuali stati di flip e rotazione dalle info del pezzo local bFlipNest = true local bFlip = false local bRotNest = true local nStepRotNest = 90 local nRotate = 0 -- flip local bManualFlip = EgtGetInfo( nPartId, "MANUALFLIP", 'b') if bManualFlip then bFlipNest = false elseif EgtExistsInfo( nPartId, "NestFlip") then bFlipNest = EgtGetInfo( nPartId, "NestAllowFlip", 'b') bFlip = EgtGetInfo( nPartId, "NestFlip", 'b') end -- rotation local bManualRot = EgtGetInfo( nPartId, "MANUALROT", 'b') if bManualRot then bRotNest = false elseif EgtExistsInfo( nPartId, "NestRot") then nStepRotNest = EgtGetInfo( nPartId, "NestStepRot", 'i') bRotNest = EgtGetInfo( nPartId, "NestAllowRot", 'b') -- verifico se rotazione è valida (pezzo è contenuto nel grezzo) local bValidRotationInRaw = b3Part:getDimX() < RawParts[1].Len - 2 * NEST.KERF + GEO.EPS_SMALL and b3Part:getDimY() < dRawMaxWidth - 2 * NEST.KERF + GEO.EPS_SMALL if not bValidRotationInRaw then bRotNest = true nStepRotNest = 90 end end -- recupero vecchio contorno se già presente local nOutlineLayer = EgtGetFirstNameInGroup(nPartId, "Outline") local nOldOutline = EgtGetFirstNameInGroup(nOutlineLayer, 'ON_TMP') local nOutline, nCnt -- se l'outline non è stato calcolato oppure il pezzo è stato modificato, lo ricalcolo local bPartIsModified = EgtGetInfo( nPartId, "MODIFIEDFORNEST", 'b') if not nOldOutline or bPartIsModified then -- elimino il vecchio contorno se pesente while ( nOldOutline) do EgtErase(nOldOutline or GDB_ID.NULL) nOldOutline = EgtGetFirstNameInGroup( nOutlineLayer, 'ON_TMP') end -- Recupero o ricalcolo il solido local SolidId = EgtBeamGetSolid( nPartId) if not SolidId or not EgtGetInfo( SolidId, 'VALID') then EgtBeamCalcSolid( nPartId, true) SolidId = EgtBeamGetSolid( nPartId) end nOutline, nCnt = EgtGetSurfTmSilhouette( SolidId, Z_AX(), 10, nOutlineLayer, GDB_RT.GLOB) EgtBeamShowSolid( nPartId, false) for nInd = 0, nCnt - 1 do EgtSetName( nOutline + nInd, 'ON_TMP') EgtSetStatus( nOutline + nInd, 0) end local nCurrOutline = nOutline if nCnt > 0 then local frSum EgtScale( nOutline, GLOB_FRM(), 1, 1, 0, GDB_RT.GLOB) EgtModifyCurveExtrusion( nOutline, Z_AX(), GDB_RT.GLOB) -- verifico che la prima curva sia l'unico loop esterno local vtCrvRefN = EgtCurveArea( nOutline) for nInd = 1, nCnt - 1 do local vtCrvN = EgtCurveArea( nOutline + nInd) -- se trovo più loop esterni uso come contorno quello del box (quindi forzo nCurrOutline a nil) if vtCrvN and vtCrvRefN and AreSameVectorApprox( vtCrvN, vtCrvRefN) then nCurrOutline = nil for nInd2 = 0, nCnt - 1 do EgtErase( nOutline + nInd2) end nCnt = 1 break end end end if nCurrOutline and nCurrOutline ~= GDB_ID.NULL then nOutline = nCurrOutline else -- se non ho trovato contorno, lo recupero dal Box local nBoxLayer = EgtGetFirstNameInGroup(nPartId, "Box") local nBox = EgtGetFirstNameInGroup(nBoxLayer, "Box") local BBox = EgtGetBBoxGlob(nBox, GDB_BB.STANDARD) nOutline = EgtRectangle2P(nOutlineLayer, Point3d( BBox:getMin():getX(), BBox:getMin():getY(), 0), Point3d( BBox:getMax():getX(), BBox:getMax():getY(), 0), GDB_RT.GLOB) EgtOutLog("Impossible creating silhouette for part " .. tostring(nPartId)) nCnt = 1 end EgtSetName( nOutline, 'ON_TMP') EgtSetStatus( nOutline, 0) else -- altrimenti uso l'outline già calcolato nOutline = nOldOutline -- recupero il valore di nCnt local vOutlinesIds = EgtGetNameInGroup( nOutlineLayer, 'ON_TMP') nCnt = #vOutlinesIds end -- calcolo aree lavorazione local nToolOutlineIds = ComputeToolOutlines( nPartId) if bFlipNest and #nToolOutlineIds > 0 then bFlipNest = false end if nOutline then -- aggiungo pezzo al nesting EgtAutoNestAddPart( nPartId, nOutline, bFlipNest, bRotNest, nStepRotNest, 0, nCount) -- EgtAutoNestAddPart( PartId, OutlineId, bCanFlip, bCanRotate, dRotStep, nPriority, nCount) end -- aggiungo finestre for nInd = 1, nCnt - 1 do local _, _, dArea = EgtCurveArea( nOutline + nInd) if dArea and dArea > s_dNestHoleMinArea then EgtModifyCurveExtrusion( nOutline + nInd, Z_AX(), GDB_RT.GLOB) EgtAutoNestAddHoleToPart( nPartId, nOutline + nInd) end end -- aggiungo aree di lavorazione del pezzo local sInfo = "" for nInd = 1, #nToolOutlineIds do EgtAutoNestAddToolOutlineToPart( nPartId, nToolOutlineIds[nInd]) sInfo = sInfo .. tostring(nToolOutlineIds[nInd]) .. "," end -- salvo tra le info del pezzo gli id delle sue aree di lavorazione if sInfo ~= "" then EgtSetInfo( nPartId, "ToolOutlines", sInfo) end -- Eventuali vincoli StripX e StripY local bOnEdge = EgtGetInfo(nPartId, "NestOnEdge", 'b') local bOnAngle = EgtGetInfo(nPartId, "NestOnAngle", 'b') if bOnEdge or bOnAngle then local nBoxLayerId = EgtGetFirstNameInGroup( nPartId, "Box") local nBoxId = EgtGetFirstNameInGroup( nBoxLayerId, "Box") local b3Part = EgtGetBBoxGlob(nBoxId, GDB_BB.STANDARD) -- punto di riferimento sul pezzo local ptRef local dval = 10 if ( s_sOrigCorner == 'TL' or s_sOrigCorner == 'TR') then ptRef = Point3d( b3Part:getMax():getX() - b3Part:getDimX() / 2, b3Part:getMax():getY() - dval, 0) else ptRef = Point3d( b3Part:getMin():getX() + b3Part:getDimX() / 2, b3Part:getMin():getY() + dval, 0) end -- vincolo StripY local bOnlyOnRefSide = EgtGetInfo( nPartId, "OnlyOnRefSide", 'b') if bOnlyOnRefSide then if s_sOrigCorner == 'BL' or s_sOrigCorner == 'BR' then EgtAutoNestSetStripYconstraintToPart( nPartId, ptRef, NEST.KERF + dval + dStripYCorr, 100000) else EgtAutoNestSetStripYconstraintToPart( nPartId, ptRef, - 1000, dRawMaxWidth + 1000 - ( NEST.KERF + dval - dStripYCorr)) end else EgtAutoNestSetStripYconstraintToPart( nPartId, ptRef, NEST.KERF + dval + dStripYCorr, dRawMaxWidth - 2 * ( NEST.KERF + dval)) end -- se sull'angolo aggiungo anche vincolo StripX if bOnAngle then if s_sOrigCorner == 'TL' or s_sOrigCorner == 'BL' then EgtAutoNestSetStripXconstraintToPart( nPartId, ptRef, NEST.KERF + b3Part:getDimX() / 2 + dStripXCorr, 100000) else EgtAutoNestSetStripXconstraintToPart( nPartId, ptRef, -100, RawParts[1].Len + 100 - NEST.KERF - b3Part:getDimX() / 2 + dStripXCorr) end end end -- verifico se anullato nesting if EgtProcessEvents( 100 + ( nPartIndex / nTotParts * 100), 0) == 1 then return false end until true end return true end ------------------------------------------------------------------ ------------------------------------------------------------------ -- Inizializzo contatori errori e avvisi local nErrCnt = 0 local nWarnCnt = 0 local bOk = true NEST.ERR = 0 -- resetto gruppo di lavorazione corrente EgtResetCurrMachGroup() -- modalità nesting utile per cabinet: un grezzo per ogni pezzo, pezzo centrato nel grezzo. Il grezzo non viene preso da magazzino ma creato ad hoc, con un dato sovramateriale. -- il numero del MachGroup deve coincidere con il PDN del pezzo originale if NEST.FLAG ~= 11 and WD.ENABLE_SIMPLE_NESTING then -- sovramateriale richiesto local dOverMaterial = max( 0, NEST.KERF or 2) -- inizializzazione parametri per creazione grezzo tramite BatchProcessNew _G.WALL = {} WALL.FILE = NEST.FILE WALL.MACHINE = NEST.MACHINE WALL.BASEDIR = NEST.BASEDIR WALL.FLAG = 6 -- CREATE_PANEL WALL.NESTING_REF = 'BL' -- si ricrea la lista pezzi ordinata local PartList = {} local nPartCounter = 0 for nPartId, nCount in pairs( PART) do nPartCounter = nPartCounter + 1 PartList[nPartCounter] = {} PartList[nPartCounter].nId = nPartId PartList[nPartCounter].nCount = nCount PartList[nPartCounter].nPdn = EgtGetInfo( nPartId, "PDN", 'i') PartList[nPartCounter].bAddCounterToNaming = false -- se presente un pezzo con multipli il PDN sarà seguito da un contatore if nCount > 1 then PartList[nPartCounter].bAddCounterToNaming = true end end -- si riordina in base al PDN table.sort( PartList, function(a, b) return tonumber( a.nPdn) < tonumber( b.nPdn) end) -- per ogni singolo pezzo si chiama la fliprot per avere la migliore orientazione -- si creano duplo e machgroup e si settano le note opportune per poi creare i grezzi richiamando la BatchProcessNew (flag 6) for i = 1, #PartList do local nPartId = PartList[i].nId local nPartCount = PartList[i].nCount local nPdn = PartList[i].nPdn local bAddCounterToNaming = PartList[i].bAddCounterToNaming -- si ruotano e invertono i pezzi per avere la migliore posizione di lavorazione _G.NFAR = {} NFAR.ERR = 0 NFAR.MSG = '' NFAR.PARTID = nPartId NFAR.BASEDIR = NEST.BASEDIR dofile( NEST.BASEDIR .. "\\NestFlipAndRotate.lua") -- informazioni dalla parte originale -- box e relative dimensioni local nBoxLayerId = EgtGetFirstNameInGroup( nPartId, "Box") local nBoxId = EgtGetFirstNameInGroup( nBoxLayerId, "Box") local b3Part = EgtGetBBoxGlob( nBoxId, GDB_BB.STANDARD) -- inversione e rotazione local nPartFlip = EgtGetInfo( nPartId, "INVERTED", 'i') or 0 local nPartRotation = EgtGetInfo( nPartId, "ROTATED", 'i') or 0 -- dimensioni grezzo, considerando sovramateriale local dRawLength = b3Part:getDimX() + 2 * dOverMaterial local dRawWidth = b3Part:getDimY() + 2 * dOverMaterial for j = 1, nPartCount do -- creazione gruppo di lavoro local sMachGroupName = nPdn if bAddCounterToNaming then sMachGroupName = nPdn .. '_' .. string.format("%02d", j) end local nMachGroupId = EgtAddMachGroup( sMachGroupName) if not nMachGroupId then EgtOutLog( "Errore: MachGroup " .. nPdn .. " già presente") EgtOutBox( 'Error : MachGroup ' .. nPdn .. ' already existing', 'Nesting failed') NEST.ERR = 2 return end -- settaggio note in gruppo di lavoro EgtSetInfo( nMachGroupId, "PANELLEN", dRawLength) EgtSetInfo( nMachGroupId, "PANELWIDTH", dRawWidth) EgtSetInfo( nMachGroupId, "MATERIAL", NEST.MATERIAL) EgtSetInfo( nMachGroupId, "PRODID", NEST.PRODID) EgtSetInfo( nMachGroupId, "PATTID", nMachGroupId) -- creazione duplo local nPartDuploId = EgtDuploNew( nPartId) -- settaggio note in duplo EgtSetInfo( nMachGroupId, "PART" .. 1, nPartDuploId .. "," .. EgtNumToString( dOverMaterial) .. "," .. EgtNumToString( dOverMaterial) .. "," .. 0 .."," .. 0) EgtSetInfo( nPartDuploId, "POSY", dOverMaterial) EgtSetInfo( nPartDuploId, "POSX", dOverMaterial) EgtSetInfo( nPartDuploId, "FLIP", nPartFlip) EgtSetInfo( nPartDuploId, "ROT", nPartRotation) -- creazione grezzi tramite BatchProcessNew EgtSetCurrMachGroup( nMachGroupId) EgtSetInfo( nMachGroupId, "UPDATEUI", 1) dofile( NEST.BASEDIR .. "\\BatchProcessNew.lua") end end EgtProcessEvents( 200 + 100, 0) EgtResetCurrMachGroup() EgtOutLog( ' +++ NestProcess completed') EgtOutLog( ' +++ Generating Cutting List') return elseif NEST.FLAG == 11 then local vCuttingListType = EgtSplitString( WD.PANELSAW_TYPE) for i = 1, #vCuttingListType do local sCuttingListType = vCuttingListType[i] PanelSaw.GenerateCuttingList( sCuttingListType) end return end -- inizio nesting automatico EgtAutoNestStart() -- lista dei grezzi local RawParts = {} -- creo tabella dei grezzi for sIndex, dLen in pairs( LEN) do RawParts[tonumber(sIndex)] = { Len = dLen} end for sIndex, dWidth in pairs( WIDTH) do RawParts[tonumber(sIndex)].Width = dWidth end for sIndex, nQty in pairs( QTY) do RawParts[tonumber(sIndex)].Qty = nQty end for sIndex, sMaterial in pairs( MATERIAL) do RawParts[tonumber(sIndex)].Material = sMaterial end -- local vDoneManually, vPartsDoneManually = ComputeRestrictedZones( RawParts) -- creo grezzi e li aggiungo al nesting AddRawParts(RawParts, vDoneManually) if not bOk then EgtOutLog("Interruzione nesting") NEST.ERR = 1 end -- calcolo rotazioni e flip ed aggiungo pezzi al nesting if bOk then bOk = AddParts(RawParts, vPartsDoneManually) end if not bOk then EgtOutLog("Interruzione nesting") NEST.ERR = 1 end if bOk then -- aggiungo offset tra pezzi EgtAutoNestSetInterpartGap( NEST.OFFSET) -- Impostazione corner di inizio del nesting (NST_CORNER.BL, TL, BR, TR) EgtAutoNestSetStartCorner( NEST.CORNER) -- Report dei dati di nesting per debug EgtAutoNestSetReportFile( EgtGetTempDir() .. '\\LastNest.json') -- imposto tempo di nesting e lo avvio EgtAutoNestCompute( true, NEST.TIME) end -- Variabili di calcolo local nNestedParts, nParts, nSheets, nNestings, dTotFillRatio local bNestingOk = bOk -- Attesa fine calcolo local nTime = 0 while bNestingOk do bNestingOk, nStat = EgtAutoNestGetComputationStatus() if nStat == 2 or nStat == 3 then nNestedParts, nParts, nSheets, nNestings, dTotFillRatio = EgtAutoNestGetResults() --EgtOutText( string.format( 'Parts : %d/%d Filling : %.2f%%', nNestedParts, nParts, 100 * dTotFillRatio)) end if nStat == 3 then break end nTime = nTime + 1 if EgtProcessEvents( nTime / NEST.TIME * 100, 995) == 1 then bNestingOk = EgtAutoNestCancelComputation() bOk = false EgtOutLog("Interruzione nesting") NEST.ERR = 1 break end end local dXCorr = 0 local dYCorr = 0 if NEST.CORNER == NST_CORNER.TL then dYCorr = - 0.1 elseif NEST.CORNER == NST_CORNER.TR then dYCorr = - 0.1 dXCorr = - 0.1 elseif NEST.CORNER == NST_CORNER.BR then dXCorr = - 0.1 end -- se nesting andato bene if bNestingOk then -- disposizione sheet e parts local SheetId = GDB_ID.NULL local vtAdd = V_NULL() local MachGroupList = {} local nPartCount = 0 local Sheet = {PartList = {}} local bFirstSheet = true for i = 0, 999 do local nType, nId, nFlag, dX, dY, dAngRot = EgtAutoNestGetOneResult( i) if not nType then break end -- se sheet if nType > 0 then MachGroupList = {} for MGIndex = 1, nType do -- creo gruppo di lavorazione local MachGroupName = NewMachGroupName() local nMachGroup = EgtAddMachGroup(MachGroupName, sCurrMachName) table.insert( MachGroupList, { MGId = nMachGroup}) for nIndex = 1, #RawParts do for nInd2 = 1, #(RawParts[nIndex].PartId) do if RawParts[nIndex].PartId[nInd2] == nId then EgtSetInfo(nMachGroup, "PANELLEN", RawParts[nIndex].Len) EgtSetInfo(nMachGroup, "PANELWIDTH", RawParts[nIndex].Width) EgtSetInfo(nMachGroup, "MATERIAL", NEST.MATERIAL) EgtSetInfo(nMachGroup, "AUTONEST", 1) -- scrivo dati per variabili P di comunicazione con la macchina in gruppo di lavorazione EgtSetInfo( nMachGroup, "PRODID", NEST.PRODID) EgtSetInfo( nMachGroup, "PATTID", nMachGroup) end end end -- aggiungo pezzi nestati a mano nPartCount = 0 for nInd = 1, #vDoneManually do if vDoneManually[nInd].SheetId == nId then vDoneManually[nInd].Done = 1 for nInd2 = 1, #(vDoneManually[nInd].Parts) do nPartCount = nPartCount + 1 local nPartDuploId = EgtDuploNew( vDoneManually[nInd].Parts[nInd2].Id) -- aggiungo le curve corrispondenti alle aree di lavorazione del pezzo local sToolOutlines = EgtGetInfo( vDoneManually[nInd].Parts[nInd2].Id, "ToolOutlines", 's') if sToolOutlines then -- recupero o creo il gruppo per gli outlines local nToolOutlinesGrp = EgtGetFirstNameInGroup( nPartDuploId, "ToolOutlines") if not nToolOutlinesGrp then nToolOutlinesGrp = EgtGroup( nPartDuploId) EgtSetName( nToolOutlinesGrp, "ToolOutlines") EgtSetStatus( nToolOutlinesGrp, GDB_ST.ON) end for str in string.gmatch(sToolOutlines, "([^"..",".."]+)") do EgtCopyGlob( tonumber(str), nToolOutlinesGrp) end end -- applico flip, rotazione e traslazione pezzo e box da nesting EgtSetInfo( nMachGroup, "PART" .. nPartCount, nPartDuploId .. "," .. EgtNumToString( vDoneManually[nInd].Parts[nInd2].posX, 3) .. "," .. EgtNumToString( vDoneManually[nInd].Parts[nInd2].posY, 3) .. "," .. 0 .."," .. 0) EgtSetInfo( nPartDuploId, "POSX", vDoneManually[nInd].Parts[nInd2].posX) EgtSetInfo( nPartDuploId, "POSY", vDoneManually[nInd].Parts[nInd2].posY) end end end end -- altrimenti pezzo else nPartCount = nPartCount + 1 for MGIndex = 1, #MachGroupList do local nMachGroup = MachGroupList[MGIndex].MGId --EgtSetCurrMachGroup(nMachGroup) -- se c'e' un grezzo valido if nMachGroup and nMachGroup ~= GDB_ID.NULL then -- creo pezzo copia local nPartDuploId = EgtDuploNew( nId) local nPartInd -- aggiungo le curve corrispondenti alle aree di lavorazione del pezzo local sToolOutlines = EgtGetInfo( nId, "ToolOutlines", 's') if sToolOutlines then -- recupero o creo il gruppo per gli outlines local nToolOutlinesGrp = EgtGetFirstNameInGroup( nPartDuploId, "ToolOutlines") if not nToolOutlinesGrp then nToolOutlinesGrp = EgtGroup( nPartDuploId) EgtSetName( nToolOutlinesGrp, "ToolOutlines") EgtSetStatus( nToolOutlinesGrp, GDB_ST.ON) end for str in string.gmatch(sToolOutlines, "([^"..",".."]+)") do EgtCopyGlob( tonumber(str), nToolOutlinesGrp) end end -- applico flip, rotazione e traslazione pezzo e box da nesting if nFlag == 1 then EgtRotate( nPartDuploId, ORIG(), X_AX(), 180, GDB_RT.GLOB) end EgtRotate( nPartDuploId, ORIG(), Z_AX(), dAngRot, GDB_RT.GLOB) EgtMove( nPartDuploId, Vector3d( dX, dY, 0), GDB_RT.GLOB) local nBoxLayerId = EgtGetFirstNameInGroup( nPartDuploId, "Box") local nBoxId = EgtGetFirstNameInGroup( nBoxLayerId, "Box") local PartBBox = EgtGetBBoxGlob(nBoxId, GDB_BB.STANDARD) local ptPos = Point3d( PartBBox:getMin():getX(), PartBBox:getMin():getY(), 0) EgtSetInfo( nMachGroup, "PART" .. nPartCount, nPartDuploId .. "," .. EgtNumToString( ptPos:getX() + dXCorr, 3) .. "," .. EgtNumToString( ptPos:getY() + dYCorr, 3) .. "," .. 0 .."," .. 0) EgtSetInfo( nPartDuploId, "POSX", ptPos:getX() + dXCorr) EgtSetInfo( nPartDuploId, "POSY", ptPos:getY() + dYCorr) local nPartFlip = EgtGetInfo( nId, "INVERTED", 'i') or 0 nPartFlip = EgtIf( nPartFlip == 180, 1, 0) local nTotFlip = EgtIf( nPartFlip ~= nFlag, 180, 0) EgtSetInfo( nPartDuploId, "FLIP", nTotFlip) local bPartFlip = ( nTotFlip == 180) local nPartRot = EgtGetInfo( nId, "ROTATED", 'i') or 0 local nTotRot = nPartRot - EgtIf( bPartFlip, -dAngRot, dAngRot) if nTotRot < 0 then nTotRot = nTotRot + 360 elseif nTotRot >= 360 then nTotRot = nTotRot - 360 end EgtSetInfo( nPartDuploId, "ROT", nTotRot) end end end end -- creo gruppi di lavorazione per fogli con pezzi nestati solo a mano for nInd = 1, #vDoneManually do if vDoneManually[nInd].Done == 0 then -- creo gruppo di lavorazione local MachGroupName = NewMachGroupName() local nMachGroup = EgtAddMachGroup(MachGroupName, sCurrMachName) table.insert( MachGroupList, { MGId = nMachGroup}) for nIndex = 1, #RawParts do for nInd2 = 1, #(RawParts[nIndex].PartId) do if RawParts[nIndex].PartId[nInd2] == vDoneManually[nInd].SheetId then EgtSetInfo(nMachGroup, "PANELLEN", RawParts[nIndex].Len) EgtSetInfo(nMachGroup, "PANELWIDTH", RawParts[nIndex].Width) EgtSetInfo(nMachGroup, "MATERIAL", NEST.MATERIAL) EgtSetInfo(nMachGroup, "AUTONEST", 1) -- scrivo dati per variabili P di comunicazione con la macchina in gruppo di lavorazione EgtSetInfo( nMachGroup, "PRODID", NEST.PRODID) EgtSetInfo( nMachGroup, "PATTID", nMachGroup) end end end -- aggiungo pezzi nestati a mano nPartCount = 0 for nInd2 = 1, #(vDoneManually[nInd].Parts) do nPartCount = nPartCount + 1 local nPartDuploId = EgtDuploNew( vDoneManually[nInd].Parts[nInd2].Id) -- aggiungo le curve corrispondenti alle aree di lavorazione del pezzo local sToolOutlines = EgtGetInfo( vDoneManually[nInd].Parts[nInd2].Id, "ToolOutlines", 's') if sToolOutlines then -- recupero o creo il gruppo per gli outlines local nToolOutlinesGrp = EgtGetFirstNameInGroup( nPartDuploId, "ToolOutlines") if not nToolOutlinesGrp then nToolOutlinesGrp = EgtGroup( nPartDuploId) EgtSetName( nToolOutlinesGrp, "ToolOutlines") EgtSetStatus( nToolOutlinesGrp, GDB_ST.ON) end for str in string.gmatch(sToolOutlines, "([^"..",".."]+)") do EgtCopyGlob( tonumber(str), nToolOutlinesGrp) end end -- applico flip, rotazione e traslazione pezzo e box da nesting EgtSetInfo( nMachGroup, "PART" .. nPartCount, nPartDuploId .. "," .. EgtNumToString( vDoneManually[nInd].Parts[nInd2].posX, 3) .. "," .. EgtNumToString( vDoneManually[nInd].Parts[nInd2].posY, 3) .. "," .. 0 .."," .. 0) EgtSetInfo( nPartDuploId, "POSX", vDoneManually[nInd].Parts[nInd2].posX) EgtSetInfo( nPartDuploId, "POSY", vDoneManually[nInd].Parts[nInd2].posY) end end end -- creo grezzi per ogni gruppo di lavorazione local MachGroupToTCnt = EgtGetMachGroupCount() local MachGroupIndex = 0 _G.WALL = {} WALL.FILE = NEST.FILE WALL.MACHINE = NEST.MACHINE WALL.BASEDIR = NEST.BASEDIR WALL.FLAG = 6 -- CREATE_PANEL WALL.NESTING_REF = 'BL' nMachGroup = EgtGetFirstMachGroup() while nMachGroup do EgtSetCurrMachGroup( nMachGroup) if EgtGetInfo( nMachGroup, "AUTONEST",'i') == 1 then MachGroupIndex = MachGroupIndex + 1 EgtRemoveInfo( nMachGroup, "AUTONEST") EgtSetInfo( nMachGroup, "UPDATEUI", 1) dofile( NEST.BASEDIR .. "\\BatchProcessNew.lua") -- aggiorno interfaccia EgtProcessEvents( 200 + ( MachGroupIndex / MachGroupToTCnt * 100), 0) end -- ciclo sui duplo per sistemare Q local nRawPartId = EgtGetFirstRawPart() -- recupero box grezzo ridotto della tolleranza local b3Raw = EgtGetRawPartBBox( nRawPartId) b3Raw:expand( - s_dInsideRawTol) EgtOutLog( 'RawBox='..tostring( b3Raw)) local nPartDuploId = EgtGetFirstPartInRawPart( nRawPartId) while nPartDuploId do local vPartProc = WE.CollectFeatures( nPartDuploId) for ProcIndex = 1, #vPartProc do local Proc = vPartProc[ProcIndex] if LapJoint.Identify( Proc) then -- cerco la faccia rivolta verso l'alto e la faccia rivolta verso il basso local nFaceInd = -1 local nFaceDownInd = -1 for nIdx = 0, Proc.Fct - 1 do local vtN = EgtSurfTmFacetNormVersor( Proc.Id, nIdx, GDB_ID.ROOT) if vtN:getZ() > 0.95 then nFaceInd = nIdx elseif vtN:getZ() < - 0.95 then nFaceDownInd = nIdx end end -- se è lap joint dall'alto if nFaceInd ~= -1 and nFaceDownInd == -1 then local bResetQ = false local ptCen = EgtSurfTmFacetCenter( Proc.Id, nFaceInd, GDB_ID.ROOT) -- se all'interno rimuovo info Q if EnclosesPointXY( b3Raw, ptCen) then bResetQ = true elseif Proc.Fct == 2 then -- se non è interno ma ha 2 facce non ortogonali rimuovo info Q local vtN1 = EgtSurfTmFacetNormVersor( Proc.Id, 0, GDB_ID.ROOT) local vtN2 = EgtSurfTmFacetNormVersor( Proc.Id, 1, GDB_ID.ROOT) if abs( vtN1 * vtN2) > GEO.EPS_SMALL then bResetQ = true end end if bResetQ then -- resetto parametro Q if Proc.Prc == 30 then EgtRemoveInfo( Proc.Id, "Q08") EgtRemoveInfo( Proc.Id, "Q08A") else EgtRemoveInfo( Proc.Id, "Q03") EgtRemoveInfo( Proc.Id, "Q03A") end end end elseif DoubleCut.Identify( Proc) then -- verifico se due facce e rivolto verso l'alto if Proc.Fct == 2 then local vtN = {} vtN[1] = EgtSurfTmFacetNormVersor( Proc.Id, 0, GDB_ID.ROOT) vtN[2] = EgtSurfTmFacetNormVersor( Proc.Id, 1, GDB_ID.ROOT) if ( vtN[1]:getZ() >= 0.95 or vtN[2]:getZ() >= 0.95) then local nFaceInd = EgtIf( vtN[1]:getZ() >= 0.95, 0, 1) local ptCen = EgtSurfTmFacetCenter( Proc.Id, nFaceInd, GDB_ID.ROOT) -- se all'interno rimuovo info Q if EnclosesPointXY( b3Raw, ptCen) then -- resetto parametro Q if Proc.Prc == 12 then EgtRemoveInfo( Proc.Id, "Q02") EgtRemoveInfo( Proc.Id, "Q02A") end end end end end end nPartDuploId = EgtGetNextPartInRawPart(nPartDuploId) end nMachGroup = EgtGetNextMachGroup(nMachGroup) end else EgtOutLog("Errore: nesting fallito") NEST.ERR = 2 end EgtResetCurrMachGroup() -- cancello rettangolo del materiale per nesting for RawPartId = 1, #RawParts do EgtErase(RawParts[RawPartId].PartId) end local nOutlineGrp = EgtGetFirstNameInGroup(GDB_ID.ROOT, "ToolOutlines") if nOutlineGrp and nOutlineGrp ~= GDB_ID.NULL then EgtErase( nOutlineGrp) end local nSheetDefectsGrp = EgtGetFirstNameInGroup(GDB_ID.ROOT, "SheetDefects") if nSheetDefectsGrp and nSheetDefectsGrp ~= GDB_ID.NULL then EgtErase( nSheetDefectsGrp) end -- EgtSaveFile( "C:\\EgtData\\EgtBEAMWALL\\Temp\\file.nge") EgtOutLog( ' +++ NestProcess completed')