mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-08 00:41:36 +00:00
Representation Items UI - support editing layers
Example - https://imgur.com/a/3fn051N
This commit is contained in:
@@ -34,6 +34,7 @@ classes = (
|
||||
operator.DuplicateMoveLinkedAggregate,
|
||||
operator.DuplicateMoveLinkedAggregateMacro,
|
||||
operator.EditObjectPlacement,
|
||||
operator.EditRepresentationItemLayer,
|
||||
operator.EditRepresentationItemShapeAspect,
|
||||
operator.EditRepresentationItemStyle,
|
||||
operator.EnableEditingRepresentationItemShapeAspect,
|
||||
@@ -67,6 +68,7 @@ classes = (
|
||||
operator.SelectConnection,
|
||||
operator.SelectRepresentationItem,
|
||||
operator.SwitchRepresentation,
|
||||
operator.UnassignRepresentationItemLayer,
|
||||
operator.UnassignRepresentationItemStyle,
|
||||
operator.UpdateItemAttributes,
|
||||
operator.UpdateParametricRepresentation,
|
||||
|
||||
@@ -23,6 +23,7 @@ import logging
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.layer
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
@@ -2325,7 +2326,7 @@ class EnableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
|
||||
props = obj.BIMGeometryProperties
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing = True
|
||||
|
||||
props.items.clear()
|
||||
@@ -3209,3 +3210,51 @@ class OverrideMove(bpy.types.Operator):
|
||||
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"}
|
||||
|
||||
@@ -32,7 +32,7 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import Optional
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
|
||||
def get_contexts(self, context):
|
||||
@@ -105,6 +105,14 @@ def get_material_constituents(self, context, edit_text):
|
||||
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):
|
||||
shape_aspect_id = self.representation_item_shape_aspect
|
||||
attrs = self.shape_aspect_attrs
|
||||
@@ -128,6 +136,16 @@ class RepresentationItem(PropertyGroup):
|
||||
shape_aspect_id: IntProperty(name="Shape Aspect IFC ID")
|
||||
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):
|
||||
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):
|
||||
contexts: EnumProperty(items=get_contexts, name="Contexts")
|
||||
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
|
||||
)
|
||||
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
|
||||
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]
|
||||
|
||||
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):
|
||||
# Revit workaround
|
||||
|
||||
@@ -179,7 +179,7 @@ class BIM_PT_representation_items(Panel):
|
||||
RepresentationItemsData.load()
|
||||
|
||||
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.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_id = active_item.surface_style_id
|
||||
shape_aspect = active_item.shape_aspect
|
||||
layer = active_item.layer
|
||||
|
||||
# Style.
|
||||
row = self.layout.row(align=True)
|
||||
if props.is_editing_item_style:
|
||||
# NOTE: we currently support 1 item having just 1 style
|
||||
@@ -226,9 +228,19 @@ class BIM_PT_representation_items(Panel):
|
||||
if surface_style:
|
||||
row.operator("bim.unassign_representation_item_style", icon="X", text="")
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text=active_item.layer or "No Presentation Layer", icon="STICKY_UVS_LOC")
|
||||
# Presentation layer.
|
||||
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 "UV" in active_item.tags:
|
||||
text = "Has UV mapping"
|
||||
@@ -242,6 +254,7 @@ class BIM_PT_representation_items(Panel):
|
||||
text = "Has no colour mapping"
|
||||
self.layout.label(text=text, icon="COLOR")
|
||||
|
||||
# Shape aspect.
|
||||
row = self.layout.row(align=True)
|
||||
if props.is_editing_item_shape_aspect:
|
||||
row.prop(props, "representation_item_shape_aspect", icon="SHAPEKEY_DATA", text="")
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import bonsai.tool as tool
|
||||
from typing import Any
|
||||
|
||||
|
||||
def refresh():
|
||||
@@ -31,12 +32,31 @@ class LayersData:
|
||||
|
||||
@classmethod
|
||||
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
|
||||
|
||||
@classmethod
|
||||
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
|
||||
def active_layers(cls) -> dict[int, str]:
|
||||
|
||||
Reference in New Issue
Block a user