Reimplement lost feature from refactor - native handling of swept disks (mostly rebar) in Blender during import

This commit is contained in:
Dion Moult
2021-03-17 12:39:23 +11:00
parent efce28c053
commit 0bc76b1af7
+50 -109
View File
@@ -285,8 +285,9 @@ class IfcImporter:
self.settings_native.set(self.settings_native.INCLUDE_CURVES, True) self.settings_native.set(self.settings_native.INCLUDE_CURVES, True)
self.settings_2d = ifcopenshell.geom.settings() self.settings_2d = ifcopenshell.geom.settings()
self.settings_2d.set(self.settings_2d.INCLUDE_CURVES, True) self.settings_2d.set(self.settings_2d.INCLUDE_CURVES, True)
self.include_elements = [] self.filter_mode = "BLACKLIST"
self.exclude_elements = [] self.include_elements = set()
self.exclude_elements = set()
self.project = None self.project = None
self.spatial_structure_elements = {} self.spatial_structure_elements = {}
self.elements = {} self.elements = {}
@@ -298,7 +299,7 @@ class IfcImporter:
self.time = 0 self.time = 0
self.unit_scale = 1 self.unit_scale = 1
self.added_data = {} self.added_data = {}
self.native_elements = {} self.native_elements = set()
self.native_data = {} self.native_data = {}
self.aggregates = {} self.aggregates = {}
@@ -338,14 +339,12 @@ class IfcImporter:
self.profile_code("Create opening collection") self.profile_code("Create opening collection")
self.process_element_filter() self.process_element_filter()
self.profile_code("Process element filter") self.profile_code("Process element filter")
# TODO: Deprecate self.parse_native_elements()
# self.parse_native_elements() self.profile_code("Parsing native elements")
# self.profile_code("Parsing native elements")
self.create_grids() self.create_grids()
self.profile_code("Create grids") self.profile_code("Create grids")
# TODO: Deprecate self.create_native_products()
# self.create_native_products() self.profile_code("Create native products")
# self.profile_code("Create native products")
self.create_products() self.create_products()
self.profile_code("Create products") self.profile_code("Create products")
self.create_type_products() self.create_type_products()
@@ -394,71 +393,35 @@ class IfcImporter:
def process_element_filter(self): def process_element_filter(self):
if not self.ifc_import_settings.ifc_selector: if not self.ifc_import_settings.ifc_selector:
return return
self.include_elements = []
selector = ifcopenshell.util.selector.Selector() selector = ifcopenshell.util.selector.Selector()
elements = selector.parse(self.file, self.ifc_import_settings.ifc_selector) elements = selector.parse(self.file, self.ifc_import_settings.ifc_selector)
if self.ifc_import_settings.ifc_import_filter == "WHITELIST": if self.ifc_import_settings.ifc_import_filter == "WHITELIST":
self.include_elements = elements self.filter_mode = "WHITELIST"
self.include_elements = set(elements)
elif self.ifc_import_settings.ifc_import_filter == "BLACKLIST": elif self.ifc_import_settings.ifc_import_filter == "BLACKLIST":
self.exclude_elements = elements self.exclude_elements = set(elements)
self.filter_mode = "BLACKLIST"
def parse_native_elements(self): def parse_native_elements(self):
self.parse_native_swept_disk_solid() if self.filter_mode == "WHITELIST":
self.parse_native_extruded_area_solid() for element in self.include_elements:
self.parse_native_faceted_brep() if self.is_native(element):
if self.include_elements: self.native_elements[element.GlobalId] = element
include_global_ids = [e.GlobalId for e in self.include_elements] self.include_elements -= self.native_elements
filtered_native_elements = {} elif self.filter_mode == "BLACKLIST":
for global_id in self.native_elements.keys(): for element in set(self.file.by_type("IfcElement")) - self.exclude_elements:
if global_id in include_global_ids: if self.is_native(element):
filtered_native_elements[global_id] = self.native_elements[global_id] self.native_elements.add(element)
self.native_elements = filtered_native_elements self.exclude_elements |= self.native_elements
elif self.exclude_elements:
exclude_global_ids = [e.GlobalId for e in self.exclude_elements]
filtered_native_elements = {}
for global_id in self.native_elements.keys():
if global_id not in exclude_global_ids:
filtered_native_elements[global_id] = self.native_elements[global_id]
self.native_elements = filtered_native_elements
def parse_native_swept_disk_solid(self): def is_native(self, element):
for element in self.file.by_type("IfcSweptDiskSolid"): if not element.Representation or not element.Representation.Representations:
if [e for e in self.file.get_inverse(element) if e.is_a("IfcBooleanResult")]: return
continue for representation in self.get_body_representations(element.Representation.Representations):
self.swap_out_with_dummy_geometry(element) # Single swept disk solids (e.g. rebar) are better natively represented as beveled curves
if len(representation["raw"].Items) == 1 and representation["raw"].Items[0].is_a("IfcSweptDiskSolid"):
def parse_native_extruded_area_solid(self): self.native_data[element.GlobalId] = {"type": "IfcSweptDiskSolid"}
for element in self.file.by_type("IfcExtrudedAreaSolid"): return True
if element.SweptArea.is_a() not in [
"IfcArbitraryClosedProfileDef",
"IfcRectangleProfileDef",
"IfcCircleProfileDef",
]:
continue
if [e for e in self.file.get_inverse(element) if e.is_a("IfcBooleanResult")]:
continue
self.swap_out_with_dummy_geometry(element)
def parse_native_faceted_brep(self):
for element in self.file.by_type("IfcFacetedBrep"):
if [e for e in self.file.get_inverse(element) if e.is_a("IfcBooleanResult")]:
continue
self.swap_out_with_dummy_geometry(element)
def swap_out_with_dummy_geometry(self, element):
dummy_geometry = self.get_dummy_geometry()
inverse_elements = self.file.get_inverse(element)
for inverse_element in inverse_elements:
if inverse_element.is_a("IfcShapeRepresentation"):
inverse_element.RepresentationType = "Curve"
for product in self.get_products_from_shape_representation(inverse_element):
self.native_elements.setdefault(product.GlobalId, {})[dummy_geometry.id()] = element
ifcopenshell.util.element.replace_attribute(inverse_element, element, dummy_geometry)
def get_dummy_geometry(self):
point = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
direction = self.file.createIfcVector(self.file.createIfcDirection((0.0, 0.0, 1.0)), 1000.0)
return self.file.createIfcLine(point, direction)
def get_products_from_shape_representation(self, element): def get_products_from_shape_representation(self, element):
products = [pr.ShapeOfProduct[0] for pr in element.OfProductRepresentation] products = [pr.ShapeOfProduct[0] for pr in element.OfProductRepresentation]
@@ -649,30 +612,14 @@ class IfcImporter:
return representation_map return representation_map
def create_native_products(self): def create_native_products(self):
if not self.native_elements:
return
# TODO: the iterator is kind of useless here, rewrite this
iterator = ifcopenshell.geom.iterator(
self.settings_native,
self.file,
multiprocessing.cpu_count(),
include=[self.file.by_guid(guid) for guid in self.native_elements.keys()] or None,
)
valid_file = iterator.initialize()
total = 0 total = 0
checkpoint = time.time() checkpoint = time.time()
if not valid_file: for element in self.native_elements:
return False
while True:
total += 1 total += 1
if total % 250 == 0: if total % 250 == 0:
print("{} elements processed in {:.2f}s ...".format(total, time.time() - checkpoint)) print("{} elements processed in {:.2f}s ...".format(total, time.time() - checkpoint))
checkpoint = time.time() checkpoint = time.time()
shape = iterator.get() self.create_product(element, mesh=self.create_native_mesh(element))
if shape:
self.create_product(self.file.by_id(shape.guid), shape)
if not iterator.next():
break
print("Done creating geometry") print("Done creating geometry")
def create_products(self): def create_products(self):
@@ -734,7 +681,7 @@ class IfcImporter:
vertex = list(subelement.VertexGeometry.Coordinates) vertex = list(subelement.VertexGeometry.Coordinates)
break break
if not vertex: if not vertex:
continue # TODO implement non cartesian point vertexes continue # TODO implement non cartesian point vertexes
placement_matrix[0, 3] += vertex[0] * self.unit_scale placement_matrix[0, 3] += vertex[0] * self.unit_scale
placement_matrix[1, 3] += vertex[1] * self.unit_scale placement_matrix[1, 3] += vertex[1] * self.unit_scale
placement_matrix[2, 3] += vertex[2] * self.unit_scale placement_matrix[2, 3] += vertex[2] * self.unit_scale
@@ -747,15 +694,10 @@ class IfcImporter:
def create_curve_products(self, products): def create_curve_products(self, products):
if self.ifc_import_settings.should_use_cpu_multiprocessing: if self.ifc_import_settings.should_use_cpu_multiprocessing:
iterator = ifcopenshell.geom.iterator( iterator = ifcopenshell.geom.iterator(
self.settings_2d, self.settings_2d, self.file, multiprocessing.cpu_count(), include=products
self.file,
multiprocessing.cpu_count(),
include=products
) )
else: else:
iterator = ifcopenshell.geom.iterator( iterator = ifcopenshell.geom.iterator(self.settings_2d, self.file, include=products)
self.settings_2d, self.file, include=products
)
valid_file = iterator.initialize() valid_file = iterator.initialize()
if not valid_file: if not valid_file:
return False return False
@@ -773,7 +715,7 @@ class IfcImporter:
break break
print("Done creating geometry") print("Done creating geometry")
def create_product(self, element, shape=None): def create_product(self, element, shape=None, mesh=None):
if element is None: if element is None:
return return
@@ -782,18 +724,17 @@ class IfcImporter:
self.ifc_import_settings.logger.info("Creating object %s", element) self.ifc_import_settings.logger.info("Creating object %s", element)
if shape: if mesh:
pass
elif shape:
mesh_name = self.get_mesh_name(shape.geometry) mesh_name = self.get_mesh_name(shape.geometry)
mesh = self.meshes.get(mesh_name) mesh = self.meshes.get(mesh_name)
if mesh is None: if mesh is None:
if element.GlobalId in self.native_elements: mesh = self.create_mesh(element, shape)
mesh = self.create_native_mesh(element, shape) if "-" in shape.geometry.id:
if mesh is None: mesh.BIMMeshProperties.ifc_definition_id = int(shape.geometry.id.split("-")[0])
mesh = self.create_mesh(element, shape) else:
if "-" in shape.geometry.id: mesh.BIMMeshProperties.ifc_definition_id = int(shape.geometry.id)
mesh.BIMMeshProperties.ifc_definition_id = int(shape.geometry.id.split("-")[0])
else:
mesh.BIMMeshProperties.ifc_definition_id = int(shape.geometry.id)
self.meshes[mesh_name] = mesh self.meshes[mesh_name] = mesh
else: else:
mesh = None mesh = None
@@ -818,9 +759,8 @@ class IfcImporter:
obj.display_type = "WIRE" obj.display_type = "WIRE"
return obj return obj
def create_native_mesh(self, element, shape): def create_native_mesh(self, element):
# TODO This should be split off into its own module for run-time native mesh conversion # TODO This should be split off into its own module for run-time native mesh conversion
data = self.native_elements[element.GlobalId]
materials = [] materials = []
items = [] items = []
for representation in self.get_body_representations(element.Representation.Representations): for representation in self.get_body_representations(element.Representation.Representations):
@@ -830,8 +770,6 @@ class IfcImporter:
# Magic string NULLMAT represents no material, unless this has a better approach # Magic string NULLMAT represents no material, unless this has a better approach
material_name = "NULLMAT" material_name = "NULLMAT"
materials.append(material_name) materials.append(material_name)
if item.id() in data:
item = data[item.id()]
if item.is_a() == "IfcExtrudedAreaSolid": if item.is_a() == "IfcExtrudedAreaSolid":
native = self.create_native_extruded_area_solid(item, element) native = self.create_native_extruded_area_solid(item, element)
if native: if native:
@@ -1216,7 +1154,9 @@ class IfcImporter:
def create_aggregate_tree(self): def create_aggregate_tree(self):
for aggregate in self.aggregates.values(): for aggregate in self.aggregates.values():
if aggregate["container"].is_a("IfcSpatialStructureElement"): if aggregate["container"].is_a("IfcSpatialStructureElement"):
self.spatial_structure_elements[aggregate["container"].GlobalId]["blender"].children.link(aggregate["blender"]) self.spatial_structure_elements[aggregate["container"].GlobalId]["blender"].children.link(
aggregate["blender"]
)
else: else:
self.aggregates[aggregate["container"].GlobalId]["blender"].children.link(aggregate["blender"]) self.aggregates[aggregate["container"].GlobalId]["blender"].children.link(aggregate["blender"])
@@ -1229,7 +1169,7 @@ class IfcImporter:
self.aggregates[element.GlobalId] = { self.aggregates[element.GlobalId] = {
"blender": collection, "blender": collection,
"blender_obj": obj, "blender_obj": obj,
"container": self.get_aggregate_container(element) "container": self.get_aggregate_container(element),
} }
def get_aggregate_container(self, element): def get_aggregate_container(self, element):
@@ -1468,6 +1408,7 @@ class IfcImporter:
ios_materials = [] ios_materials = []
for mat in geometry.materials: for mat in geometry.materials:
# See bug #866
if mat.original_name(): if mat.original_name():
ios_materials.append(mat.original_name()) ios_materials.append(mat.original_name())
else: else: