Compare commits

...

8 Commits

Author SHA1 Message Date
Bruno Postle dbe2c541c7 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.
2025-03-22 08:40:20 +00:00
Bruno Postle 7d4a017c15 Don't create orientation slots for all spaces
Trying to create orientation slots for all spaces could potentially
involve a lot of duplicate method calls because blender has no way to
query the list of existing orientation slots, so we can't check to see
if setting a slot is necessary.

So this patch now simply creates/overwrites and uses a single
orientation slot whenever the user switches the default spatial
container, and then only if the spatial orientation is non-world.
2025-03-14 19:17:50 +00:00
Bruno Postle 14f5aa7c4f Merge branch 'v0.8.0' into orientation_slots 2025-02-24 22:07:27 +00:00
Bruno Postle c585c789ac Follow orientation through ContainedInStructure
create_orientation_slots() can be given an element and it will find the
highest matching parent product orientation via elements and/or
spatial elements
2025-02-24 21:53:42 +00:00
Bruno Postle 891752a569 Move blender object manipulation to inner loop
create_orientation_slots() can be called a lot of times recursively, so
only do creation and deletion of dummy objects when absolutely necessary
2025-02-24 19:56:37 +00:00
Bruno Postle 6faef6564e s/element/product/ in create_orientation_slots()
This method should work with anything that has an ObjectPlacement, ie.
an IfcProduct
2025-02-24 19:38:29 +00:00
Bruno Postle 62bd495a9e Create transform orientation slots #6128
Blender supports named UCS coordinate systems. Refreshing the spatial
decomposition panel now creates orientation slots for each unique
spatial element orientation.

Additionally, setting the default spatial container selects an
appropriate orientation slot. eg. if a building is rotated, selecting to
work in a storey will set the orientation to the building orientation.

These slots are only created/updated in the spatial decomposition panel,
maybe they should also be created when loading the file and other
places.

The blender API doesn't currently allow listing custom orientation slots
for deletion, so if a spatial element is deleted or renamed, the old
orientation slot will persist for the rest of the session.
2025-02-23 22:29:42 +00:00
Andrej730 b93cbbf989 black . 2025-02-23 13:29:12 +00:00
3 changed files with 60 additions and 0 deletions
@@ -334,6 +334,7 @@ class SetDefaultContainer(bpy.types.Operator):
def execute(self, context):
core.set_default_container(tool.Spatial, container=tool.Ifc.get().by_id(self.container))
core.set_orientation_slot(tool.Spatial, container=tool.Ifc.get().by_id(self.container))
return {"FINISHED"}
+4
View File
@@ -121,6 +121,10 @@ def import_spatial_decomposition(spatial: tool.Spatial) -> None:
spatial.import_spatial_decomposition()
def set_orientation_slot(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None:
spatial.create_orientation_slot(container)
def contract_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None:
spatial.contract_container(container)
spatial.import_spatial_decomposition()
+55
View File
@@ -38,6 +38,7 @@ import bonsai.core.geometry
import bonsai.core.unit
import bonsai.tool as tool
import json
import numpy as np
from math import pi
from mathutils import Vector, Matrix
from shapely import Polygon
@@ -500,6 +501,60 @@ class Spatial(bonsai.core.tool.Spatial):
for child in children or []:
cls.import_spatial_element(child, level_index + 1)
@classmethod
def create_orientation_slot(cls, container: ifcopenshell.entity_instance) -> None:
active_slot = bpy.context.scene.transform_orientation_slots[0]
placement = container.ObjectPlacement
combined_matrix = ifcopenshell.util.placement.get_local_placement(placement)[:3, :3]
if np.allclose(combined_matrix, np.eye(3), atol=1e-6):
# this spatial element has global orientation
active_slot.type = "GLOBAL"
return
elif (
hasattr(container, "Decomposes")
and container.Decomposes
and hasattr(container.Decomposes[0].RelatingObject, "ObjectPlacement")
):
# this spatial element is part of a decomposition
parent_placement = container.Decomposes[0].RelatingObject.ObjectPlacement
parent_matrix = ifcopenshell.util.placement.get_local_placement(parent_placement)[:3, :3]
if np.allclose(combined_matrix, parent_matrix, atol=1e-6):
# this spatial element has the same orientation as its parent
cls.create_orientation_slot(container=container.Decomposes[0].RelatingObject)
return
# this spatial element has a unique orientation
orientation_name = container.is_a() + "/" + container.Name
# stash selected objects
active_object = bpy.context.view_layer.objects.active
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)
active_slot.type = orientation_name
active_slot.custom_orientation.matrix = np.linalg.inv(combined_matrix)
# delete dummy object
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
@classmethod
def edit_container_attributes(cls, entity: ifcopenshell.entity_instance) -> None:
# TODO
obj = tool.Ifc.get_object(entity)
bonsai.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
name = bpy.context.scene.BIMSpatialDecompositionProperties.container_name
if name != entity.Name:
cls.edit_container_name(entity, name)
@classmethod
def edit_container_name(cls, container: ifcopenshell.entity_instance, name: str) -> None:
tool.Ifc.run("attribute.edit_attributes", product=container, attributes={"Name": name})