-- 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.nFct do vAdj[i] = {} for j = i + 1, Proc.nFct 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.nFct do vAdj[i][i] = 0 for j = i + 1, Proc.nFct 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.nFct <= 1 then return end local vTriangularFaces = {} for i = 1, Proc.nFct 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.nFct 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 --------------------------------------------------------------------- -- 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.nFct <= 1 then return end local FacesByAdjacencyNumber = {} for i = 1, 10 do FacesByAdjacencyNumber[i] = {} end for i = 1, Proc.nFct 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.idPart, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD) local vAdj if Proc.AdjacencyMatrix then vAdj = Proc.AdjacencyMatrix else vAdj = FaceData.GetAdjacencyMatrix( Proc) end -- reset eventuali visualizzazioni facce a due colori EgtSurfTmResetTwoColors( Proc.id) for i = 1, Proc.nFct do Faces[i] = {} Faces[i].id = i - 1 Faces[i].ptCenter, Faces[i].vtN = EgtSurfTmFacetCenter( Proc.id, i - 1, GDB_ID.ROOT) if Proc.nFct < 6 then local frHV, dFaceWidth, dFaceHeight = BeamLib.GetFaceHvRefDim( Proc.id, i - 1, Part) -- frame OCS faccia Faces[i].vtFrameHV = frHV -- TODO valutare se Width e Height si possono rimuovere -- larghezza OCS faccia Faces[i].dWidth = dFaceWidth -- altezza OCS faccia Faces[i].dHeight = dFaceHeight -- elevazione calcolata rispetto al box della parte Faces[i].dElevation = EgtSurfTmFacetElevationInBBox( Proc.id, i - 1, b3Solid, true, GDB_ID.ROOT) -- TODO qui sarebbe meglio l'area vera e non quella del rettangolo minimo local _, dLongEdgeDimension, dShortEdgeDimension = EgtSurfTmFacetMinAreaRectangle( Proc.id, i - 1, GDB_ID.ROOT) Faces[i].dArea = dShortEdgeDimension * dLongEdgeDimension local nFaceType, vEdges = EgtSurfTmGetFacetOutlineInfo( Proc.id, i - 1, GDB_ID.ROOT) Faces[i].IsOkForMachining = nFaceType < 1 Faces[i].Edges = vEdges -- TODO valutare se fare un output unico alla fine o gestire log in altro modo EgtOutLog( 'Facet ' .. Faces[i].id .. ' of ' .. Proc.nFct - 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.nFct 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.idPart, 'Box') or GDB_ID.NULL, GDB_BB.STANDARD) if not ( Proc.Topology.bIsThrough and Proc.Topology.bAllRightAngles and Proc.nFct < 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.idPart) 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.nFct do EgtCutSurfTmPlane( TunnelAddedFaces.MiddleFaceTm.id, Proc.Faces[i].ptCenter, -Proc.Faces[i].vtN, false, GDB_ID.ROOT) end TunnelAddedFaces.MiddleFaceTm.Type = 'Tunnel' -- TODO aggiungere anche informazioni Edges tunnel?? return TunnelAddedFaces end ------------------------------------------------------------------------------------------------------------- local function GetBottomFace( Proc) local BottomFace = {} if Proc.Topology.sFamily == 'Tunnel' then return nil elseif not ( Proc.Topology.sFamily == 'Rabbet' or Proc.Topology.sFamily == 'VGroove' or Proc.Topology.sFamily == 'Groove' or Proc.Topology.sFamily == '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.nFct - 1] if #BottomFaces > 1 then local dMinElevation = GEO.INFINITO for i = 1, #BottomFaces do if Proc.Faces[BottomFaces[i].id + 1].dElevation < dMinElevation then dMinElevation = Proc.Faces[BottomFaces[i].id + 1].dElevation BottomFace = Proc.Faces[BottomFaces[i].id + 1] end end else BottomFace = BottomFaces[1] end end if not BottomFace.IsOkForMachining then return nil end BottomFace.Type = 'Bottom' BottomFace.MainEdges = {} BottomFace.MainEdges.LongEdges = {} BottomFace.MainEdges.SideEdges = {} local EdgesSortedByGreatestLength = {} for i = 1, #BottomFace.Edges do EdgesSortedByGreatestLength[i] = {} EdgesSortedByGreatestLength[i].nIndex = i EdgesSortedByGreatestLength[i].dLength = BottomFace.Edges[i].Len end table.sort( EdgesSortedByGreatestLength, function (a, b) return a.dLength > b.dLength end) local nFirstLongEdgeIndex = EdgesSortedByGreatestLength[1].nIndex for i = 1, #BottomFace.Edges do local nPreviousEdgeIndex = i - 1 if i == 1 then nPreviousEdgeIndex = #BottomFace.Edges end local nNextEdgeIndex = i + 1 if i == #BottomFace.Edges then nNextEdgeIndex = 1 end local CurrentEdge = {} CurrentEdge.idAdjacentFace = BottomFace.Edges[i].Adj CurrentEdge.vtToolDirection = Vector3d( BottomFace.Edges[i].Norm) CurrentEdge.dLength = BottomFace.Edges[i].Len CurrentEdge.dElevation = BottomFace.Edges[i].Elev CurrentEdge.bIsOpen = BottomFace.Edges[i].Open CurrentEdge.bIsStartOpen = BottomFace.Edges[nPreviousEdgeIndex].Open CurrentEdge.bIsEndOpen = BottomFace.Edges[nNextEdgeIndex].Open if i == nFirstLongEdgeIndex then BottomFace.MainEdges.LongEdges[1] = CurrentEdge BottomFace.MainEdges.LongEdges[1].Type = 'Long' elseif nNextEdgeIndex == nFirstLongEdgeIndex then BottomFace.MainEdges.SideEdges[1] = CurrentEdge BottomFace.MainEdges.SideEdges[1].Type = 'Side' elseif nPreviousEdgeIndex == nFirstLongEdgeIndex then BottomFace.MainEdges.SideEdges[2] = CurrentEdge BottomFace.MainEdges.SideEdges[2].Type = 'Side' else BottomFace.MainEdges.LongEdges[2] = CurrentEdge BottomFace.MainEdges.LongEdges[2].Type = 'Long' end end return BottomFace end ------------------------------------------------------------------------------------------------------------- local function GetLongFaces( Proc, MainFaces) local LongFaces = {} if Proc.nFct > 5 then error( 'GetLongFaces : Topology not implemented') end local BottomFace = MainFaces.BottomFace or GetBottomFace( Proc) local nFirstLongFaceId = GDB_ID.NULL local nSecondLongFaceId = GDB_ID.NULL if not BottomFace then local FacesSortedByGreatestArea = {} for i = 1, Proc.nFct do FacesSortedByGreatestArea[i] = {} FacesSortedByGreatestArea[i].id = Proc.Faces[i].id FacesSortedByGreatestArea[i].dArea = Proc.Faces[i].dArea end table.sort( FacesSortedByGreatestArea, function (a, b) return a.dArea > b.dArea end) nFirstLongFaceId = FacesSortedByGreatestArea[1].id local FacesNotAdjacent = GetNotAdjacentFaces( Proc, nFirstLongFaceId) nSecondLongFaceId = FacesNotAdjacent[1].id else if not BottomFace.MainEdges.LongEdges[1].bIsOpen then nFirstLongFaceId = BottomFace.MainEdges.LongEdges[1].idAdjacentFace end if not BottomFace.MainEdges.LongEdges[2].bIsOpen then nSecondLongFaceId = BottomFace.MainEdges.LongEdges[2].idAdjacentFace end end if nFirstLongFaceId > -1 then LongFaces[1] = Proc.Faces[nFirstLongFaceId + 1] end if nSecondLongFaceId > -1 then LongFaces[2] = Proc.Faces[nSecondLongFaceId + 1] end for i = 1, #LongFaces do LongFaces[i].Type = 'Long' LongFaces[i].MainEdges = {} LongFaces[i].MainEdges.SideEdges = {} LongFaces[i].MainEdges.OppositeEdges = {} for j = 1, #LongFaces[i].Edges do local nPreviousEdgeIndex = j - 1 if j == 1 then nPreviousEdgeIndex = #LongFaces[1].Edges end local nNextEdgeIndex = j + 1 if j == #LongFaces[i].Edges then nNextEdgeIndex = 1 end local CurrentEdge = {} CurrentEdge.idAdjacentFace = LongFaces[i].Edges[j].Adj CurrentEdge.vtToolDirection = Vector3d( LongFaces[i].Edges[j].Norm) CurrentEdge.dLength = LongFaces[i].Edges[j].Len CurrentEdge.dElevation = LongFaces[i].Edges[j].Elev CurrentEdge.bIsOpen = LongFaces[i].Edges[j].Open CurrentEdge.bIsStartOpen = LongFaces[i].Edges[nPreviousEdgeIndex].Open CurrentEdge.bIsEndOpen = LongFaces[i].Edges[nNextEdgeIndex].Open if Proc.Topology.sFamily == 'Tunnel' then if CurrentEdge.idAdjacentFace > -1 then table.insert( LongFaces[i].MainEdges.SideEdges, CurrentEdge) else table.insert( LongFaces[i].MainEdges.OppositeEdges, CurrentEdge) end else if CurrentEdge.idAdjacentFace == BottomFace.id then LongFaces[i].MainEdges.BottomEdge = CurrentEdge LongFaces[i].MainEdges.BottomEdge.Type = 'Bottom' elseif LongFaces[i].Edges[nNextEdgeIndex].Adj == BottomFace.id then LongFaces[i].MainEdges.SideEdges[1] = CurrentEdge LongFaces[i].MainEdges.SideEdges[1].Type = 'Side' elseif LongFaces[i].Edges[nPreviousEdgeIndex].Adj == BottomFace.id then LongFaces[i].MainEdges.SideEdges[2] = CurrentEdge LongFaces[i].MainEdges.SideEdges[2].Type = 'Side' else table.insert( LongFaces[i].MainEdges.OppositeEdges, CurrentEdge) LongFaces[i].MainEdges.OppositeEdges[#LongFaces[i].MainEdges.OppositeEdges].Type = 'Opposite' end end end end return LongFaces end ------------------------------------------------------------------------------------------------------------- local function GetSideFaces( Proc, MainFaces) local SideFaces = {} if Proc.nFct > 5 then error( 'GetSideFaces : Topology not implemented') end -- TODO da sistemare come Bottom e Long local idBottomFace = GDB_ID.NULL if Proc.Topology.sFamily ~= '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.nFct 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]) SideFaces[#SideFaces].Type = 'Side' end end end -- TODO aggiungere anche informazioni Edges?? 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.sFamily == 'Rabbet' or Proc.Topology.sFamily == 'VGroove' or Proc.Topology.sFamily == 'Groove' or Proc.Topology.sFamily == 'Pocket' or Proc.Topology.sFamily == 'Tunnel' then if Proc.Topology.bIsThrough and Proc.Topology.bAllRightAngles and Proc.nFct < 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