From 4b8238a6027e72f5ee4607d487aa41973b8e6d38 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Sat, 1 Oct 2022 20:31:35 +1000 Subject: [PATCH] Openings are no longer loaded when loading fresh IFC projects --- src/blenderbim/blenderbim/bim/import_ifc.py | 22 +------ .../ifcopenshell/api/geometry/add_boolean.py | 63 +++++++++++++++++++ .../api/geometry/remove_boolean.py | 53 ++++++++++++++++ 3 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py create mode 100644 src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index 2df7e9c867..8b50d50450 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -181,7 +181,6 @@ class IfcImporter: self.elements = set() self.type_collection = None self.type_products = {} - self.openings = {} self.meshes = {} self.mesh_shapes = {} self.time = 0 @@ -224,8 +223,6 @@ class IfcImporter: self.profile_code("Process context filter") self.create_collections() self.profile_code("Create collections") - self.create_openings_collection() - self.profile_code("Create opening collection") self.create_materials() self.profile_code("Create materials") self.create_styles() @@ -322,8 +319,9 @@ class IfcImporter: self.elements = self.file.by_type("IfcElement") self.annotations = set(self.file.by_type("IfcAnnotation")) + self.elements = [e for e in self.elements if not e.is_a("IfcFeatureElement")] if self.ifc_import_settings.is_coordinating: - self.elements = [e for e in self.elements if e.Representation and not e.is_a("IfcFeatureElement")] + self.elements = [e for e in self.elements if e.Representation] self.elements = set(self.elements[offset:offset_limit]) @@ -928,10 +926,6 @@ class IfcImporter: elif hasattr(element, "ObjectPlacement"): self.set_matrix_world(obj, self.apply_blender_offset_to_matrix_world(obj, self.get_element_matrix(element))) - self.add_opening_relation(element, obj) - - if element.is_a("IfcOpeningElement"): - obj.display_type = "WIRE" return obj def get_representation_item_material_name(self, item): @@ -1247,7 +1241,6 @@ class IfcImporter: # Occurs when reloading a project pass project_collection = bpy.context.view_layer.layer_collection.children[self.project["blender"].name] - project_collection.children[self.opening_collection.name].hide_viewport = True project_collection.children[self.type_collection.name].hide_viewport = True def clean_mesh(self): @@ -1270,11 +1263,6 @@ class IfcImporter: bpy.ops.object.editmode_toggle(context_override) IfcStore.edited_objs.clear() - def add_opening_relation(self, element, obj): - if not element.is_a("IfcOpeningElement"): - return - self.openings[element.GlobalId] = obj - def load_file(self): self.ifc_import_settings.logger.info("loading file %s", self.ifc_import_settings.input_file) if not bpy.context.scene.BIMProperties.ifc_file: @@ -1405,10 +1393,6 @@ class IfcImporter: if parent: self.collections[parent.GlobalId].children.link(aggregate["collection"]) - def create_openings_collection(self): - self.opening_collection = bpy.data.collections.new("IfcOpeningElements") - self.project["blender"].children.link(self.opening_collection) - def create_materials(self): for material in self.file.by_type("IfcMaterial"): self.create_material(material) @@ -1653,8 +1637,6 @@ class IfcImporter: return self.collections[element.GlobalId].objects.link(obj) elif element.is_a("IfcTypeObject"): return self.type_collection.objects.link(obj) - elif element.is_a("IfcOpeningElement"): - return self.opening_collection.objects.link(obj) elif element.is_a("IfcStructuralMember"): return self.structural_member_collection.objects.link(obj) elif element.is_a("IfcStructuralConnection"): diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py new file mode 100644 index 0000000000..53fbd82640 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py @@ -0,0 +1,63 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import ifcopenshell.util.unit + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = { + "representation": None, + "operator": "DIFFERENCE", + # The XY plane is the clipping boundary and +Z is removed. + "matrix": None, # A matrix to define a clipping half space solid. + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) + self.settings["representation"].RepresentationType = "Clipping" + result = self.create_half_space_solid() + items = [] + for item in self.settings["representation"].Items: + items.append(self.file.createIfcBooleanResult(self.settings["operator"], item, result)) + self.settings["representation"].Items = items + + def create_half_space_solid(self): + clipping = self.settings["matrix"] + return self.file.createIfcHalfSpaceSolid( + self.file.createIfcPlane( + self.file.createIfcAxis2Placement3D( + self.file.createIfcCartesianPoint( + ( + self.convert_si_to_unit(clipping[0][3]), + self.convert_si_to_unit(clipping[1][3]), + self.convert_si_to_unit(clipping[2][3]), + ) + ), + self.file.createIfcDirection((clipping[0][2], clipping[1][2], clipping[2][2])), + self.file.createIfcDirection((clipping[0][0], clipping[1][0], clipping[2][0])), + ) + ), + False, + ) + + def convert_si_to_unit(self, co): + return co / self.settings["unit_scale"] diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py new file mode 100644 index 0000000000..d4c7458335 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_boolean.py @@ -0,0 +1,53 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"item": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + item = None + for inverse in self.file.get_inverse(self.settings["item"]): + if inverse.is_a("IfcBooleanResult"): + item = inverse + break + + representation = self.get_representation(item) + + first_operand = item.FirstOperand + second_operand = item.SecondOperand + for inverse in self.file.get_inverse(item): + ifcopenshell.util.element.replace_attribute(inverse, item, first_operand) + self.file.remove(item) + ifcopenshell.util.element.remove_deep2(self.file, second_operand) + + if not [i for i in representation.Items if i.is_a("IfcBooleanResult")]: + representation.RepresentationType == "SweptSolid" + + def get_representation(self, item): + for inverse in self.file.get_inverse(item): + if inverse.is_a("IfcShapeRepresentation"): + return inverse + elif inverse.is_a("IfcRepresentationItem"): + return self.get_representation(inverse)