diff --git a/src/ifcblenderexport/blenderbim/bim/__init__.py b/src/ifcblenderexport/blenderbim/bim/__init__.py index 8b29a96757..71e3ff9f2f 100644 --- a/src/ifcblenderexport/blenderbim/bim/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/__init__.py @@ -205,6 +205,11 @@ if bpy is not None: operator.SetViewportShadowFromSun, operator.SetNorthOffset, operator.GetNorthOffset, + operator.AddPresentationLayer, + operator.AddToPresentationLayer, + operator.RemoveFromPresentationLayer, + operator.RemovePresentationLayer, + operator.UpdatePresentationLayer, operator.AddDrawingStyleAttribute, operator.RemoveDrawingStyleAttribute, operator.CopyPropertyToSelection, @@ -248,6 +253,7 @@ if bpy is not None: prop.BcfTopicDocumentReference, prop.BcfTopicRelatedTopic, prop.Subcontext, + prop.PresentationLayer, prop.BIMProperties, prop.BIMDebugProperties, prop.BCFProperties, @@ -286,6 +292,7 @@ if bpy is not None: ui.BIM_PT_qa, ui.BIM_PT_library, ui.BIM_PT_gis, + ui.BIM_PT_presentation_layers, ui.BIM_PT_diff, ui.BIM_PT_cobie, ui.BIM_PT_patch, @@ -293,6 +300,7 @@ if bpy is not None: ui.BIM_PT_debug, ui.BIM_PT_material, ui.BIM_PT_mesh, + ui.BIM_PT_presentation_layer_data, ui.BIM_PT_object, ui.BIM_PT_object_material, ui.BIM_PT_object_psets, @@ -311,6 +319,7 @@ if bpy is not None: ui.BIM_UL_generic, ui.BIM_UL_clash_sets, ui.BIM_UL_constraints, + ui.BIM_UL_presentation_layers, ui.BIM_UL_document_information, ui.BIM_UL_document_references, ui.BIM_UL_topics, diff --git a/src/ifcblenderexport/blenderbim/bim/export_ifc.py b/src/ifcblenderexport/blenderbim/bim/export_ifc.py index ba87fa124b..c34ffe3f89 100644 --- a/src/ifcblenderexport/blenderbim/bim/export_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/export_ifc.py @@ -975,9 +975,10 @@ class IfcParser: def load_presentation_layer_assignments(self): for representation in self.representations.values(): if representation["presentation_layer"]: - self.presentation_layer_assignments.setdefault(representation["presentation_layer"], []).append( - representation - ) + pl = representation["presentation_layer"] + if pl.name == '': + continue + self.presentation_layer_assignments.setdefault(pl.name, []).append(representation) def load_representations(self): if not self.ifc_export_settings.has_representations: @@ -1109,9 +1110,7 @@ class IfcParser: "is_native": mesh.BIMMeshProperties.is_native if hasattr(mesh, "BIMMeshProperties") else False, "is_swept_solid": mesh.BIMMeshProperties.is_swept_solid if hasattr(mesh, "BIMMeshProperties") else False, "is_generated": False, - "presentation_layer": mesh.BIMMeshProperties.presentation_layer - if hasattr(mesh, "BIMMeshProperties") - else None, + "presentation_layer": obj.BIMObjectProperties.presentation_layer if hasattr(obj, "BIMObjectProperties") else None, "attributes": {"Name": mesh.name}, } @@ -2033,10 +2032,26 @@ class IfcExporter: return self.file.createIfcStyledItem(representation_item, [surface_style], item["attributes"]["Name"]) def create_presentation_layer_assignments(self): + scene_props = bpy.context.scene.BIMProperties for name, assigned_items in self.ifc_parser.presentation_layer_assignments.items(): - self.file.createIfcPresentationLayerAssignment( - name, None, [i["ifc"].MappedRepresentation for i in assigned_items], None - ) + if name == '': + continue + pl = scene_props.presentation_layers[name] + print("Presentation Export: ", name, pl.identifier, pl.layer_on) + + with_style = False + if pl.layer_on is False: + with_style = True + + if with_style is False: + self.file.createIfcPresentationLayerAssignment( + name, pl.description, [i["ifc"].MappedRepresentation for i in assigned_items], pl.identifier + ) + else: + self.file.createIfcPresentationLayerWithStyle( + name, pl.description, [i["ifc"].MappedRepresentation for i in assigned_items], pl.identifier, + pl.layer_on, pl.layer_frozen, pl.layer_blocked, pl.layer_styles + ) def create_materials(self): for material in self.ifc_parser.materials.values(): diff --git a/src/ifcblenderexport/blenderbim/bim/import_ifc.py b/src/ifcblenderexport/blenderbim/bim/import_ifc.py index 332df44e94..a8909cf076 100644 --- a/src/ifcblenderexport/blenderbim/bim/import_ifc.py +++ b/src/ifcblenderexport/blenderbim/bim/import_ifc.py @@ -479,6 +479,7 @@ class IfcImporter: self.profile_code("Merging by colour") self.add_project_to_scene() self.profile_code("Add project to scene") + self.get_presentation_layers() if self.ifc_import_settings.should_clean_mesh and len(self.file.by_type("IfcElement")) < 10000: self.clean_mesh() self.profile_code("Mesh cleaning") @@ -1326,6 +1327,25 @@ class IfcImporter: self.type_collection.name ].hide_viewport = True + def get_presentation_layers(self): + for f in self.file.by_type("IfcPresentationLayerAssignment"): + pl = bpy.context.scene.BIMProperties.presentation_layers.add() + pl.name = f.Name + pl.description = f.Description + pl.identifier = f.Identifier + if f.is_a() == "IfcPresentationLayerWithStyle": + pl.layer_on = f.LayerOn if f.LayerOn is not None else True + pl.layer_frozen = f.LayerFrozen if f.LayerFrozen is not None else False + pl.layer_blocked = f.LayerBlocked if f.LayerBlocked is not None else False + + for item in f.AssignedItems: + guid = item.OfProductRepresentation[0].ShapeOfProduct[0].GlobalId + for obj in bpy.context.selectable_objects: + global_id = obj.BIMObjectProperties.attributes.get("GlobalId") + if global_id and global_id.string_value == guid: + obj.BIMObjectProperties.presentation_layer.name = pl.name + obj.hide_set(not pl.layer_on) + def clean_mesh(self): obj = None last_obj = None diff --git a/src/ifcblenderexport/blenderbim/bim/operator.py b/src/ifcblenderexport/blenderbim/bim/operator.py index 9d489eaaac..4159cf4b94 100644 --- a/src/ifcblenderexport/blenderbim/bim/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/operator.py @@ -4324,6 +4324,80 @@ class GetNorthOffset(bpy.types.Operator): return {"FINISHED"} +class AddToPresentationLayer(bpy.types.Operator): + bl_idname = "bim.add_to_presentation_layer" + bl_label = "Add To Presentation Layer" + index: bpy.props.IntProperty() + + def execute(self, context): + presentation_layer = bpy.context.scene.BIMProperties.presentation_layers[self.index] + + # vl = bpy.context.scene.view_layers[presentation_layer.name] + + for obj in bpy.context.selected_objects: + obj.BIMObjectProperties.presentation_layer.name = presentation_layer.name + elem_name = obj.BIMObjectProperties.attributes["Name"].string_value + obj.hide_set(not presentation_layer.layer_on) + print(f'Adding "{elem_name}" to View/Presentation Layer "({self.index} - {presentation_layer.name})"') + + return {"FINISHED"} + + +class RemoveFromPresentationLayer(bpy.types.Operator): + bl_idname = "bim.remove_from_presentation_layer" + bl_label = "Remove Selected From Presentation Layer" + + def execute(self, context): + for el in bpy.context.selected_objects: + if el.BIMObjectProperties.presentation_layer.name != "": + el.BIMObjectProperties.presentation_layer.name = "" + + return {"FINISHED"} + + +class AddPresentationLayer(bpy.types.Operator): + bl_idname = "bim.add_presentation_layer" + bl_label = "Add Presentation Layer" + + def execute(self, context): + new = bpy.context.scene.BIMProperties.presentation_layers.add() + + # Default values of a new presentation layer + new.name = "New Presentation Layer" + new.description = "A Description" + new.identifier = "Something" + new.layer_on = True + new.layer_frozen = False + new.layer_blocked = False + + return {"FINISHED"} + + +class RemovePresentationLayer(bpy.types.Operator): + bl_idname = "bim.remove_presentation_layer" + bl_label = "Remove Presentation Layer" + index: bpy.props.IntProperty() + + def execute(self, context): + bpy.context.scene.BIMProperties.presentation_layers.remove(self.index) + + return {"FINISHED"} + + +class UpdatePresentationLayer(bpy.types.Operator): + bl_idname = "bim.update_presentation_layer" + bl_label = "Hide/Show selected Presentation Layer" + index: bpy.props.IntProperty() + + def execute(self, context): + pl = bpy.context.scene.BIMProperties.presentation_layers[self.index] + for obj in bpy.context.scene.objects: + if obj.BIMObjectProperties.presentation_layer.name == pl.name: + set_status = obj.hide_get() + obj.hide_set(not obj.hide_get()) + return {"FINISHED"} + + class AddDrawingStyleAttribute(bpy.types.Operator): bl_idname = "bim.add_drawing_style_attribute" bl_label = "Add Drawing Style Attribute" diff --git a/src/ifcblenderexport/blenderbim/bim/prop.py b/src/ifcblenderexport/blenderbim/bim/prop.py index cee36f4db0..3504cc6ddd 100644 --- a/src/ifcblenderexport/blenderbim/bim/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/prop.py @@ -792,6 +792,16 @@ class ClashSet(PropertyGroup): b: CollectionProperty(name="Group B", type=ClashSource) +class PresentationLayer(PropertyGroup): + name: StringProperty(name="Name") + description: StringProperty(name="Description") + identifier: StringProperty(name="Identifier") + layer_on: BoolProperty(name="LayerOn", default=True) + layer_frozen: BoolProperty(name="LayerFrozen", default=False) + layer_blocked: BoolProperty(name="LayerBlocked", default=False) + layer_styles: EnumProperty(items=[], name="LayerStyles") + + class Constraint(PropertyGroup): name: StringProperty(name="Name") description: StringProperty(name="Description") @@ -1564,6 +1574,8 @@ class BIMProperties(PropertyGroup): override_colour: FloatVectorProperty( name="Override Colour", subtype="COLOR", default=(1, 0, 0, 1), min=0.0, max=1.0, size=4 ) + active_presentation_layer_index: IntProperty(name="Active Presentation Layer Index") + presentation_layers: CollectionProperty(name="Presentation Layers", type=PresentationLayer) class BCFProperties(PropertyGroup): @@ -1668,6 +1680,7 @@ class BIMObjectProperties(PropertyGroup): qto_name: EnumProperty(items=getQtoNames, name="Qto Name") has_boundary_condition: BoolProperty(name="Has Boundary Condition") boundary_condition: PointerProperty(name="Boundary Condition", type=BoundaryCondition) + presentation_layer: PointerProperty(name="Presentation Layer", type=PresentationLayer) structural_member_connection: PointerProperty(name="Structural Member Connection", type=bpy.types.Object) representation_contexts: CollectionProperty(name="Representation Contexts", type=Subcontext) # Address applies to IfcSite's SiteAddress and IfcBuilding's BuildingAddress @@ -1711,7 +1724,7 @@ class BIMMeshProperties(PropertyGroup): is_swept_solid: BoolProperty(name="Is Swept Solid") swept_solids: CollectionProperty(name="Swept Solids", type=SweptSolid) is_parametric: BoolProperty(name="Is Parametric", default=False) - presentation_layer: StringProperty(name="Presentation Layer") + presentation_layer: PointerProperty(name="Presentation Layer", type=PresentationLayer) geometry_type: StringProperty(name="Geometry Type") ifc_definition: StringProperty(name="IFC Definition") ifc_definition_id: IntProperty(name="IFC Definition ID") diff --git a/src/ifcblenderexport/blenderbim/bim/ui.py b/src/ifcblenderexport/blenderbim/bim/ui.py index a29a05cadb..ed252c1796 100644 --- a/src/ifcblenderexport/blenderbim/bim/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/ui.py @@ -774,9 +774,6 @@ class BIM_PT_mesh(Panel): row.prop(ifc_parameter, "value", text="") row.operator("bim.update_ifc_representation", icon="FILE_REFRESH", text="").index = index - row = layout.row() - row.prop(props, "presentation_layer") - row = layout.row() row.prop(props, "is_parametric") row = layout.row() @@ -804,6 +801,51 @@ class BIM_PT_mesh(Panel): row.prop(props, "swept_solids") +class BIM_PT_presentation_layer_data(Panel): + bl_label = "IFC Presentation Layers" + bl_idname = "BIM_PT_presentation" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "data" + + @classmethod + def poll(cls, context): + return ( + context.active_object is not None + and context.active_object.type == "MESH" + and hasattr(context.active_object.data, "BIMMeshProperties") + ) + + def draw(self, context): + if not context.active_object.data: + return + layout = self.layout + elem_props = context.active_object.BIMObjectProperties.presentation_layer + scene_props = context.scene.BIMProperties + + if elem_props.name != "": + layout.label(text=f'Object is part of Presentation layer "{elem_props.name}"') + layout.row().operator("bim.remove_from_presentation_layer") + else: + layout.label(text="Not included in any presentation layer") + + if scene_props.presentation_layers: + layout.template_list( + "BIM_UL_presentation_layers", + "", + scene_props, + "presentation_layers", + scene_props, + "active_presentation_layer_index", + ) + pres_layer = scene_props.presentation_layers[scene_props.active_presentation_layer_index] + if pres_layer.name in bpy.context.scene.BIMProperties.presentation_layers: + op = layout.row().operator("bim.add_to_presentation_layer") + op.index = scene_props.active_presentation_layer_index + else: + layout.label(text="Presentation Layer is invalid") + + class BIM_PT_material(Panel): bl_label = "IFC Materials" bl_idname = "BIM_PT_material" @@ -909,6 +951,56 @@ class BIM_PT_gis(Panel): row.operator("bim.set_north_offset") +class BIM_PT_presentation_layers(Panel): + bl_label = "IFC Presentation Layers" + bl_idname = "BIM_PT_presentation_layer" + bl_options = {"DEFAULT_CLOSED"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + props = context.scene.BIMProperties + + layout.row().prop(props, "presentation_layers") + layout.row().operator("bim.add_presentation_layer") + + if props.presentation_layers: + layout.template_list( + "BIM_UL_presentation_layers", "", props, "presentation_layers", props, "active_presentation_layer_index" + ) + pres_layer = props.presentation_layers[props.active_presentation_layer_index] + + row = layout.row(align=True) + row.prop(pres_layer, "name") + if pres_layer.name in bpy.context.scene.BIMProperties.presentation_layers: + pres_layer = bpy.context.scene.BIMProperties.presentation_layers[pres_layer.name] + row.operator( + "bim.remove_presentation_layer", icon="X", text="" + ).index = props.active_presentation_layer_index + row = layout.row() + row.prop(pres_layer, "description") + row = layout.row() + row.prop(pres_layer, "identifier") + row = layout.row() + row.prop(pres_layer, "layer_on") + row = layout.row() + row.prop(pres_layer, "layer_frozen") + row = layout.row() + row.prop(pres_layer, "layer_blocked") + row = layout.row() + row.prop(pres_layer, "layer_styles") + + op = layout.row().operator("bim.update_presentation_layer") + op.index = props.active_presentation_layer_index + + + else: + layout.label(text="Presentation Layer is invalid") + + class BIM_PT_drawings(Panel): bl_label = "SVG Drawings" bl_idname = "BIM_PT_drawings" @@ -1981,6 +2073,15 @@ class BIM_UL_constraints(bpy.types.UIList): layout.label(text="", translate=False) +class BIM_UL_presentation_layers(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + ob = data + if item: + layout.prop(item, "name", text="", emboss=False) + else: + layout.label(text="", translate=False) + + class BIM_UL_document_information(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): ob = data diff --git a/src/ifcblenderexport/blenderbim/libs/site/packages/.gitignore b/src/ifcblenderexport/blenderbim/libs/site/packages/.gitignore new file mode 100644 index 0000000000..808d16c90d --- /dev/null +++ b/src/ifcblenderexport/blenderbim/libs/site/packages/.gitignore @@ -0,0 +1,5 @@ +* +!.gitignore +!ifcopenshell.pth +!OCC.pth +!svgwrite.pth \ No newline at end of file diff --git a/test/input b/test/input index 828994fa4c..a75c92451c 160000 --- a/test/input +++ b/test/input @@ -1 +1 @@ -Subproject commit 828994fa4c7f28c6afd3f70a9ee9f07710482f8e +Subproject commit a75c92451c8e5b63641b57ed8216849b0121d33d