-- FaceData.lua by Egalware s.r.l. 2024/04/18 -- Libreria lettura o calcolo dati e proprietà delle facce di una trimesh -- 2024/04/02 PRIMA VERSIONE CALCOLO LAVORAZIONI CON STRATEGIE -- Tabella per definizione modulo local FaceData = {} -- carico librerie local BeamLib = require( 'BeamLib') --------------------------------------------------------------------- -- restituisce la matrice delle adiacenze di Proc dove i e j sono le facce e a(ij) è l'angolo tra di esse; 0 se nessuna adiacenza function FaceData.GetAdjacencyMatrix( Proc) local vAdj = {} -- essendo la matrice simmetrica a diagonale nulla, ne calcolo solo la metà superiore for i = 1, Proc.Fct do vAdj[i] = {} for j = i + 1, Proc.Fct do _, _, _, vAdj[i][j] = EgtSurfTmFacetsContact( Proc.Id, i - 1, j - 1, GDB_ID.ROOT) if not vAdj[i][j] then vAdj[i][j] = 0 end end end -- riempio di conseguenza il resto della matrice for i = 1, Proc.Fct do vAdj[i][i] = 0 for j = i + 1, Proc.Fct do vAdj[j][i] = vAdj[i][j] end end return vAdj end --------------------------------------------------------------------- -- restituisce un vettore con gli indici (0 based) delle facce triangolari (o quasi) di Proc function FaceData.GetTriangularFaces( Proc) -- se la feature ha una sola faccia, esco subito if Proc.Fct <= 1 then return end local vTriangularFaces = {} for i = 1, Proc.Fct do if BeamLib.Is3EdgesApprox( Proc, i - 1) then table.insert( vTriangularFaces, i - 1) end end return vTriangularFaces end ------------------------------------------------------------------------------------------------------------- local function GetNotAdjacentFaces( Proc, idFace) local NotAdjacentFaces = {} for i = 1, Proc.Fct do if Proc.Faces[i].Id ~= idFace then local bIsAdjacent = false for j = 1, #Proc.Faces[idFace + 1].Adjacencies do if Proc.Faces[i].Id == Proc.Faces[idFace + 1].Adjacencies[j] then bIsAdjacent = true end end if not bIsAdjacent then table.insert( NotAdjacentFaces, Proc.Faces[i]) end end end return NotAdjacentFaces end --------------------------------------------------------------------- local function GetFacesContactLength( Proc, idFace1, idFace2) local _, ptP1, ptP2 = EgtSurfTmFacetsContact( Proc.Id, idFace1, idFace2, GDB_ID.ROOT) local dLength = 0 if ptP1 and ptP2 then dLength = dist( ptP1, ptP2) else -- TODO se non trova lunghezza serve dare messaggio di errore? end return dLength end --------------------------------------------------------------------- -- restituisce una tabella che correla numero di adiacenze e id delle facce con quello stesso numero di adiacenze function FaceData.GetFacesByAdjacencyNumber( Proc) -- se la feature ha una sola faccia, esco subito if Proc.Fct <= 1 then return end local FacesByAdjacencyNumber = {} for i = 1, 10 do FacesByAdjacencyNumber[i] = {} end for i = 1, Proc.Fct do table.insert( FacesByAdjacencyNumber[#Proc.Faces[i].Adjacencies], Proc.Faces[i]) end return FacesByAdjacencyNumber end ------------------------------------------------------------------------------------------------------------- function FaceData.GetFacesInfo( Proc, Part) EgtOutLog( '---Faces START---') local Faces = {} local b3Solid = EgtGetBBoxGlob( EgtGetFirstNameInGroup( Proc.PartId, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD) local vAdj if Proc.AdjacencyMatrix then vAdj = Proc.AdjacencyMatrix else vAdj = FaceData.GetAdjacencyMatrix( Proc) end for i = 1, Proc.Fct do -- reset colore faccia EgtSurfTmSetFaceColor( Proc.Id, i - 1, 0) Faces[i] = {} Faces[i].Id = i - 1 Faces[i].PtCenter, Faces[i].VtN = EgtSurfTmFacetCenter( Proc.Id, i - 1, GDB_ID.ROOT) if Proc.Fct < 6 then local frHV, dFaceWidth, dFaceHeight = BeamLib.GetFaceHvRefDim( Proc.Id, i - 1, Part) -- frame OCS faccia Faces[i].FrameHV = frHV -- larghezza OCS faccia Faces[i].Width = dFaceWidth -- altezza OCS faccia Faces[i].Height = dFaceHeight -- elevazione calcolata rispetto al box della parte Faces[i].Elevation = EgtSurfTmFacetElevationInBBox( Proc.Id, i - 1, b3Solid, true, GDB_ID.ROOT) -- TODO valutare se fare un output unico alla fine o gestire log in altro modo EgtOutLog( 'Facet ' .. Faces[i].Id .. ' of ' .. Proc.Fct - 1) EgtOutLog( ' VtN: ' .. tostring( Faces[i].VtN)) -- adiacenze della faccia -- TODO chiamarle in modo che si capisca che sono solo gli id e non l'intero oggetto faccia Faces[i].Adjacencies = {} for j = 1, Proc.Fct do if vAdj[i][j] and vAdj[i][j] ~= 0 and ( i ~= j) then table.insert( Faces[i].Adjacencies, j - 1) if EgtGetDebugLevel() >= 3 then EgtOutLog( ' Adjacent to facet: ' .. j - 1) end end end end end EgtOutLog( '---Faces END---') return Faces end ------------------------------------------------------------------------------------------------------------- -- TODO valutare refactoring per mettere i calcoli del tunnel in una funzione -- TODO valutare se restituire anche altre informazioni oltre alle facce (es: box); magari metterle in funzione a parte chiamata allo stesso livello della GetMainFAces local function GetTunnelFaces( Proc) local TunnelAddedFaces = {} -- TODO scrivere il box della parte nella Proc o fare funzione per recuperarlo local b3Part = EgtGetBBoxGlob( EgtGetFirstNameInGroup( Proc.PartId, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD) if not ( Proc.Topology.IsThrough and Proc.Topology.AllRightAngles and Proc.Fct < 5) then error( 'GetTunnelFaces : Topology not implemented') end -- direzione del tunnel local vtTunnelDirection = Proc.Faces[1].VtN ^ Proc.Faces[ Proc.Faces[1].Adjacencies[1] + 1].VtN -- centro del tunnel local frTunnel = Frame3d( Proc.Faces[1].PtCenter, vtTunnelDirection) local b3Tunnel = EgtGetBBoxRef( Proc.Id, GDB_BB.STANDARD, frTunnel) local ptTunnelCenter = b3Tunnel:getCenter() ptTunnelCenter:toGlob( frTunnel) -- recupero gruppo per geometria addizionale local nAddGrpId = BeamLib.GetAddGroup( Proc.PartId) if not nAddGrpId then -- TODO gestire meglio questo errore. Non conviene creare e verificare all'inizio se il gruppo esiste? EgtOutLog( 'Error : missing AddGroup') return TunnelAddedFaces end -- faccia centrale, si crea larga come la parte e poi si trimma TunnelAddedFaces.MiddleFaceTm = {} TunnelAddedFaces.MiddleFaceTm.Id = EgtSurfTmPlaneInBBox( nAddGrpId, ptTunnelCenter, vtTunnelDirection, b3Part, GDB_ID.ROOT) -- TODO se non si riesce a costruire la faccia bisogna dare errore o semplicemente non ritornarla?? for i = 1, Proc.Fct do EgtCutSurfTmPlane( TunnelAddedFaces.MiddleFaceTm.Id, Proc.Faces[i].PtCenter, -Proc.Faces[i].VtN, false, GDB_ID.ROOT) end return TunnelAddedFaces end ------------------------------------------------------------------------------------------------------------- local function GetBottomFace( Proc) local BottomFace = {} if Proc.Topology.Family == 'Tunnel' then return nil elseif not ( Proc.Topology.Family == 'Rabbet' or Proc.Topology.Family == 'VGroove' or Proc.Topology.Family == 'Groove' or Proc.Topology.Family == 'Pocket') then error( 'GetBottomFace : Topology not implemented') end -- la faccia di fondo ha sempre Fct - 1 adiacenze. Se si trovano più facce di fondo si sceglie quella con minor elevazione local FacesByAdjacencyNumber = FaceData.GetFacesByAdjacencyNumber( Proc) if FacesByAdjacencyNumber then local BottomFaces = FacesByAdjacencyNumber[ Proc.Fct - 1] if #BottomFaces > 1 then local dMinElevation = GEO.INFINITO for i = 1, #BottomFaces do if Proc.Faces[BottomFaces[i].Id + 1].Elevation < dMinElevation then dMinElevation = Proc.Faces[BottomFaces[i].Id + 1].Elevation BottomFace = Proc.Faces[BottomFaces[i].Id + 1] end end else BottomFace = BottomFaces[1] end end return BottomFace end ------------------------------------------------------------------------------------------------------------- local function GetLongFaces( Proc, MainFaces) local LongFaces = {} if Proc.Fct > 5 then error( 'GetLongFaces : Topology not implemented') end local idBottomFace = GDB_ID.NULL local idTunnelMiddleFace = GDB_ID.NULL if Proc.Topology.Family == 'Tunnel' then if MainFaces.TunnelAddedFaces then idTunnelMiddleFace = MainFaces.TunnelAddedFaces.MiddleFaceTm.Id else local TunnelAddedFaces = GetTunnelFaces( Proc) idTunnelMiddleFace = TunnelAddedFaces.MiddleFaceTm.Id or idTunnelMiddleFace end else if MainFaces.BottomFace then idBottomFace = MainFaces.BottomFace.Id else local BottomFace = GetBottomFace( Proc) idBottomFace = BottomFace.Id or idBottomFace end end local FacesToAnalyze = {} for i = 1, Proc.Fct do if Proc.Faces[i].Id ~= idBottomFace then table.insert( FacesToAnalyze, Proc.Faces[i]) if Proc.Topology.Family == 'Tunnel' then -- TODO questo non funziona nei tunnel, da modificare FacesToAnalyze[#FacesToAnalyze].LengthOnMainFace = GetFacesContactLength( Proc, idTunnelMiddleFace, Proc.Faces[i].Id) else FacesToAnalyze[#FacesToAnalyze].LengthOnMainFace = GetFacesContactLength( Proc, idBottomFace, Proc.Faces[i].Id) end end end table.sort( FacesToAnalyze, function( a, b) return a.LengthOnMainFace > b.LengthOnMainFace end) -- la prima faccia lunga è sempre la prima della lista LongFaces[1] = FacesToAnalyze[1] -- si cerca l'eventuale seconda faccia lunga, ossia quella non adiacente alla prima if Proc.Fct > 3 then local NotAdjacentFaces = GetNotAdjacentFaces(Proc, LongFaces[1].Id) if #NotAdjacentFaces > 0 then LongFaces[2] = NotAdjacentFaces[1] end end return LongFaces end ------------------------------------------------------------------------------------------------------------- local function GetSideFaces( Proc, MainFaces) local SideFaces = {} if Proc.Fct > 5 then error( 'GetSideFaces : Topology not implemented') end local idBottomFace = GDB_ID.NULL if Proc.Topology.Family ~= 'Tunnel' then if MainFaces.BottomFace then idBottomFace = MainFaces.BottomFace.Id else local BottomFace = GetBottomFace( Proc) idBottomFace = BottomFace.Id or idBottomFace end end local LongFaces = {} if MainFaces.LongFaces then LongFaces = MainFaces.LongFaces else LongFaces = GetLongFaces( Proc, MainFaces) end for i = 1, Proc.Fct do if not idBottomFace or i ~= ( idBottomFace + 1) then local bIsSideFace = true for j = 1, #LongFaces do if Proc.Faces[i].Id == LongFaces[j].Id then bIsSideFace = false break end end if bIsSideFace then table.insert( SideFaces, Proc.Faces[i]) end end end return SideFaces end ------------------------------------------------------------------------------------------------------------- -- recupero facce principali della feature, in base alla topologia function FaceData.GetMainFaces( Proc) EgtOutLog( '---MainFaces START---') local MainFaces = {} -- CASO 1 : Feature tipo LapJoint if Proc.Topology.Family == 'Rabbet' or Proc.Topology.Family == 'VGroove' or Proc.Topology.Family == 'Groove' or Proc.Topology.Family == 'Pocket' or Proc.Topology.Family == 'Tunnel' then if Proc.Topology.IsThrough and Proc.Topology.AllRightAngles and Proc.Fct < 5 then MainFaces.TunnelAddedFaces = GetTunnelFaces( Proc) end MainFaces.BottomFace = GetBottomFace( Proc) MainFaces.LongFaces = GetLongFaces( Proc, MainFaces) MainFaces.SideFaces = GetSideFaces( Proc, MainFaces) -- TODO funzione apposita per informazioni log? if EgtGetDebugLevel() >= 3 then if MainFaces.BottomFace then -- colore differente per la faccia di fondo EgtSurfTmSetFaceColor( Proc.Id, MainFaces.BottomFace.Id, 1) EgtOutLog( 'Bottom Face : ' .. MainFaces.BottomFace.Id) end if MainFaces.LongFaces then for i = 1, #MainFaces.LongFaces do EgtOutLog( 'Long Face : ' .. MainFaces.LongFaces[i].Id) end end if MainFaces.SideFaces then for i = 1, #MainFaces.SideFaces do EgtOutLog( 'Side Face : ' .. MainFaces.SideFaces[i].Id) end end if MainFaces.TunnelAddedFaces then EgtOutLog( 'Middle Face (Trimesh): ' .. MainFaces.TunnelAddedFaces.MiddleFaceTm.Id) end end else EgtOutLog( '---MainFaces NOT NEEDED---') end EgtOutLog( '---MainFaces END---') return MainFaces end ------------------------------------------------------------------------------------------------------------- return FaceData