From 42f32e34186f999bb31856a3b5bf5757235a6c62 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 25 Sep 2024 23:48:44 +1000 Subject: [PATCH] Fix #5460. Assigning a class now uses the new add element mode internally. --- src/bonsai/bonsai/bim/module/root/data.py | 18 +++------- src/bonsai/bonsai/bim/module/root/operator.py | 35 +++++++++++++------ src/bonsai/bonsai/bim/module/root/prop.py | 10 ++++++ 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/root/data.py b/src/bonsai/bonsai/bim/module/root/data.py index a14d3da18f..38db05960e 100644 --- a/src/bonsai/bonsai/bim/module/root/data.py +++ b/src/bonsai/bonsai/bim/module/root/data.py @@ -135,21 +135,13 @@ class IfcClassData: ("EMPTY", "No Geometry", "Start with an empty object"), None, ] - if ( - hasattr(bpy.context, "selected_objects") - and len(bpy.context.selected_objects) > 0 - and (obj := bpy.context.active_object) - and obj.type == "MESH" - ): - templates.append( - ( - "OBJ", - "Tessellation From Active Selection", - "Use the actively selected object as a template to create a new tessellation", - ) - ) templates.extend( [ + ( + "OBJ", + "Tessellation From Object", + "Use an object as a template to create a new tessellation", + ), ( "MESH", "Custom Tessellation", diff --git a/src/bonsai/bonsai/bim/module/root/operator.py b/src/bonsai/bonsai/bim/module/root/operator.py index cedbdab474..0affd1031f 100644 --- a/src/bonsai/bonsai/bim/module/root/operator.py +++ b/src/bonsai/bonsai/bim/module/root/operator.py @@ -198,17 +198,35 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator): if obj.mode != "OBJECT": self.report({"ERROR"}, "Object must be in OBJECT mode to assign class") continue - core.assign_class( + element = core.assign_class( tool.Ifc, tool.Collector, tool.Root, obj=obj, ifc_class=ifc_class, predefined_type=predefined_type, - should_add_representation=self.should_add_representation, + should_add_representation=False, context=ifc_context, ifc_representation_class=self.ifc_representation_class, ) + if self.should_add_representation and obj.data and len(obj.data.vertices): + builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) + unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + verts = [v.co / unit_scale for v in obj.data.vertices] + faces = [p.vertices[:] for p in obj.data.polygons] + item = builder.mesh(verts, faces) + representation = builder.get_representation(ifc_context, [item]) + ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation) + bonsai.core.geometry.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=representation, + should_reload=True, + is_global=True, + should_sync_changes_first=False, + ) + context.view_layer.objects.active = active_object @@ -357,21 +375,13 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator): if representation_template == "EMTPY" or not ifc_context: pass - elif ( - representation_template == "OBJ" - and (template_obj := context.active_object) - and template_obj.type == "MESH" - and len(template_obj.data.vertices) - ): + elif representation_template == "OBJ" and (template_obj := props.representation_obj): obj.matrix_world = template_obj.matrix_world builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get()) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) - bm = bmesh.new() - bmesh.ops.create_cube(bm, size=0.5) verts = [v.co / unit_scale for v in template_obj.data.vertices] faces = [p.vertices[:] for p in template_obj.data.polygons] item = builder.mesh(verts, faces) - bm.free() representation = builder.get_representation(ifc_context, [item]) ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation) bonsai.core.geometry.switch_representation( @@ -531,6 +541,9 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator): row = self.layout.row() row.prop(props, "ifc_userdefined_type") prop_with_search(self.layout, props, "representation_template", text="Representation") + if props.representation_template == "OBJ": + row = self.layout.row() + row.prop(props, "representation_obj", text="Object") if props.representation_template != "EMPTY": prop_with_search(self.layout, props, "contexts") diff --git a/src/bonsai/bonsai/bim/module/root/prop.py b/src/bonsai/bonsai/bim/module/root/prop.py index 1fa48af61c..0de2f0e5ba 100644 --- a/src/bonsai/bonsai/bim/module/root/prop.py +++ b/src/bonsai/bonsai/bim/module/root/prop.py @@ -115,6 +115,10 @@ def is_object_class_applicable(self, obj): return element.is_a("IfcTypeObject") == active_element.is_a("IfcTypeObject") +def poll_representation_obj(self, obj): + return obj.type == "MESH" and obj.data.polygons + + class BIMRootProperties(PropertyGroup): contexts: EnumProperty(items=get_contexts, name="Contexts", options=set()) ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=refresh_classes) @@ -124,6 +128,12 @@ class BIMRootProperties(PropertyGroup): representation_template: bpy.props.EnumProperty( items=get_representation_template, name="Representation Template", default=0 ) + representation_obj: bpy.props.PointerProperty( + type=bpy.types.Object, + name="Representation Object", + poll=poll_representation_obj, + description="The representation will be a tessellation of the selected object", + ) relating_class_object: PointerProperty( type=bpy.types.Object, name="Copy Class",