diff --git a/src/blenderbim/blenderbim/bim/module/model/opening.py b/src/blenderbim/blenderbim/bim/module/model/opening.py index adcf1f1079..9c98680917 100644 --- a/src/blenderbim/blenderbim/bim/module/model/opening.py +++ b/src/blenderbim/blenderbim/bim/module/model/opening.py @@ -528,17 +528,7 @@ class AddBoolean(Operator, tool.Ifc.Operator): "geometry.add_boolean", tool.Ifc.get(), representation=representation, operator="DIFFERENCE", **mesh_data ) - pset = ifcopenshell.util.element.get_pset(element1, "BBIM_Boolean") - if pset: - pset = tool.Ifc.get().by_id(pset["id"]) - data = json.loads(pset["Data"]) - data.extend([b.id() for b in booleans]) - data = list(set(data)) - else: - pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element1, name="BBIM_Boolean") - data = [b.id() for b in booleans] - ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": json.dumps(data)}) - + tool.Model.mark_manual_booleans(element1, booleans) tool.Model.clear_scene_openings() blenderbim.core.geometry.switch_representation( @@ -672,17 +662,17 @@ class RemoveBooleans(Operator, tool.Ifc.Operator, AddObjectHelper): except: continue - boolean_id = None + boolean = None for inverse in tool.Ifc.get().get_inverse(item): if inverse.is_a("IfcBooleanResult"): - boolean_id = inverse.id() + boolean = inverse break ifcopenshell.api.run("geometry.remove_boolean", tool.Ifc.get(), item=item) if obj.data.BIMMeshProperties.obj: upstream_obj = obj.data.BIMMeshProperties.obj element = tool.Ifc.get_entity(upstream_obj) - bbim_boolean_updates.setdefault(element, []).append(boolean_id) + bbim_boolean_updates.setdefault(element, []).append(boolean) body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") if body: blenderbim.core.geometry.switch_representation( @@ -696,18 +686,8 @@ class RemoveBooleans(Operator, tool.Ifc.Operator, AddObjectHelper): ) bpy.data.objects.remove(obj) - for element, boolean_ids in bbim_boolean_updates.items(): - pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean") - if not pset: - continue - data = set(json.loads(pset["Data"])) - data -= set(boolean_ids) - data = list(data) - pset = tool.Ifc.get().by_id(pset["id"]) - if data: - ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": json.dumps(data)}) - else: - ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset) + for element, booleans in bbim_boolean_updates.items(): + tool.Model.unmark_manual_booleans(element, booleans) tool.Blender.set_active_object(upstream_obj) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/tool/model.py b/src/blenderbim/blenderbim/tool/model.py index 5b3ec23be9..67dd73d058 100644 --- a/src/blenderbim/blenderbim/tool/model.py +++ b/src/blenderbim/blenderbim/tool/model.py @@ -474,11 +474,7 @@ class Model(blenderbim.core.tool.Model): return {"thickness": thickness, "offset": offset, "direction_sense": direction_sense} @classmethod - def get_manual_booleans(cls, element): - pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean") - if not pset: - return [] - boolean_ids = json.loads(pset["Data"]) + def get_booleans(cls, element): body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") if not body: return [] @@ -487,11 +483,51 @@ class Model(blenderbim.core.tool.Model): while items: item = items.pop() if item.is_a("IfcBooleanResult"): - if item.id() in boolean_ids: - booleans.append(item) + booleans.append(item) items.append(item.FirstOperand) return booleans + @classmethod + def get_manual_booleans(cls, element): + pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean") + if not pset: + return [] + boolean_ids = json.loads(pset["Data"]) + body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") + if not body: + return [] + booleans = [b for b in cls.get_booleans(element) if b.id() in boolean_ids] + return booleans + + @classmethod + def mark_manual_booleans(cls, element, booleans): + pset_data = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean") + boolean_ids = [b.id() for b in booleans] + if pset_data: + pset = tool.Ifc.get().by_id(pset_data["id"]) + data = json.loads(pset_data["Data"]) + data.extend(boolean_ids) + data = list(set(data)) + else: + pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="BBIM_Boolean") + data = boolean_ids + ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": json.dumps(data)}) + + @classmethod + def unmark_manual_booleans(cls, element, booleans): + pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean") + if not pset: + return + boolean_ids = [b.id() for b in booleans] + data = set(json.loads(pset["Data"])) + data -= set(boolean_ids) + data = list(data) + pset = tool.Ifc.get().by_id(pset["id"]) + if data: + ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": json.dumps(data)}) + else: + ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset) + @classmethod def get_flow_segment_axis(cls, obj): z_values = [v[2] for v in obj.bound_box] diff --git a/src/blenderbim/test/tool/test_model.py b/src/blenderbim/test/tool/test_model.py index 20329a3cad..71289bf64f 100644 --- a/src/blenderbim/test/tool/test_model.py +++ b/src/blenderbim/test/tool/test_model.py @@ -54,10 +54,20 @@ class TestGenerateOccurrenceName(NewFile): class TestGetManualBooleans(NewFile): - def test_run(self): - assert isinstance(subject(), blenderbim.core.tool.Model) + def test_get_manual_booleans(self): + clippings = [ + { + "type": "IfcBooleanClippingResult", + "operand_type": "IfcHalfSpaceSolid", + "matrix": np.eye(4).tolist(), + }, + { + "type": "IfcBooleanResult", + "operand_type": "IfcHalfSpaceSolid", + "matrix": np.eye(4).tolist(), + }, + ] - def setup_profile_represntation(self, clippings=[]): ifc = ifcopenshell.file() self.ifc = ifc tool.Ifc.set(ifc) @@ -89,21 +99,9 @@ class TestGetManualBooleans(NewFile): "geometry.add_profile_representation", ifc, context=body, profile=hea100, depth=5, clippings=clippings ) ifcopenshell.api.run("geometry.assign_representation", ifc, product=element, representation=representation) - return element, representation - def test_manual_booleans(self): - element, representation = self.setup_profile_represntation() - matrix = np.eye(4) - ifcopenshell.api.run( - "geometry.add_boolean", self.ifc, representation=representation, type="IfcHalfSpaceSolid", matrix=matrix - ) - assert len(subject.get_manual_booleans(element)) == 1 - - def test_automatic_booleans_ignored(self): - clipping = { - "type": "IfcBooleanClippingResult", - "operand_type": "IfcHalfSpaceSolid", - "matrix": np.eye(4).tolist(), - } - element, representation = self.setup_profile_represntation(clippings=[clipping]) + booleans = subject.get_booleans(element) + assert len(booleans) == 2 assert len(subject.get_manual_booleans(element)) == 0 + subject.mark_manual_booleans(element, booleans) + assert len(subject.get_manual_booleans(element)) == 2 diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py index 3973b0a27f..66a5bf3baf 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_boolean.py @@ -57,9 +57,11 @@ class Usecase: ) ): items.append(self.file.createIfcBooleanClippingResult(self.settings["operator"], item, result)) + representation_type = "Clipping" else: items.append(self.file.createIfcBooleanResult(self.settings["operator"], item, result)) - self.settings["representation"].RepresentationType = "CSG" + representation_type = "CSG" + self.settings["representation"].RepresentationType = representation_type self.settings["representation"].Items = items return items