See #5972. Add tests for adding boolean and check against recursive booleans and invalid boolean items.

This commit is contained in:
Dion Moult
2025-01-18 21:13:37 +11:00
parent 35734f4b67
commit a12427e44a
4 changed files with 177 additions and 189 deletions
+5 -35
View File
@@ -533,11 +533,11 @@ class AddBoolean(Operator, tool.Ifc.Operator):
def _execute(self, context):
first_obj = tool.Blender.get_active_object()
if not tool.Geometry.is_representation_item(first_obj):
self.report({"INFO"}, "At least two representation items must be selected to add a boolean.")
if not first_obj or not tool.Geometry.is_boolean_operand(first_obj):
self.report({"INFO"}, "At least two valid objects must be selected to add a boolean.")
return {"CANCELLED"}
second_objs = [
o for o in tool.Blender.get_selected_objects() if o != first_obj and tool.Geometry.is_representation_item(o)
o for o in tool.Blender.get_selected_objects() if o != first_obj and tool.Geometry.is_boolean_operand(o)
]
if not second_objs:
self.report({"INFO"}, "At least two representation items must be selected to add a boolean.")
@@ -546,38 +546,8 @@ class AddBoolean(Operator, tool.Ifc.Operator):
props = context.scene.BIMBooleanProperties
first_item = tool.Ifc.get().by_id(first_obj.data.BIMMeshProperties.ifc_definition_id)
while True:
is_part_of_boolean = False
for inverse in tool.Ifc.get().get_inverse(first_item):
if inverse.is_a("IfcBooleanResult"):
is_part_of_boolean = True
first_item = inverse
if not is_part_of_boolean:
break
# Don't replace style or aspect relationships.
to_replace = set(
[
i
for i in tool.Ifc.get().get_inverse(first_item)
if i.is_a("IfcShapeRepresentation") or i.is_a("IfcBooleanResult")
]
)
first = first_item
booleans = set()
for second_obj in second_objs:
second = tool.Ifc.get().by_id(second_obj.data.BIMMeshProperties.ifc_definition_id)
for inverse in tool.Ifc.get().get_inverse(second):
if inverse.is_a("IfcShapeRepresentation"):
inverse.Items = list(set(inverse.Items) - {second})
first = tool.Ifc.get().create_entity("IfcBooleanResult", props.operator, first, second)
booleans.add(first)
for inverse in to_replace:
ifcopenshell.util.element.replace_attribute(inverse, first_item, first)
second_items = [tool.Ifc.get().by_id(o.data.BIMMeshProperties.ifc_definition_id) for o in second_objs]
booleans = ifcopenshell.api.geometry.add_boolean(tool.Ifc.get(), first_item, second_items, props.operator)
rep_obj = bpy.context.scene.BIMGeometryProperties.representation_obj
rep_element = tool.Ifc.get_entity(rep_obj)
+16
View File
@@ -978,6 +978,22 @@ class Geometry(bonsai.core.tool.Geometry):
and tool.Ifc.get().by_id(ifc_id).is_a("IfcRepresentationItem")
)
@classmethod
def is_boolean_operand(cls, obj: bpy.types.Object) -> bool:
return bool(
(data := obj.data)
and isinstance(data, Geometry.TYPES_WITH_MESH_PROPERTIES)
and (ifc_id := data.BIMMeshProperties.ifc_definition_id)
and (item := tool.Ifc.get().by_id(ifc_id))
and (
item.is_a("IfcBooleanResult")
or item.is_a("IfcCsgPrimitive3D")
or item.is_a("IfcHalfSpaceSolid")
or item.is_a("IfcSolidModel")
or item.is_a("IfcTessellatedFaceSet")
)
)
@classmethod
def is_text_literal(cls, representation: ifcopenshell.entity_instance) -> bool:
items = ifcopenshell.util.representation.resolve_items(representation)