topologicpy 0.8.29__py3-none-any.whl → 0.8.30__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
topologicpy/Topology.py CHANGED
@@ -2118,17 +2118,15 @@ class Topology():
2118
2118
  return Topology.ByDXFFile(file, sides=sides)
2119
2119
 
2120
2120
  @staticmethod
2121
- def ByIFCFile(file, includeTypes=[], excludeTypes=[], transferDictionaries=False,
2121
+ def ByIFCFile(ifc_file, includeTypes=[], excludeTypes=[], transferDictionaries=False,
2122
2122
  removeCoplanarFaces=False,
2123
- xMin: float = -0.5, yMin: float = -0.5, zMin: float = -0.5,
2124
- xMax: float = 0.5, yMax: float = 0.5, zMax: float = 0.5,
2125
2123
  epsilon=0.0001, tolerance=0.0001, silent=False):
2126
2124
  """
2127
2125
  Create a topology by importing it from an IFC file.
2128
2126
 
2129
2127
  Parameters
2130
2128
  ----------
2131
- file : file object
2129
+ ifc_file : file object
2132
2130
  The input IFC file.
2133
2131
  includeTypes : list , optional
2134
2132
  The list of IFC object types to include. It is case insensitive. If set to an empty list, all types are included. The default is [].
@@ -2138,18 +2136,6 @@ class Topology():
2138
2136
  If set to True, the dictionaries from the IFC file will be transferred to the topology. Otherwise, they won't. The default is False.
2139
2137
  removeCoplanarFaces : bool , optional
2140
2138
  If set to True, coplanar faces are removed. Otherwise they are not. The default is False.
2141
- xMin : float, optional
2142
- The desired minimum value to assign for a vertex's X coordinate. The default is -0.5.
2143
- yMin : float, optional
2144
- The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5.
2145
- zMin : float, optional
2146
- The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5.
2147
- xMax : float, optional
2148
- The desired maximum value to assign for a vertex's X coordinate. The default is 0.5.
2149
- yMax : float, optional
2150
- The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5.
2151
- zMax : float, optional
2152
- The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5.
2153
2139
  epsilon : float , optional
2154
2140
  The desired epsilon (another form of tolerance) for finding if two faces are coplanar. The default is 0.01.
2155
2141
  tolerance : float , optional
@@ -2162,119 +2148,89 @@ class Topology():
2162
2148
  The created list of topologies.
2163
2149
 
2164
2150
  """
2151
+ import multiprocessing
2165
2152
  import ifcopenshell
2166
2153
  import ifcopenshell.geom
2167
- from topologicpy.Vertex import Vertex
2168
- from topologicpy.Wire import Wire
2169
2154
  from topologicpy.Face import Face
2170
2155
  from topologicpy.Cluster import Cluster
2171
2156
  from topologicpy.Topology import Topology
2172
2157
  from topologicpy.Dictionary import Dictionary
2173
- from concurrent.futures import ThreadPoolExecutor
2174
- import random
2175
-
2176
- # Early filtering in parallel (safe)
2177
- def is_valid(product):
2178
- is_a = product.is_a().lower()
2179
- include = [t.lower() for t in includeTypes]
2180
- exclude = [t.lower() for t in excludeTypes]
2181
- return (not include or is_a in include) and (is_a not in exclude)
2182
-
2183
- with ThreadPoolExecutor() as executor:
2184
- products = list(file.by_type("IfcProduct"))
2185
- valid_entities = list(executor.map(lambda p: p if is_valid(p) else None, products))
2186
- valid_entities = [e for e in valid_entities if e is not None]
2187
-
2158
+
2159
+ # 1 Guard against including and excluding the same types
2160
+ includeTypes = [s.lower() for s in includeTypes]
2161
+ excludeTypes = [s.lower() for s in excludeTypes]
2162
+ if len(includeTypes) > 0 and len(excludeTypes) > 0:
2163
+ excludeTypes = [s for s in excludeTypes if s not in includeTypes]
2164
+ # 2 Setup geometry settings
2188
2165
  settings = ifcopenshell.geom.settings()
2189
2166
  settings.set(settings.USE_WORLD_COORDS, True)
2167
+ # A string representation of the OCC representation
2168
+ settings.set("iterator-output", ifcopenshell.ifcopenshell_wrapper.NATIVE)
2169
+ # A string representation of the OCC representation
2190
2170
  settings.set("iterator-output", ifcopenshell.ifcopenshell_wrapper.SERIALIZED)
2171
+
2172
+ # 3 Create iterator
2173
+ it = ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count())
2174
+ if not it.initialize():
2175
+ if not silent:
2176
+ print("Topology.ByIFCFile")
2177
+ raise RuntimeError("Geometry iterator failed to initialize")
2191
2178
 
2179
+ # 4) Loop over shapes
2192
2180
  topologies = []
2193
- for entity in valid_entities:
2194
- if not hasattr(entity, "Representation") or not entity.Representation:
2195
- continue
2196
- shape = ifcopenshell.geom.create_shape(settings, entity)
2197
- topology = None
2198
- if hasattr(shape.geometry, 'brep_data'):
2199
- brep_string = shape.geometry.brep_data
2200
- topology = Topology.ByBREPString(brep_string)
2201
- if not topology:
2202
- if not silent:
2203
- print(f"Topology.ByIFCFile - Warning: Could not convert entity {getattr(entity, 'GlobalId', 0)} to a topology. Skipping.")
2204
- continue
2181
+
2182
+ while True:
2183
+ shape = it.get()
2184
+ element = ifc_file.by_guid(shape.guid)
2185
+ element_type = element.is_a().lower()
2186
+ if ((element_type in includeTypes) or (len(includeTypes) == 0)) and ((not element_type in excludeTypes) or (len(excludeTypes) == 0)):
2187
+ geom = shape.geometry
2188
+ topology = Topology.ByBREPString(geom.brep_data)
2189
+ faces = Topology.Faces(topology)
2190
+ new_faces = []
2191
+ for face in faces:
2192
+ eb = Face.ExternalBoundary(face)
2193
+ ib = Face.InternalBoundaries(face)
2194
+ f = Face.ByWires(eb, ib)
2195
+ new_faces.append(f)
2196
+ topology = Topology.SelfMerge(Cluster.ByTopologies(new_faces))
2205
2197
  if removeCoplanarFaces:
2206
2198
  topology = Topology.RemoveCoplanarFaces(topology, epsilon=epsilon, tolerance=tolerance)
2207
- else:
2208
- if not silent:
2209
- print(f"Topology.ByIFCFile - Warning: Entity {getattr(entity, 'GlobalId', 0)} does not have a BREP. Skipping.")
2210
- continue
2211
-
2212
-
2213
- if transferDictionaries:
2214
- entity_dict = {
2215
- "TOPOLOGIC_id": str(Topology.UUID(topology)),
2216
- "TOPOLOGIC_name": getattr(entity, 'Name', "Untitled"),
2217
- "TOPOLOGIC_type": Topology.TypeAsString(topology),
2218
- "IFC_global_id": getattr(entity, 'GlobalId', 0),
2219
- "IFC_name": getattr(entity, 'Name', "Untitled"),
2220
- "IFC_type": entity.is_a()
2221
- }
2222
-
2223
- # Optionally add property sets
2224
- psets = {}
2225
- if hasattr(entity, 'IsDefinedBy'):
2226
- for rel in entity.IsDefinedBy:
2227
- if rel.is_a('IfcRelDefinesByProperties'):
2228
- pdef = rel.RelatingPropertyDefinition
2229
- if pdef and pdef.is_a('IfcPropertySet'):
2230
- key = f"IFC_{pdef.Name}"
2231
- props = {}
2232
- for prop in pdef.HasProperties:
2233
- if prop.is_a('IfcPropertySingleValue') and prop.NominalValue:
2234
- props[f"IFC_{prop.Name}"] = prop.NominalValue.wrappedValue
2235
- psets[key] = props
2236
-
2237
- final_dict = Dictionary.ByPythonDictionary(entity_dict)
2238
- if psets:
2239
- pset_dict = Dictionary.ByPythonDictionary(psets)
2240
- final_dict = Dictionary.ByMergedDictionaries([final_dict, pset_dict])
2241
-
2242
- topology = Topology.SetDictionary(topology, final_dict)
2243
-
2244
- topologies.append(topology)
2245
-
2246
- final_topologies = []
2247
- for topology in topologies:
2248
- faces = []
2249
-
2250
- for w in Topology.Wires(topology):
2251
- # Skip trivial wires (e.g. edges)
2252
- if len(Topology.Vertices(w)) < 3:
2253
- continue
2254
-
2255
- # Only attempt face creation if the wire is closed
2256
- if Wire.IsClosed(w) and Wire.IsManifold(w):
2257
- f = Face.ByWire(w)
2258
- if f:
2259
- faces.append(f)
2260
- continue
2261
-
2262
- # fallback: keep wire
2263
- faces.append(w)
2199
+ if transferDictionaries:
2200
+ element_dict = {
2201
+ "TOPOLOGIC_id": str(Topology.UUID(topology)),
2202
+ "TOPOLOGIC_name": getattr(element, 'Name', "Untitled"),
2203
+ "TOPOLOGIC_type": Topology.TypeAsString(topology),
2204
+ "IFC_global_id": getattr(element, 'GlobalId', 0),
2205
+ "IFC_name": getattr(element, 'Name', "Untitled"),
2206
+ "IFC_type": element_type
2207
+ }
2208
+
2209
+ # Optionally add property sets
2210
+ psets = {}
2211
+ if hasattr(element, 'IsDefinedBy'):
2212
+ for rel in element.IsDefinedBy:
2213
+ if rel.is_a('IfcRelDefinesByProperties'):
2214
+ pdef = rel.RelatingPropertyDefinition
2215
+ if pdef and pdef.is_a('IfcPropertySet'):
2216
+ key = f"IFC_{pdef.Name}"
2217
+ props = {}
2218
+ for prop in pdef.HasProperties:
2219
+ if prop.is_a('IfcPropertySingleValue') and prop.NominalValue:
2220
+ props[f"IFC_{prop.Name}"] = prop.NominalValue.wrappedValue
2221
+ psets[key] = props
2222
+
2223
+ final_dict = Dictionary.ByPythonDictionary(element_dict)
2224
+ if psets:
2225
+ pset_dict = Dictionary.ByPythonDictionary(psets)
2226
+ final_dict = Dictionary.ByMergedDictionaries([final_dict, pset_dict])
2227
+
2228
+ topology = Topology.SetDictionary(topology, final_dict)
2229
+ topologies.append(topology)
2230
+ if not it.next():
2231
+ break
2264
2232
 
2265
- # Avoid unnecessary Cluster/SelfMerge if there's only one face
2266
- if len(faces) == 1:
2267
- final_topology = faces[0]
2268
- else:
2269
- final_topology = Topology.SelfMerge(Cluster.ByTopologies(faces))
2270
- if final_topology == None:
2271
- final_topology = Cluster.ByTopologies(faces)
2272
- if transferDictionaries:
2273
- d = Topology.Dictionary(topology)
2274
- final_topology = Topology.SetDictionary(final_topology, d)
2275
-
2276
- final_topologies.append(final_topology)
2277
- return final_topologies
2233
+ return topologies
2278
2234
 
2279
2235
  @staticmethod
2280
2236
  def _ByIFCFile_old(file, includeTypes=[], excludeTypes=[], transferDictionaries=False, removeCoplanarFaces=False):
@@ -6590,15 +6546,19 @@ class Topology():
6590
6546
  return False, None
6591
6547
  # Done with Exclusion Tests.
6592
6548
 
6593
- faces_a = Topology.Faces(topologyA)
6594
- faces_b = Topology.Faces(topologyB)
6595
- if len(faces_a) > 0 and len(faces_b) > 0:
6596
- largest_faces_a = Topology.LargestFaces(topologyA)
6597
- largest_faces_b = Topology.LargestFaces(topologyB)
6549
+ if Topology.IsInstance(topologyA, "face"):
6550
+ largest_faces_a = [topologyA]
6551
+ largest_faces_b = [topologyB]
6598
6552
  else:
6599
- if not silent:
6600
- print("Topology.IsSimilar - Error: The topologies do not have faces. Returning None.")
6601
- return False, None
6553
+ faces_a = Topology.Faces(topologyA)
6554
+ faces_b = Topology.Faces(topologyB)
6555
+ if len(faces_a) > 0 and len(faces_b) > 0:
6556
+ largest_faces_a = Topology.LargestFaces(topologyA)
6557
+ largest_faces_b = Topology.LargestFaces(topologyB)
6558
+ else:
6559
+ if not silent:
6560
+ print("Topology.IsSimilar - Error: The topologies do not have faces. Returning None.")
6561
+ return False, None
6602
6562
 
6603
6563
  # Process largest faces
6604
6564
  for face_a in largest_faces_a:
topologicpy/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.8.29'
1
+ __version__ = '0.8.30'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topologicpy
3
- Version: 0.8.29
3
+ Version: 0.8.30
4
4
  Summary: An AI-Powered Spatial Modelling and Analysis Software Library for Architecture, Engineering, and Construction.
5
5
  Author-email: Wassim Jabi <wassim.jabi@gmail.com>
6
6
  License: AGPL v3 License
@@ -25,14 +25,14 @@ topologicpy/ShapeGrammar.py,sha256=JwE__VcKum5X3r33WwToEWtpJdFuhzZYyNhU2ob15ss,2
25
25
  topologicpy/Shell.py,sha256=h8S2nP1e0JtMxeOdAFZVhOYTJWTW8vlZRM5lxK0gu2o,89577
26
26
  topologicpy/Speckle.py,sha256=-eiTqJugd7pHiHpD3pDUcDO6CGhVyPV14HFRzaqEoaw,18187
27
27
  topologicpy/Sun.py,sha256=_VBBAUIDhvpkp72JBZlv7k9qx9jYubm3yM56UZ1Nc6c,36837
28
- topologicpy/Topology.py,sha256=UN1BnwS4G0DN5oLb6rLFUHEPxTj7PR6KzH-AnJQ0NHQ,478331
28
+ topologicpy/Topology.py,sha256=De_-naqB-hBPjcFeDQjbKUS7QzfhVxWJzu-7IMqLojc,476669
29
29
  topologicpy/Vector.py,sha256=mx7fgABdioikPWM9HzXKzmqfx3u_XBcU_jlLD4qK2x8,42407
30
30
  topologicpy/Vertex.py,sha256=UMDhERrLH6b4WOu4pl0UgYzcfp9-NvmASLtKXwetO_4,84687
31
31
  topologicpy/Wire.py,sha256=eRs4PM7h4yU5v6umPh0oBJR4cN8BwsqlVroaFdnvK4w,228499
32
32
  topologicpy/__init__.py,sha256=RMftibjgAnHB1vdL-muo71RwMS4972JCxHuRHOlU428,928
33
- topologicpy/version.py,sha256=c72Y0CCfU5Qgxq7demxoD7bRlDWidTBPnLUByhWGtHU,23
34
- topologicpy-0.8.29.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
35
- topologicpy-0.8.29.dist-info/METADATA,sha256=ahE6eaRHVYotJtOLSidVqD3l81GOrkKbBhlSnJauDsk,10535
36
- topologicpy-0.8.29.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
37
- topologicpy-0.8.29.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
38
- topologicpy-0.8.29.dist-info/RECORD,,
33
+ topologicpy/version.py,sha256=jN0mlk8vix45n9If_9Klf_t7fIZ7At3hYPz3_y-xov4,23
34
+ topologicpy-0.8.30.dist-info/licenses/LICENSE,sha256=FK0vJ73LuE8PYJAn7LutsReWR47-Ooovw2dnRe5yV6Q,681
35
+ topologicpy-0.8.30.dist-info/METADATA,sha256=RlkwNXmDCcLnsl11dRg0HUVn5D2vEq9BmcH2mJ4zRMU,10535
36
+ topologicpy-0.8.30.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
37
+ topologicpy-0.8.30.dist-info/top_level.txt,sha256=J30bDzW92Ob7hw3zA8V34Jlp-vvsfIkGzkr8sqvb4Uw,12
38
+ topologicpy-0.8.30.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5