-- WallLib.lua by Egaltech s.r.l. 2023/10/16 -- Libreria globale per Pareti -- 2023/06/26 Spostata qui Is3EdgesApprox da FreeContour. Ora cerca autonomamente il gruppo se non viene passato. -- 2023/11/14 Aggiunte funzioni GetProcessAffectedFaces, GetProcessDistanceToNearestParts, GetProcessDistanceToRawPart. -- 2023/12/11 Aggiunta funzione IsFeatureCuttingEntireSection (allineamento Topology con Beam). -- Tabella per definizione modulo local WallLib = {} -- Include require( 'EgtBase') EgtOutLog( ' WallLib started', 1) ------------------------------------------------------------------------------------------------------------- function WallLib.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 ------------------------------------------------------------------------------------------------------------- function WallLib.CreateOrEmptyAddGroup( PartId) -- recupero i dati del gruppo aggiuntivo local AddGrpId, sMchGrp = WallLib.GetAddGroup( PartId) if not sMchGrp then return false end -- se esiste lo svuoto if AddGrpId then return EgtEmptyGroup( AddGrpId) end -- altrimenti lo creo 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, sMchGrp) EgtSetInfo( AddGrpId, GDB_SI.MGRPONLY, EgtGetCurrMachGroup()) EgtSetColor( AddGrpId, Color3d( 80, 160, 160, 50)) return true end ------------------------------------------------------------------------------------------------------------- function WallLib.GetPointDirDepth( nRawId, ptP, vtDir) -- recupero il solido del grezzo local nSolId = EgtGetFirstNameInGroup( nRawId, 'RawSolid') if not nSolId then return end -- interseco con la retta local bOk, vType, vPar = EgtLineSurfTmInters( ptP, vtDir, nSolId, GDB_RT.GLOB) if not bOk then return end if not vPar or #vPar == 0 then return -2 end local dLenIn, dLenOut for i = 1, #vPar do if vPar[i] < 0 then if vType[i] == GDB_SLT.IN or vType[i] == GDB_SLT.TG_INI then dLenIn = -1 end if vType[i] == GDB_SLT.OUT or vType[i] == GDB_SLT.TG_FIN then dLenIn = -2 end else if vType[i] == GDB_SLT.IN or vType[i] == GDB_SLT.TG_INI then dLenIn = vPar[i] end if vType[i] == GDB_SLT.OUT or vType[i] == GDB_SLT.TG_FIN or vType[i] == GDB_SLT.TOUCH then dLenOut = vPar[i] end end end return dLenIn, dLenOut end --------------------------------------------------------------------- function WallLib.GetFaceElevation( nSurfId, nFac, nRawId) local ptC, vtN = EgtSurfTmFacetCenter( nSurfId, nFac, GDB_ID.ROOT) if not ptC or not vtN then return 0 end local frOCS = Frame3d( ptC, vtN) ; local b3Box = EgtGetBBoxRef( nSurfId, GDB_BB.STANDARD, frOCS) local dElev = b3Box:getMax():getZ() if nRawId then local _, dCenElev = WallLib.GetPointDirDepth( nRawId, ptC, vtN) if dCenElev and dCenElev > dElev then dElev = dCenElev end local dOffsX = min( 20, b3Box:getDimX() / 4) local _, dP1Elev = WallLib.GetPointDirDepth( nRawId, ptC + dOffsX * frOCS:getVersX(), vtN) if dP1Elev and dP1Elev > dElev then dElev = dP1Elev end local _, dP2Elev = WallLib.GetPointDirDepth( nRawId, ptC - dOffsX * frOCS:getVersX(), vtN) if dP2Elev and dP2Elev > dElev then dElev = dP2Elev end local dOffsY = min( 20, b3Box:getDimY() / 4) local _, dP3Elev = WallLib.GetPointDirDepth( nRawId, ptC + dOffsY * frOCS:getVersY(), vtN) if dP3Elev and dP3Elev > dElev then dElev = dP3Elev end local _, dP4Elev = WallLib.GetPointDirDepth( nRawId, ptC - dOffsY * frOCS:getVersY(), vtN) if dP4Elev and dP4Elev > dElev then dElev = dP4Elev end end return dElev end --------------------------------------------------------------------- function WallLib.GetFaceWithMostAdj( nSurfId, nPartId, bCompare3Fc, dCosSideAng) -- recupero il numero di facce local nFacCnt = EgtSurfTmFacetCount( nSurfId) if not dCosSideAng then dCosSideAng = -0.09 end -- recupero le normali delle facce local vvtN = {} for i = 1, nFacCnt do local _, vtN = EgtSurfTmFacetCenter( nSurfId, i - 1, GDB_ID.ROOT) vvtN[i] = vtN ; end -- adiacenze e sottosquadra delle facce local vAdj = {} local vUcut = {} local vOrtho = {} local vBlind = {} for i = 1, nFacCnt do -- recupero le adiacenze del loop esterno local vFacAdj = EgtSurfTmFacetAdjacencies( nSurfId, i - 1)[1] -- le conto local nCount = 0 for j = 1, #vFacAdj do if vFacAdj[j] >= 0 then nCount = nCount + 1 end end vAdj[i] = nCount -- ne determino eventuale sottosquadra ( dal valore passato o - 3deg) e ortogonalità local bUcut = false local bOrtho = true for j = 1, #vFacAdj do if vFacAdj[j] >= 0 then local vtN = vvtN[i] local vtN2 = vvtN[vFacAdj[j]+1] local dResV = vtN * vtN2 if dResV < dCosSideAng - GEO.EPS_SMALL then bUcut = true end if abs( dResV) > 2 * GEO.EPS_SMALL then bOrtho = false end end end -- verifico se schermata da altra faccia local bBlind = false for j = 1, nFacCnt do if i ~= j then if vvtN[i] * vvtN[j] < -0.5 then bBlind = true end end end -- assegno i risultati vUcut[i] = bUcut vOrtho[i] = bOrtho vBlind[i] = bBlind end -- se 4 facce tutte con adiacenza 2, allora è un tunnel if nFacCnt == 4 then if vAdj[1] == 2 and vAdj[2] == 2 and vAdj[3] == 2 and vAdj[4] == 2 then -- se tutte le facce sono ortogonali tra loro esco con un flag che ne indica questa propietà if vOrtho[1] == true and vOrtho[2] == true and vOrtho[3] == true and vOrtho[4] == true then return -1, GEO.INFINITO, true else return -1, GEO.INFINITO end end end -- se 3 facce con una che ha 2 adiacenze e le altre hanno 1 adiacenza, allora è una semi-fessura if bCompare3Fc and nFacCnt == 3 then local nCount2Adc = 0 local nCount1Adc = 0 -- ottengo il numero di facce con due adiacenze e il numero di facce con una adiacenza for i = 1, #vAdj do if vAdj[i] == 2 then nCount2Adc = nCount2Adc + 1 elseif vAdj[i] == 1 then nCount1Adc = nCount1Adc + 1 end end -- se il numero di adiacenze corrisponde if nCount2Adc == 1 and nCount1Adc == 2 then if vOrtho[1] == true and vOrtho[2] == true and vOrtho[3] == true then return -1, GEO.INFINITO, true else return -1, GEO.INFINITO end end end -- recupero le facce non in sottosquadra e con il maggior numero di adiacenze local nFacInd = {} local nMaxAdj = -1 local nSupAdj = -1 for i = 1, nFacCnt do if not vUcut[i] and not vBlind[i] then if vAdj[i] >= nMaxAdj and vAdj[i] > 0 then table.insert( nFacInd, i - 1) nMaxAdj = vAdj[i] elseif vAdj[i] > 0 then table.insert( nFacInd, i - 1) end end if vAdj[i] > nSupAdj then nSupAdj = vAdj[i] end end -- verifico non ci sia una faccia in sottosquadra con adiacenza superiore if nSupAdj > nMaxAdj then return -2, GEO.INFINITO end -- premio la faccia con minore elevazione local nFacOpt, nFacOpt2 local nOptAdj, nOptAdj2 local dMinElev, dMinElev2 = GEO.INFINITO, GEO.INFINITO for i = 1, #nFacInd do local dElev = WallLib.GetFaceElevation( nSurfId, nFacInd[i], nPartId) if dElev < dMinElev and ( not nOptAdj or vAdj[nFacInd[i]+1] >= nOptAdj) then if dMinElev < dMinElev2 then nFacOpt2 = nFacOpt nOptAdj2 = nOptAdj dMinElev2 = dMinElev end nFacOpt = nFacInd[i] nOptAdj = vAdj[nFacInd[i]+1] dMinElev = dElev elseif dElev < dMinElev2 and ( not nOptAdj2 or vAdj[nFacInd[i]+1] >= nOptAdj2) then nFacOpt2 = nFacInd[i] nOptAdj2 = vAdj[nFacInd[i]+1] dMinElev2 = dElev end end return nFacOpt, dMinElev, nFacOpt2, dMinElev2 end --------------------------------------------------------------------- function WallLib.GetFaceHvRefDim( nSurfId, nFacet) -- recupero centro e normale della faccia local ptC, vtN = EgtSurfTmFacetCenter( nSurfId, nFacet, GDB_ID.ROOT) if not ptC or not vtN then return end -- riferimento tipo OCS della faccia (X orizz, Y max pendenza, Z normale) local frHV = Frame3d( ptC, vtN) if frHV:getVersY():getZ() < 0 then frHV:rotate( ptC, vtN, 180) end -- determino l'ingombro in questo riferimento local b3HV = EgtSurfTmGetFacetBBoxRef( nSurfId, nFacet, GDB_BB.STANDARD, frHV) -- aggiusto l'origine del riferimento per metterlo nel centro del box local ptBoxCen = b3HV:getCenter() frHV:move( ptBoxCen:getX() * frHV:getVersX() + ptBoxCen:getY() * frHV:getVersY()) -- restituisco i valori calcolati return frHV, b3HV:getDimX(), b3HV:getDimY() end --------------------------------------------------------------------- function WallLib.GetNearestParalOpposite( vtRef) -- devo confrontare la componente orizzontale con quella verticale local dHorSq = vtRef:getX() * vtRef:getX() + vtRef:getY() * vtRef:getY() local dVertSq =vtRef:getZ() * vtRef:getZ() -- se prevalente la componente orizzontale if dHorSq >= dVertSq then if abs( vtRef:getX()) > abs( vtRef:getY()) then if vtRef:getX() > 0 then return MCH_MILL_FU.PARAL_LEFT else return MCH_MILL_FU.PARAL_RIGHT end else if vtRef:getY() > 0 then return MCH_MILL_FU.PARAL_FRONT else return MCH_MILL_FU.PARAL_BACK end end -- altrimenti prevale la verticale else if vtRef:getZ() > 0 then return MCH_MILL_FU.PARAL_DOWN else return MCH_MILL_FU.PARAL_TOP end end return nil end --------------------------------------------------------------------- function WallLib.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 -- devo confrontare la componente orizzontale con quella verticale local dHorSq = vtMyRef:getX() * vtMyRef:getX() + vtMyRef:getY() * vtMyRef:getY() local dVertSq = vtMyRef:getZ() * vtMyRef:getZ() -- se prevalente la componente orizzontale if dHorSq >= dVertSq then if abs( vtMyRef:getX()) >= abs( vtMyRef:getY()) then if vtMyRef:getX() > 0 then return MCH_MILL_FU.ORTHO_LEFT else return MCH_MILL_FU.ORTHO_RIGHT end else if vtMyRef:getY() > 0 then return MCH_MILL_FU.ORTHO_FRONT else return MCH_MILL_FU.ORTHO_BACK end end -- altrimenti prevale la verticale else if vtMyRef:getZ() > 0 then return MCH_MILL_FU.ORTHO_DOWN else return MCH_MILL_FU.ORTHO_TOP end end return nil end --------------------------------------------------------------------- function WallLib.TestElleShape3( nIdGeom, nNumFacet) -- valida solo nel caso di tre facce if nNumFacet ~= 3 then return false end -- determino se L con una faccia terminale o U con tre facce local bIsL = true for i = 1, 3 do local vFacAdj = EgtSurfTmFacetAdjacencies( nIdGeom, i - 1)[1] -- le conto local nCount = 0 for j = 1, #vFacAdj do if vFacAdj[j] >= 0 then nCount = nCount + 1 end end if nCount == 1 then bIsL = false break end end return bIsL end --------------------------------------------------------------------- function WallLib.TestElleShape4( nIdGeom, nNumFacet) -- valida solo nel caso di quattro facce if nNumFacet ~= 4 then return false end -- determino se L con due facce terminali o O local nFac3Adj = 0 local dMinArea3 = GEO.INFINITO * GEO.INFINITO local dMaxArea2 = 0 for i = 1, 4 do local vFacAdj = EgtSurfTmFacetAdjacencies( nIdGeom, i - 1)[1] -- le conto local nCount = 0 for j = 1, #vFacAdj do if vFacAdj[j] >= 0 then nCount = nCount + 1 end end local _, dH, dV = EgtSurfTmFacetMinAreaRectangle( nIdGeom, i - 1, GDB_ID.ROOT) local dArea = dH * dV if nCount == 2 then dMaxArea2 = max( dMaxArea2, dArea) elseif nCount == 3 then dMinArea3 = min( dMinArea3, dArea) nFac3Adj = nFac3Adj + 1 end end if nFac3Adj ~= 2 then return false end -- verifico se L profonda oppure lunga if dMinArea3 < dMaxArea2 then return 1 else return 2 end end --------------------------------------------------------------------- -- Funzione per determinare se la faccia ha lati molto corti (trascurabili) ed è quindi approssimabile ad una 3 facce function WallLib.Is3EdgesApprox( Proc, nFacet, nAddGrpId) nAddGrpId = nAddGrpId or WallLib.GetAddGroup( Proc.PartId) -- recupero il contorno della faccia local nContourId, nContourCnt = EgtExtractSurfTmFacetLoops( Proc.Id, nFacet, nAddGrpId) if not nContourId then return false end EgtMergeCurvesInCurveCompo( nContourId) -- recupero il numero di lati del contorno local _, nEntityCount = EgtCurveDomain( nContourId) if not nEntityCount then return false end -- se sono già tre, ho finito if nEntityCount == 3 then return true end -- rimuovo i lati molto corti dal conteggio totale local nEdges = nEntityCount for i = 1, nEntityCount do local dLength = EgtCurveCompoLength( nContourId, i - 1) if dLength < 15 then nEdges = nEdges - 1 end end -- verifico il numero significativo di lati local bResult = ( nEdges == 3) -- cancello tutti i contorni appena creati EgtErase( EgtTableFill( nContourId, nContourCnt)) if bResult then EgtOutLog( 'FreeContour : Face with ' .. tostring( nEntityCount) .. ' edges skipped (approx 3 edges)') end return bResult end ------------------------------------------------------------------------------------------------------------- -- restituisce le facce della parte interessate dalla feature Proc function WallLib.GetProcessAffectedFaces( Proc) local nBoxSolidId = EgtGetFirstNameInGroup( Proc.PartId or GDB_ID.NULL, 'Box') local b3Part = EgtGetBBoxGlob( nBoxSolidId, GDB_BB.STANDARD) local vtFacesAffected = { Top = false, Bottom = false, Front = false, Back = false, Left = false, Right = false} if Proc.Box and not Proc.Box:isEmpty() then if Proc.Box:getMax():getZ() > b3Part:getMax():getZ() - 500 * GEO.EPS_SMALL then vtFacesAffected.Top = true end if Proc.Box:getMin():getZ() < b3Part:getMin():getZ() + 500 * GEO.EPS_SMALL then vtFacesAffected.Bottom = true end if Proc.Box:getMin():getY() < b3Part:getMin():getY() + 500 * GEO.EPS_SMALL then vtFacesAffected.Front = true end if Proc.Box:getMax():getY() > b3Part:getMax():getY() - 500 * GEO.EPS_SMALL then vtFacesAffected.Back = true end if Proc.Box:getMin():getX() < b3Part:getMin():getX() + 500 * GEO.EPS_SMALL then vtFacesAffected.Left = true end if Proc.Box:getMax():getX() > b3Part:getMax():getX() - 500 * GEO.EPS_SMALL then vtFacesAffected.Right = true end end return vtFacesAffected end ------------------------------------------------------------------------------------------------------------- -- restituisce le distanze XY tra la feature Proc e le altre parti più vicine, secondo le direzioni Y-/Y+/X-/X+ function WallLib.GetProcessDistanceToNearestParts( Proc) local b3Proc = Proc.Box local ptMinProc = b3Proc:getMin() local ptMaxProc = b3Proc:getMax() local vtDistances = { Front = 0, Back = 0, Left = 0, Right = 0} if Proc.AffectedFaces.Front then vtDistances.Front = GEO.INFINITO end if Proc.AffectedFaces.Back then vtDistances.Back = GEO.INFINITO end if Proc.AffectedFaces.Left then vtDistances.Left = GEO.INFINITO end if Proc.AffectedFaces.Right then vtDistances.Right = GEO.INFINITO end local nRawId = EgtGetFirstRawPart() local nPartId = EgtGetFirstPartInRawPart( nRawId) while nPartId do if Proc.PartId ~= nPartId then local nBoxSolidId = EgtGetFirstNameInGroup( nPartId, 'Box') local b3Solid = EgtGetBBoxGlob( nBoxSolidId or GDB_ID.NULL, GDB_BB.STANDARD) b3Solid:expand( - 10 * GEO.EPS_SMALL) local ptMinPart = b3Solid:getMin() local ptMaxPart = b3Solid:getMax() if Proc.AffectedFaces.Front then if OverlapsXY( b3Proc, b3Solid) then vtDistances.Front = 0 elseif OverlapsX( b3Proc, b3Solid) then if ptMaxPart:getY() < ptMinProc:getY() + 100 * GEO.EPS_SMALL then vtDistances.Front = min( vtDistances.Front, max( 0, ptMinProc:getY() - ptMaxPart:getY())) end end end if Proc.AffectedFaces.Back then if OverlapsXY( b3Proc, b3Solid) then vtDistances.Back = 0 elseif OverlapsX( b3Proc, b3Solid) then if ptMaxProc:getY() < ptMinPart:getY() + 100 * GEO.EPS_SMALL then vtDistances.Back = min( vtDistances.Back, max( 0, ptMinPart:getY() - ptMaxProc:getY())) end end end if Proc.AffectedFaces.Left then if OverlapsXY( b3Proc, b3Solid) then vtDistances.Left = 0 elseif OverlapsY( b3Proc, b3Solid) then if ptMaxPart:getX() < ptMinProc:getX() + 100 * GEO.EPS_SMALL then vtDistances.Left = min( vtDistances.Left, max( 0, ptMinProc:getX() - ptMaxPart:getX())) end end end if Proc.AffectedFaces.Right then if OverlapsXY( b3Proc, b3Solid) then vtDistances.Right = 0 elseif OverlapsY( b3Proc, b3Solid) then if ptMaxProc:getX() < ptMinPart:getX() + 100 * GEO.EPS_SMALL then vtDistances.Right = min( vtDistances.Right, max( 0, ptMinPart:getX() - ptMaxProc:getX())) end end end end nPartId = EgtGetNextPartInRawPart( nPartId) end return vtDistances end ------------------------------------------------------------------------------------------------------------- -- restituisce le distanze XY tra la feature Proc e il termine del grezzo, secondo le direzioni Y-/Y+/X-/X+ function WallLib.GetProcessDistanceToRawPart( Proc, b3Raw) local b3Proc = Proc.Box local ptMinProc = b3Proc:getMin() local ptMaxProc = b3Proc:getMax() local ptMinRaw = b3Raw:getMin() local ptMaxRaw = b3Raw:getMax() local vtDistances = { Front = GEO.INFINITO, Back = GEO.INFINITO, Left = GEO.INFINITO, Right = GEO.INFINITO} if EnclosesXY( b3Raw, b3Proc) then vtDistances.Front = max( 0, ptMinProc:getY() - ptMinRaw:getY()) vtDistances.Back = max( 0, ptMaxRaw:getY() - ptMaxProc:getY()) vtDistances.Left = max( 0, ptMinProc:getX() - ptMinRaw:getX()) vtDistances.Right = max( 0, ptMaxRaw:getX() - ptMaxProc:getX()) end return vtDistances end ------------------------------------------------------------------------------------------------------------- function WallLib.GetNailLockOutAreas( vProc) local vNLO = {} for i = 1, #vProc do if vProc[i].LockOut == 1 then local AuxId = EgtGetInfo( vProc[i].Id, 'AUXID', 'i') if AuxId then AuxId = AuxId + vProc[i].Id end if AuxId then local nAddGrpId = WallLib.GetAddGroup( vProc[i].PartId) local nSfrId = EgtSurfFlatRegion( nAddGrpId, {AuxId}) if nSfrId then table.insert( vNLO, nSfrId) end end end end return vNLO end ------------------------------------------------------------------------------------------------------------- -- restituisce vero se la feature con box b3Proc taglia l'intera sezione della parete, rappresentata dalle sue dimensioni W, H e L function WallLib.IsFeatureCuttingEntireSection( b3Proc, dRawW, dRawH, dRawL) return ( ( ( abs( b3Proc:getDimY() - dRawW) < 10 * GEO.EPS_SMALL or b3Proc:getDimY() > dRawW) or ( abs( b3Proc:getDimX() - dRawL) < 10 * GEO.EPS_SMALL or b3Proc:getDimX() > dRawL)) and (abs(b3Proc:getDimZ() - dRawH) < 10 * GEO.EPS_SMALL or b3Proc:getDimZ() > dRawH)) end ------------------------------------------------------------------------------------------------------------- -- Splitta una tabella in tabelle di dimensioni massima nMaxItemsCount function WallLib.SplitTableInChunks( Table, nChunkSize) local Result = {} local Chunk = {} for i, v in ipairs( Table) do table.insert( Chunk, v) if #Chunk == nChunkSize then table.insert( Result, Chunk) Chunk = {} end end -- se rimangono elementi, li aggiungo if #Chunk > 0 then table.insert( Result, Chunk) end return Result end ------------------------------------------------------------------------------------------------------------- return WallLib