Representation Items UI - support editing layers

Example - https://imgur.com/a/3fn051N
This commit is contained in:
Andrej730
2025-01-27 15:16:45 +05:00
parent 56af23b89f
commit 13a182cd95
5 changed files with 144 additions and 8 deletions
@@ -34,6 +34,7 @@ classes = (
operator.DuplicateMoveLinkedAggregate, operator.DuplicateMoveLinkedAggregate,
operator.DuplicateMoveLinkedAggregateMacro, operator.DuplicateMoveLinkedAggregateMacro,
operator.EditObjectPlacement, operator.EditObjectPlacement,
operator.EditRepresentationItemLayer,
operator.EditRepresentationItemShapeAspect, operator.EditRepresentationItemShapeAspect,
operator.EditRepresentationItemStyle, operator.EditRepresentationItemStyle,
operator.EnableEditingRepresentationItemShapeAspect, operator.EnableEditingRepresentationItemShapeAspect,
@@ -67,6 +68,7 @@ classes = (
operator.SelectConnection, operator.SelectConnection,
operator.SelectRepresentationItem, operator.SelectRepresentationItem,
operator.SwitchRepresentation, operator.SwitchRepresentation,
operator.UnassignRepresentationItemLayer,
operator.UnassignRepresentationItemStyle, operator.UnassignRepresentationItemStyle,
operator.UpdateItemAttributes, operator.UpdateItemAttributes,
operator.UpdateParametricRepresentation, operator.UpdateParametricRepresentation,
@@ -23,6 +23,7 @@ import logging
import numpy as np import numpy as np
import numpy.typing as npt import numpy.typing as npt
import ifcopenshell import ifcopenshell
import ifcopenshell.api.layer
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.placement import ifcopenshell.util.placement
import ifcopenshell.util.representation import ifcopenshell.util.representation
@@ -2325,7 +2326,7 @@ class EnableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
obj = tool.Geometry.get_active_or_representation_obj() obj = tool.Geometry.get_active_or_representation_obj()
props = obj.BIMGeometryProperties props = tool.Geometry.get_object_geometry_props(obj)
props.is_editing = True props.is_editing = True
props.items.clear() props.items.clear()
@@ -3209,3 +3210,51 @@ class OverrideMove(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
return {"FINISHED"} return {"FINISHED"}
class EditRepresentationItemLayer(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_representation_item_layer"
bl_label = "Edit Representation Item Layer"
bl_description = "Edit presentation layer for the active representation item."
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
ifc_file = tool.Ifc.get()
obj = context.active_object
assert obj
props = tool.Geometry.get_object_geometry_props(obj)
new_layer = ifc_file.by_id(int(props.representation_item_layer))
item = ifc_file.by_id(int(props.active_item.ifc_definition_id))
item_layer = next(iter(item.LayerAssignment), None)
# We assume just 1 layer can be assigned to representation item.
if item_layer:
# Same layer is already assigned.
if item_layer == new_layer:
props.is_editing_item_layer = False
return {"FINISHED"}
ifcopenshell.api.layer.unassign_layer(ifc_file, [item], item_layer)
ifcopenshell.api.layer.assign_layer(ifc_file, [item], new_layer)
props.is_editing_item_layer = False
bpy.ops.bim.enable_editing_representation_items()
return {"FINISHED"}
class UnassignRepresentationItemLayer(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unassign_representation_item_layer"
bl_label = "Unassign Representation Item Layer"
bl_description = "Unassign presentation layer from the active representation item."
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
ifc_file = tool.Ifc.get()
obj = context.active_object
assert obj
props = tool.Geometry.get_object_geometry_props(obj)
item = ifc_file.by_id(props.active_item.ifc_definition_id)
layer = item.LayerAssignment[0] # If there is no layer, then button is not visible in UI.
ifcopenshell.api.layer.unassign_layer(ifc_file, [item], layer)
bpy.ops.bim.enable_editing_representation_items()
return {"FINISHED"}
+54 -2
View File
@@ -32,7 +32,7 @@ from bpy.props import (
FloatVectorProperty, FloatVectorProperty,
CollectionProperty, CollectionProperty,
) )
from typing import Optional from typing import Optional, TYPE_CHECKING
def get_contexts(self, context): def get_contexts(self, context):
@@ -105,6 +105,14 @@ def get_material_constituents(self, context, edit_text):
return ObjectMaterialData.data["active_material_constituents"] return ObjectMaterialData.data["active_material_constituents"]
def get_layers(self, context):
from bonsai.bim.module.layer.data import LayersData
if not LayersData.is_loaded:
LayersData.load()
return LayersData.data["layers_enum"]
def update_shape_aspect(self, context): def update_shape_aspect(self, context):
shape_aspect_id = self.representation_item_shape_aspect shape_aspect_id = self.representation_item_shape_aspect
attrs = self.shape_aspect_attrs attrs = self.shape_aspect_attrs
@@ -128,6 +136,16 @@ class RepresentationItem(PropertyGroup):
shape_aspect_id: IntProperty(name="Shape Aspect IFC ID") shape_aspect_id: IntProperty(name="Shape Aspect IFC ID")
tags: StringProperty(name="Tags") tags: StringProperty(name="Tags")
if TYPE_CHECKING:
name: str
surface_style: str
surface_style_id: int
layer: str
ifc_definition_id: int
shape_aspect: str
shape_aspect_id: int
tags: str
class RepresentationItemObject(PropertyGroup): class RepresentationItemObject(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
@@ -149,6 +167,20 @@ class ShapeAspect(PropertyGroup):
) )
def update_is_editing_item_layer(self: "BIMObjectGeometryProperties", context: bpy.types.Context) -> None:
if self.is_editing_item_layer:
ifc_file = tool.Ifc.get()
active_ui_item = self.active_item
assert active_ui_item
item = ifc_file.by_id(active_ui_item.ifc_definition_id)
if layer := next(iter(item.LayerAssignment), None):
self.representation_item_layer = str(layer.id())
return
if "representation_item_layer" in self:
del self["representation_item_layer"]
class BIMObjectGeometryProperties(PropertyGroup): class BIMObjectGeometryProperties(PropertyGroup):
contexts: EnumProperty(items=get_contexts, name="Contexts") contexts: EnumProperty(items=get_contexts, name="Contexts")
is_editing: BoolProperty(name="Is Editing", default=False) is_editing: BoolProperty(name="Is Editing", default=False)
@@ -161,12 +193,32 @@ class BIMObjectGeometryProperties(PropertyGroup):
items=get_shape_aspects, name="Representation Item Shape Aspect", update=update_shape_aspect items=get_shape_aspects, name="Representation Item Shape Aspect", update=update_shape_aspect
) )
shape_aspect_attrs: PointerProperty(type=ShapeAspect) shape_aspect_attrs: PointerProperty(type=ShapeAspect)
is_editing_item_layer: BoolProperty(
name="Is Editing Item's Presentation Layer",
description="Toggle editing presentation layer for the item.",
default=False,
update=update_is_editing_item_layer,
)
representation_item_layer: EnumProperty(items=get_layers, name="Representation Item's Layer")
@property @property
def active_item(self): def active_item(self):
if self.active_item_index < len(self.items): if 0 <= self.active_item_index < len(self.items):
return self.items[self.active_item_index] return self.items[self.active_item_index]
if TYPE_CHECKING:
contexts: str
is_editing: bool
items: bpy.types.bpy_prop_collection_idprop[RepresentationItem]
active_item_index: int
is_editing_item_style: bool
representation_item_style: str
is_editing_item_shape_aspect: bool
representation_item_shape_aspect: str
shape_aspect_attrs: ShapeAspect
is_editing_item_layer: bool
representation_item_layer: str
class BIMGeometryProperties(PropertyGroup): class BIMGeometryProperties(PropertyGroup):
# Revit workaround # Revit workaround
+16 -3
View File
@@ -179,7 +179,7 @@ class BIM_PT_representation_items(Panel):
RepresentationItemsData.load() RepresentationItemsData.load()
obj = context.scene.BIMGeometryProperties.representation_obj or tool.Blender.get_active_object() obj = context.scene.BIMGeometryProperties.representation_obj or tool.Blender.get_active_object()
props = obj.BIMGeometryProperties props = tool.Geometry.get_object_geometry_props(obj)
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text=f"{RepresentationItemsData.data['total_items']} Items Found") row.label(text=f"{RepresentationItemsData.data['total_items']} Items Found")
@@ -207,7 +207,9 @@ class BIM_PT_representation_items(Panel):
surface_style = active_item.surface_style surface_style = active_item.surface_style
surface_style_id = active_item.surface_style_id surface_style_id = active_item.surface_style_id
shape_aspect = active_item.shape_aspect shape_aspect = active_item.shape_aspect
layer = active_item.layer
# Style.
row = self.layout.row(align=True) row = self.layout.row(align=True)
if props.is_editing_item_style: if props.is_editing_item_style:
# NOTE: we currently support 1 item having just 1 style # NOTE: we currently support 1 item having just 1 style
@@ -226,9 +228,19 @@ class BIM_PT_representation_items(Panel):
if surface_style: if surface_style:
row.operator("bim.unassign_representation_item_style", icon="X", text="") row.operator("bim.unassign_representation_item_style", icon="X", text="")
row = self.layout.row() # Presentation layer.
row.label(text=active_item.layer or "No Presentation Layer", icon="STICKY_UVS_LOC") row = self.layout.row(align=True)
if props.is_editing_item_layer:
prop_with_search(row, props, "representation_item_layer", icon="STICKY_UVS_LOC", text="")
row.operator("bim.edit_representation_item_layer", icon="CHECKMARK", text="")
row.prop(props, "is_editing_item_layer", icon="CANCEL", text="")
else:
row.label(text=layer or "No Presentation Layer", icon="STICKY_UVS_LOC")
row.prop(props, "is_editing_item_layer", icon="GREASEPENCIL", text="")
if layer:
row.operator("bim.unassign_representation_item_layer", icon="X", text="")
# Mappings.
if active_item.name.endswith("FaceSet"): if active_item.name.endswith("FaceSet"):
if "UV" in active_item.tags: if "UV" in active_item.tags:
text = "Has UV mapping" text = "Has UV mapping"
@@ -242,6 +254,7 @@ class BIM_PT_representation_items(Panel):
text = "Has no colour mapping" text = "Has no colour mapping"
self.layout.label(text=text, icon="COLOR") self.layout.label(text=text, icon="COLOR")
# Shape aspect.
row = self.layout.row(align=True) row = self.layout.row(align=True)
if props.is_editing_item_shape_aspect: if props.is_editing_item_shape_aspect:
row.prop(props, "representation_item_shape_aspect", icon="SHAPEKEY_DATA", text="") row.prop(props, "representation_item_shape_aspect", icon="SHAPEKEY_DATA", text="")
+22 -2
View File
@@ -19,6 +19,7 @@
import bpy import bpy
import ifcopenshell import ifcopenshell
import bonsai.tool as tool import bonsai.tool as tool
from typing import Any
def refresh(): def refresh():
@@ -31,12 +32,31 @@ class LayersData:
@classmethod @classmethod
def load(cls): def load(cls):
cls.data = {"total_layers": cls.total_layers(), "active_layers": cls.active_layers()} cls.data = {
"active_layers": cls.active_layers(),
"layers": cls.layers(),
}
# After .layers().
cls.data["total_layers"] = cls.total_layers()
cls.data["layers_enum"] = cls.layers_enum()
cls.is_loaded = True cls.is_loaded = True
@classmethod @classmethod
def total_layers(cls) -> int: def total_layers(cls) -> int:
return len(tool.Ifc.get().by_type("IfcPresentationLayerAssignment")) return len(cls.data["layers"])
@classmethod
def layers(cls) -> dict[int, dict[str, Any]]:
results = dict()
KEEP_ATTRS = set(("id", "Name", "Description"))
for layer in tool.Ifc.get().by_type("IfcPresentationLayerAssignment"):
results[layer.id()] = {k: v for k, v in layer.get_info().items() if k in KEEP_ATTRS}
return results
@classmethod
def layers_enum(cls) -> list[tuple[str, str, str]]:
return list((str(data["id"]), data["Name"], data["Description"] or "") for data in cls.data["layers"].values())
@classmethod @classmethod
def active_layers(cls) -> dict[int, str]: def active_layers(cls) -> dict[int, str]: