Fix #4267 and add tests

Crash was caused by unmark_manual_booleans accessing boolean element after they were deleted from IFC
This commit is contained in:
Andrej730
2024-01-30 14:05:35 +05:00
parent af593b6663
commit 5d020f36d3
4 changed files with 44 additions and 9 deletions
@@ -668,17 +668,17 @@ class RemoveBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
except: except:
continue continue
boolean = None boolean_id = None
for inverse in tool.Ifc.get().get_inverse(item): for inverse in tool.Ifc.get().get_inverse(item):
if inverse.is_a("IfcBooleanResult"): if inverse.is_a("IfcBooleanResult"):
boolean = inverse boolean_id = inverse.id()
break break
ifcopenshell.api.run("geometry.remove_boolean", tool.Ifc.get(), item=item) ifcopenshell.api.run("geometry.remove_boolean", tool.Ifc.get(), item=item)
if obj.data.BIMMeshProperties.obj: if obj.data.BIMMeshProperties.obj:
upstream_obj = obj.data.BIMMeshProperties.obj upstream_obj = obj.data.BIMMeshProperties.obj
element = tool.Ifc.get_entity(upstream_obj) element = tool.Ifc.get_entity(upstream_obj)
bbim_boolean_updates.setdefault(element, []).append(boolean) bbim_boolean_updates.setdefault(element, []).append(boolean_id)
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if body: if body:
blenderbim.core.geometry.switch_representation( blenderbim.core.geometry.switch_representation(
@@ -692,8 +692,8 @@ class RemoveBooleans(Operator, tool.Ifc.Operator, AddObjectHelper):
) )
bpy.data.objects.remove(obj) bpy.data.objects.remove(obj)
for element, booleans in bbim_boolean_updates.items(): for element, boolean_ids in bbim_boolean_updates.items():
tool.Model.unmark_manual_booleans(element, booleans) tool.Model.unmark_manual_booleans(element, boolean_ids)
tool.Blender.set_active_object(upstream_obj) tool.Blender.set_active_object(upstream_obj)
return {"FINISHED"} return {"FINISHED"}
@@ -259,8 +259,10 @@ class BooleansMarkAsManual(bpy.types.Operator):
if self.mark_as_manual: if self.mark_as_manual:
tool.Model.mark_manual_booleans(element, booleans) tool.Model.mark_manual_booleans(element, booleans)
else: else:
tool.Model.unmark_manual_booleans(element, booleans) tool.Model.unmark_manual_booleans(element, [b.id() for b in booleans])
self.report({"INFO"}, f"{len(booleans)} were marked as {'manual' if self.mark_as_manual else 'automatic'}") self.report(
{"INFO"}, f"{len(booleans)} booleans were marked as {'manual' if self.mark_as_manual else 'automatic'}"
)
blenderbim.bim.handler.refresh_ui_data() blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"} return {"FINISHED"}
+3 -2
View File
@@ -521,11 +521,12 @@ class Model(blenderbim.core.tool.Model):
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": data}) ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": data})
@classmethod @classmethod
def unmark_manual_booleans(cls, element, booleans): def unmark_manual_booleans(cls, element, boolean_ids):
# NOTE: we use use boolean_ids instead of boolean entities
# so it will be possible to unmark manual booleans after they already was deleted
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean") pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean")
if not pset: if not pset:
return return
boolean_ids = [b.id() for b in booleans]
data = set(json.loads(pset["Data"])) data = set(json.loads(pset["Data"]))
data -= set(boolean_ids) data -= set(boolean_ids)
data = list(data) data = list(data)
+32
View File
@@ -21,6 +21,7 @@ import ifcopenshell
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.tool as tool import blenderbim.tool as tool
import numpy as np import numpy as np
import json
from test.bim.bootstrap import NewFile from test.bim.bootstrap import NewFile
from blenderbim.tool.model import Model as subject from blenderbim.tool.model import Model as subject
from ifcopenshell.util.shape_builder import V from ifcopenshell.util.shape_builder import V
@@ -108,6 +109,37 @@ class TestGetManualBooleans(NewFile):
assert len(subject.get_manual_booleans(element)) == 2 assert len(subject.get_manual_booleans(element)) == 2
class TestMarkManualBooleans(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcWall()
boolean = ifc.createIfcBooleanClippingResult()
subject.mark_manual_booleans(element, [boolean])
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean")
assert pset
value = json.loads(pset["Data"])
assert set(value) == {boolean.id()}
class TestUnmarkManualBooleans(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcWall()
boolean = ifc.createIfcBooleanClippingResult()
boolean2 = ifc.createIfcBooleanClippingResult()
subject.mark_manual_booleans(element, [boolean, boolean2])
subject.unmark_manual_booleans(element, [boolean.id()])
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean")
assert pset
value = json.loads(pset["Data"])
assert set(value) == {boolean2.id()}
class TestStairCalculatedParams(NewFile): class TestStairCalculatedParams(NewFile):
def compare_data(self, pset_data, expected_calculated_data): def compare_data(self, pset_data, expected_calculated_data):
calculated_data = subject.get_active_stair_calculated_params(pset_data) calculated_data = subject.get_active_stair_calculated_params(pset_data)