Orientation slot feature now has limited scope

No support for setting up blender orientation slots for multiple
containers or for non-spatial elements.  Setting the default container
switches to an appropriate orientation slot if the orientation of this
container isn't global.
This commit is contained in:
Bruno Postle
2025-03-22 08:40:20 +00:00
parent 7d4a017c15
commit dbe2c541c7
3 changed files with 39 additions and 55 deletions
@@ -334,7 +334,7 @@ class SetDefaultContainer(bpy.types.Operator):
def execute(self, context): def execute(self, context):
core.set_default_container(tool.Spatial, container=tool.Ifc.get().by_id(self.container)) core.set_default_container(tool.Spatial, container=tool.Ifc.get().by_id(self.container))
core.set_orientation_slot(spatial=tool.Spatial, product=tool.Ifc.get().by_id(self.container)) core.set_orientation_slot(tool.Spatial, container=tool.Ifc.get().by_id(self.container))
return {"FINISHED"} return {"FINISHED"}
+2 -2
View File
@@ -121,8 +121,8 @@ def import_spatial_decomposition(spatial: tool.Spatial) -> None:
spatial.import_spatial_decomposition() spatial.import_spatial_decomposition()
def set_orientation_slot(spatial: tool.Spatial, product: ifcopenshell.entity_instance) -> None: def set_orientation_slot(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None:
spatial.create_orientation_slots([product], use=True) spatial.create_orientation_slot(container)
def contract_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None: def contract_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None:
+36 -52
View File
@@ -502,65 +502,49 @@ class Spatial(bonsai.core.tool.Spatial):
cls.import_spatial_element(child, level_index + 1) cls.import_spatial_element(child, level_index + 1)
@classmethod @classmethod
def create_orientation_slots(cls, products: list[ifcopenshell.entity_instance], use: bool = False) -> None: def create_orientation_slot(cls, container: ifcopenshell.entity_instance) -> None:
active_slot = bpy.context.scene.transform_orientation_slots[0] active_slot = bpy.context.scene.transform_orientation_slots[0]
initial_orientation = active_slot.type placement = container.ObjectPlacement
combined_matrix = ifcopenshell.util.placement.get_local_placement(placement)[:3, :3]
for product in products: if np.allclose(combined_matrix, np.eye(3), atol=1e-6):
placement = product.ObjectPlacement # this spatial element has global orientation
combined_matrix = ifcopenshell.util.placement.get_local_placement(placement)[:3, :3] active_slot.type = "GLOBAL"
if np.allclose(combined_matrix, np.eye(3), atol=1e-6): return
# this product has global orientation elif (
active_slot.type = "GLOBAL" hasattr(container, "Decomposes")
continue and container.Decomposes
elif ( and hasattr(container.Decomposes[0].RelatingObject, "ObjectPlacement")
hasattr(product, "Decomposes") ):
and product.Decomposes # this spatial element is part of a decomposition
and hasattr(product.Decomposes[0].RelatingObject, "ObjectPlacement") parent_placement = container.Decomposes[0].RelatingObject.ObjectPlacement
): parent_matrix = ifcopenshell.util.placement.get_local_placement(parent_placement)[:3, :3]
# this product is part of a decomposition if np.allclose(combined_matrix, parent_matrix, atol=1e-6):
parent_placement = product.Decomposes[0].RelatingObject.ObjectPlacement # this spatial element has the same orientation as its parent
parent_matrix = ifcopenshell.util.placement.get_local_placement(parent_placement)[:3, :3] cls.create_orientation_slot(container=container.Decomposes[0].RelatingObject)
if np.allclose(combined_matrix, parent_matrix, atol=1e-6): return
# this product has the same orientation as its parent
cls.create_orientation_slots(products=[product.Decomposes[0].RelatingObject], use=use)
continue
elif (
hasattr(product, "ContainedInStructure")
and product.ContainedInStructure
and hasattr(product.ContainedInStructure[0].RelatingStructure, "Objectplacement")
):
# contained in a spatial element
parent_placement = product.ContainedInStructure[0].RelatingStructure.ObjectPlacement
parent_matrix = ifcopenshell.util.placement.get_local_placement(parent_placement)[:3, :3]
if np.allclose(combined_matrix, parent_matrix, atol=1e-6):
# this product has the same orientation as its container
cls.create_orientation_slots(products=[product.ContainedInStructure[0].RelatingStructure], use=use)
continue
# this product has a unique orientation # this spatial element has a unique orientation
orientation_name = product.is_a() + "/" + product.Name orientation_name = container.is_a() + "/" + container.Name
# stash selected objects # stash selected objects
active_object = bpy.context.view_layer.objects.active active_object = bpy.context.view_layer.objects.active
selected_objects = list(bpy.context.view_layer.objects.selected) selected_objects = list(bpy.context.view_layer.objects.selected)
# bpy.ops.transform.create_orientation() requires a dummy object
bpy.ops.object.empty_add(type="PLAIN_AXES")
bpy.ops.transform.create_orientation(name=orientation_name, overwrite=True) # bpy.ops.transform.create_orientation() requires a dummy object
active_slot.type = orientation_name bpy.ops.object.empty_add(type="PLAIN_AXES")
active_slot.custom_orientation.matrix = np.linalg.inv(combined_matrix) bpy.ops.transform.create_orientation(name=orientation_name, overwrite=True)
active_slot.type = orientation_name
active_slot.custom_orientation.matrix = np.linalg.inv(combined_matrix)
# delete dummy object # delete dummy object
bpy.ops.object.delete() bpy.ops.object.delete()
# reinstate selected objects
for obj in selected_objects:
obj.select_set(True)
if active_object:
bpy.context.view_layer.objects.active = active_object
if not use: # reinstate selected objects
active_slot.type = initial_orientation for obj in selected_objects:
obj.select_set(True)
if active_object:
bpy.context.view_layer.objects.active = active_object
@classmethod @classmethod
def edit_container_attributes(cls, entity: ifcopenshell.entity_instance) -> None: def edit_container_attributes(cls, entity: ifcopenshell.entity_instance) -> None: