IFC element names no longer treat the Blender name suffix as significant

This commit is contained in:
Dion Moult
2022-01-30 15:08:32 +11:00
parent 116252f857
commit 96179db7ad
5 changed files with 136 additions and 123 deletions
@@ -113,7 +113,7 @@ class ReassignClass(bpy.types.Operator):
return {"FINISHED"}
class AssignClass(bpy.types.Operator):
class AssignClass(bpy.types.Operator, Operator):
bl_idname = "bim.assign_class"
bl_label = "Assign IFC Class"
bl_options = {"REGISTER", "UNDO"}
@@ -126,135 +126,29 @@ class AssignClass(bpy.types.Operator):
should_add_representation: bpy.props.BoolProperty(default=True)
ifc_representation_class: bpy.props.StringProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
objects = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects
self.file = IfcStore.get_file()
if not self.ifc_class:
self.ifc_class = context.scene.BIMRootProperties.ifc_class
self.declaration = IfcStore.get_schema().declaration_by_name(self.ifc_class)
if self.predefined_type == "USERDEFINED":
self.predefined_type = self.userdefined_type
ifc_class = self.ifc_class or context.scene.BIMRootProperties.ifc_class
predefined_type = self.userdefined_type if self.predefined_type == "USERDEFINED" else self.predefined_type
ifc_context = self.context_id
if not ifc_context and get_contexts(self, context):
ifc_context = int(context.scene.BIMRootProperties.contexts or "0") or None
if ifc_context:
ifc_context = tool.Ifc.get().by_id(ifc_context)
active_object = context.active_object
for obj in objects:
self.assign_class(context, obj)
return {"FINISHED"}
def assign_class(self, context, obj):
if obj.BIMObjectProperties.ifc_definition_id:
return
product = ifcopenshell.api.run(
"root.create_entity",
self.file,
**{
"ifc_class": self.ifc_class,
"predefined_type": self.predefined_type or None,
"name": obj.name,
},
)
obj.name = "{}/{}".format(product.is_a(), obj.name)
IfcStore.link_element(product, obj)
if self.should_add_representation:
ifc_context = self.context_id
if not ifc_context and get_contexts(self, context):
ifc_context = int(context.scene.BIMRootProperties.contexts or "0") or None
if ifc_context:
ifc_context = tool.Ifc.get().by_id(ifc_context)
blenderbim.core.geometry.add_representation(
core.assign_class(
tool.Ifc,
tool.Geometry,
tool.Style,
tool.Surveyor,
tool.Collector,
tool.Root,
obj=obj,
ifc_class=ifc_class,
predefined_type=predefined_type,
should_add_representation=self.should_add_representation,
context=ifc_context,
ifc_representation_class=self.ifc_representation_class,
profile_set_usage=None,
)
if product.is_a("IfcElementType"):
tool.Collector.assign(obj)
elif product.is_a("IfcOpeningElement"):
obj.display_type = "WIRE"
tool.Collector.assign(obj)
elif (
product.is_a("IfcSpatialElement")
or product.is_a("IfcSpatialStructureElement")
or product.is_a("IfcProject")
or product.is_a("IfcContext")
):
self.place_in_spatial_collection(obj, context)
elif product.is_a("IfcStructuralItem"):
if product.is_a("IfcStructuralMember"):
self.place_in_structural_items_collection(obj, context, structural_collection="Members")
elif product.is_a("IfcStructuralConnection"):
self.place_in_structural_items_collection(obj, context, structural_collection="Connections")
else:
self.assign_potential_spatial_container(obj)
context.view_layer.objects.active = obj
def place_in_spatial_collection(self, obj, context):
for collection in obj.users_collection:
if collection.name == obj.name:
return
parent_collection = None
for collection in obj.users_collection:
collection.objects.unlink(obj)
if "Ifc" in collection.name:
parent_collection = collection
collection = bpy.data.collections.new(obj.name)
collection.objects.link(obj)
if parent_collection:
parent_collection.children.link(collection)
blenderbim.core.aggregate.assign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=bpy.data.objects.get(parent_collection.name),
related_obj=obj,
)
else:
context.scene.collection.children.link(collection)
def place_in_structural_items_collection(self, obj, context, structural_collection):
for project in [c for c in context.view_layer.layer_collection.children if "IfcProject" in c.name]:
if not [c for c in project.children if "StructuralItems" in c.name]:
members = bpy.data.collections.new("Members")
connections = bpy.data.collections.new("Connections")
items = bpy.data.collections.new("StructuralItems")
items.children.link(members)
items.children.link(connections)
project.collection.children.link(items)
for coll in [c for c in project.children if "StructuralItems" in c.name]:
for collection in [c for c in coll.children if structural_collection in c.name]:
for user_collection in obj.users_collection:
user_collection.objects.unlink(obj)
collection.collection.objects.link(obj)
break
break
break
def assign_potential_spatial_container(self, obj):
for collection in obj.users_collection:
if "Ifc" not in collection.name or collection.name == obj.name:
continue
spatial_obj = bpy.data.objects.get(collection.name)
if spatial_obj and spatial_obj.BIMObjectProperties.ifc_definition_id:
element = self.file.by_id(spatial_obj.BIMObjectProperties.ifc_definition_id)
if self.file.schema != "IFC2X3" and not element.is_a("IfcSpatialElement"):
continue
elif self.file.schema == "IFC2X3" and not element.is_a("IfcSpatialStructureElement"):
continue
blenderbim.core.spatial.assign_container(
tool.Ifc,
tool.Collector,
tool.Spatial,
structure_obj=spatial_obj,
element_obj=obj,
)
break
context.view_layer.objects.active = active_object
class UnlinkObject(bpy.types.Operator):
+7 -1
View File
@@ -150,6 +150,7 @@ class Drawing:
def export_text_literal_attributes(cls, obj): pass
def generate_drawing_matrix(cls, target_view, location_hint): pass
def generate_sheet_identification(cls): pass
def get_body_context(cls): pass
def get_sheet_filename(cls, document): pass
def get_text_literal(cls, obj): pass
def get_text_product(cls, element): pass
@@ -158,7 +159,7 @@ class Drawing:
def import_text_attributes(cls, obj): pass
def import_text_product(cls, obj): pass
def open_svg(cls, filepath): pass
def run_assign_class_operator(cls, obj=None, ifc_class=None, predefined_type=None): pass
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass
def update_text_value(cls, obj): pass
@@ -171,6 +172,7 @@ class Geometry:
def create_dynamic_voids(cls, obj): pass
def delete_data(cls, data): pass
def does_object_have_mesh_with_faces(cls, obj): pass
def does_representation_id_exist(cls, representation_id): pass
def duplicate_object_data(cls, obj): pass
def get_cartesian_point_coordinate_offset(cls, obj): pass
def get_element_type(cls, element): pass
@@ -180,6 +182,7 @@ class Geometry:
def get_object_materials_without_styles(cls, obj): pass
def get_profile_set_usage(cls, element): pass
def get_representation_data(cls, representation): pass
def get_representation_id(cls, representation): pass
def get_representation_name(cls, representation): pass
def get_styles(cls, obj): pass
def get_total_representation_items(cls, obj): pass
@@ -305,11 +308,14 @@ class Root:
def add_dynamic_opening_voids(cls, element, obj): pass
def does_type_have_representations(cls, element): pass
def get_element_type(cls, element): pass
def get_object_name(cls, obj): pass
def get_object_representation(cls, obj): pass
def get_representation_context(cls, representation): pass
def is_opening_element(cls, element): pass
def link_object_data(cls, source_obj, destination_obj): pass
def run_geometry_add_representation(cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None): pass
def set_element_specific_display_settings(cls, obj, element): pass
def set_object_name(cls, obj, element): pass
@interface
+11
View File
@@ -48,6 +48,12 @@ class Root(blenderbim.core.tool.Root):
def get_element_type(cls, element):
return ifcopenshell.util.element.get_type(element)
@classmethod
def get_object_name(cls, obj):
if "." in obj.name and obj.name.split(".")[-1].isnumeric():
return ".".join(obj.name.split(".")[:-1])
return obj.name
@classmethod
def get_object_representation(cls, obj):
if obj.data and obj.data.BIMMeshProperties.ifc_definition_id:
@@ -80,6 +86,11 @@ class Root(blenderbim.core.tool.Root):
profile_set_usage=profile_set_usage,
)
@classmethod
def set_element_specific_display_settings(cls, obj, element):
if element.is_a("IfcOpeningElement"):
obj.display_type = "WIRE"
@classmethod
def set_object_name(cls, obj, element):
name = obj.name
+65
View File
@@ -86,3 +86,68 @@ class TestCopyClass:
root.is_opening_element("element").should_be_called().will_return(True)
root.add_dynamic_opening_voids("element", "obj").should_be_called()
subject.copy_class(ifc, collector, geometry, root, obj="obj")
class TestAssignClass:
def test_do_nothing_if_already_assigned(self, ifc, collector, root):
ifc.get_entity("obj").should_be_called().will_return("entity")
subject.assign_class(
ifc,
collector,
root,
obj="obj",
ifc_class="ifc_class",
predefined_type="predefined_type",
should_add_representation=True,
context="context",
ifc_representation_class="ifc_representation_class",
)
def test_assign_a_class_with_geometry_and_autodetected_spatial_container(self, ifc, collector, root):
ifc.get_entity("obj").should_be_called().will_return(None)
root.get_object_name("obj").should_be_called().will_return("name")
ifc.run(
"root.create_entity", ifc_class="ifc_class", predefined_type="predefined_type", name="name"
).should_be_called().will_return("element")
root.set_object_name("obj", "element").should_be_called()
ifc.link("element", "obj").should_be_called()
root.run_geometry_add_representation(
obj="obj", context="context", ifc_representation_class="ifc_representation_class", profile_set_usage=None
).should_be_called()
root.set_element_specific_display_settings("obj", "element").should_be_called()
collector.sync("obj").should_be_called()
collector.assign("obj").should_be_called()
subject.assign_class(
ifc,
collector,
root,
obj="obj",
ifc_class="ifc_class",
predefined_type="predefined_type",
should_add_representation=True,
context="context",
ifc_representation_class="ifc_representation_class",
)
def test_not_adding_a_representation_if_requested(self, ifc, collector, root):
ifc.get_entity("obj").should_be_called().will_return(None)
root.get_object_name("obj").should_be_called().will_return("name")
ifc.run(
"root.create_entity", ifc_class="ifc_class", predefined_type="predefined_type", name="name"
).should_be_called().will_return("element")
root.set_object_name("obj", "element").should_be_called()
ifc.link("element", "obj").should_be_called()
root.set_element_specific_display_settings("obj", "element").should_be_called()
collector.sync("obj").should_be_called()
collector.assign("obj").should_be_called()
subject.assign_class(
ifc,
collector,
root,
obj="obj",
ifc_class="ifc_class",
predefined_type="predefined_type",
should_add_representation=False,
context="context",
ifc_representation_class="ifc_representation_class",
)
+37
View File
@@ -74,6 +74,18 @@ class TestGetElementType(NewFile):
assert subject.get_element_type(element) == type
class TestGetObjectName(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
assert subject.get_object_name(obj) == "Object"
def test_blender_number_suffixes_are_ignored(self):
obj = bpy.data.objects.new("Object.001", None)
assert subject.get_object_name(obj) == "Object"
obj = bpy.data.objects.new("Object.foo.123", None)
assert subject.get_object_name(obj) == "Object.foo"
class TestGetObjectRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
@@ -113,3 +125,28 @@ class TestLinkObjectData(NewFile):
class TestRunGeometryAddRepresntation(NewFile):
def test_nothing(self):
pass
class TestSetElementSpecificDisplaySettings(NewFile):
def test_opening_elements_display_as_wire(self):
ifc = ifcopenshell.file()
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
element = ifc.createIfcOpeningElement()
subject.set_element_specific_display_settings(obj, element)
assert obj.display_type == "WIRE"
class TestSetObjectName(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
element = ifc.createIfcWall()
subject.set_object_name(obj, element)
assert obj.name == "IfcWall/Object"
def test_existing_ifc_prefixes_are_not_repeated(self):
ifc = ifcopenshell.file()
obj = bpy.data.objects.new("IfcSlab/Object", bpy.data.meshes.new("Mesh"))
element = ifc.createIfcWall()
subject.set_object_name(obj, element)
assert obj.name == "IfcWall/Object"