diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index d10d9d34b6..a100c8bd23 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -29,6 +29,8 @@ import numpy as np import multiprocessing import ifcopenshell import ifcopenshell.api +import ifcopenshell.api.pset +import ifcopenshell.api.style import ifcopenshell.ifcopenshell_wrapper import ifcopenshell.geom import ifcopenshell.util.selector @@ -46,7 +48,7 @@ import bonsai.bim.export_ifc from bpy_extras.io_utils import ImportHelper from bonsai.bim.module.drawing.decoration import CutDecorator from bonsai.bim.module.drawing.data import DecoratorData, DrawingsData -from typing import NamedTuple, List, Union, Optional, Literal, TYPE_CHECKING, Any +from typing import NamedTuple, List, Union, Optional, Literal, TYPE_CHECKING, Any, TypedDict from lxml import etree from math import radians from mathutils import Vector, Color, Matrix @@ -57,6 +59,7 @@ from pathlib import Path from bpy_extras.image_utils import load_image if TYPE_CHECKING: + from bonsai.bim.module.drawing.prop import RenderType from bonsai.bim.module.project.prop import Link from bpy._typing import rna_enums @@ -2304,11 +2307,16 @@ class RemoveDrawing(bpy.types.Operator, tool.Ifc.Operator): props.should_draw_decorations = False +class DrawingStyleJson(TypedDict): + render_type: "RenderType" + raster_style: dict[str, Any] + + class ReloadDrawingStyles(bpy.types.Operator): bl_idname = "bim.reload_drawing_styles" bl_label = "Reload Drawing Styles" bl_options = {"REGISTER", "UNDO"} - bl_description = "Reload drawing styles for the active camera" + bl_description = "Reload drawing styles for the active camera from the related JSON file." def execute(self, context): if not DrawingsData.is_loaded: @@ -2329,7 +2337,7 @@ class ReloadDrawingStyles(bpy.types.Operator): ), "CurrentShadingStyle": tool.Drawing.get_default_shading_style(), } - ifcopenshell.api.run("pset.edit_pset", ifc_file, pset=pset, properties=edit_properties) + ifcopenshell.api.pset.edit_pset(ifc_file, pset=pset, properties=edit_properties) tool.Drawing.setup_shading_styles_path(shading_styles_path) DrawingsData.load() @@ -2354,7 +2362,7 @@ class ReloadDrawingStyles(bpy.types.Operator): shutil.copy(ootb_resource, json_path) with open(json_path, "r") as fi: - shading_styles_json = json.load(fi) + shading_styles_json: dict[str, DrawingStyleJson] = json.load(fi) props = tool.Drawing.get_document_props() drawing_styles = props.drawing_styles @@ -2368,11 +2376,10 @@ class ReloadDrawingStyles(bpy.types.Operator): drawing_style.raster_style = json.dumps(style_data["raster_style"]) if current_style is not None: - try: + if current_style not in styles: + self.report({"WARNING"}, f"Could not find style {current_style} in EPset_Drawing.ShadingStyles.") + else: camera_props.active_drawing_style_index = styles.index(current_style) - except ValueError: - self.report({"INFO"}, f"Could not find style {current_style} in EPset_Drawing.ShadingStyles.") - return {"FINISHED"} @@ -2478,6 +2485,7 @@ class SaveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator): class SaveDrawingStylesData(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.save_drawing_styles_data" bl_label = "Save Drawing Styles Data" + bl_description = "Save current drawing styles settings to IFC and JSON." bl_options = {"REGISTER", "UNDO"} skip_updating_current_style: bpy.props.BoolProperty(default=False) @@ -2499,10 +2507,9 @@ class SaveDrawingStylesData(bpy.types.Operator, tool.Ifc.Operator): self.report({"ERROR"}, "Shading styles file not found: {}".format(json_path)) return {"CANCELLED"} - styles_data = {} + styles_data: dict[str, DrawingStyleJson] = {} for style in drawing_styles: - style_data = {"render_type": style.render_type, "raster_style": json.loads(style.raster_style)} - styles_data[style.name] = style_data + styles_data[style.name] = {"render_type": style.render_type, "raster_style": json.loads(style.raster_style)} with open(json_path, "w") as fo: json.dump(styles_data, fo, indent=4) @@ -2521,9 +2528,8 @@ class SaveDrawingStylesData(bpy.types.Operator, tool.Ifc.Operator): ifc_file = tool.Ifc.get() drawing = ifc_file.by_id(props.active_drawing_id) pset = tool.Pset.get_element_pset(drawing, "EPset_Drawing") - ifcopenshell.api.run( - "pset.edit_pset", ifc_file, pset=pset, properties={"CurrentShadingStyle": new_style_name} - ) + assert pset + ifcopenshell.api.pset.edit_pset(ifc_file, pset=pset, properties={"CurrentShadingStyle": new_style_name}) bonsai.bim.handler.refresh_ui_data() return {"FINISHED"} @@ -2553,8 +2559,9 @@ class ActivateDrawingStyle(bpy.types.Operator, tool.Ifc.Operator): drawing = ifc_file.by_id(props.active_drawing_id) pset = tool.Pset.get_element_pset(drawing, "EPset_Drawing") - ifcopenshell.api.run( - "pset.edit_pset", ifc_file, pset=pset, properties={"CurrentShadingStyle": self.drawing_style.name} + assert pset + ifcopenshell.api.pset.edit_pset( + ifc_file, pset=pset, properties={"CurrentShadingStyle": self.drawing_style.name} ) bonsai.bim.handler.refresh_ui_data() return {"FINISHED"} @@ -3453,12 +3460,13 @@ class EditElementFilter(bpy.types.Operator, tool.Ifc.Operator): element = tool.Ifc.get_entity(obj) assert element pset = tool.Pset.get_element_pset(element, "EPset_Drawing") + assert pset if self.filter_mode == "INCLUDE": query = tool.Search.export_filter_query(props.include_filter_groups) or None - ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Include": query}) + ifcopenshell.api.pset.edit_pset(tool.Ifc.get(), pset=pset, properties={"Include": query}) elif self.filter_mode == "EXCLUDE": query = tool.Search.export_filter_query(props.exclude_filter_groups) or None - ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Exclude": query}) + ifcopenshell.api.pset.edit_pset(tool.Ifc.get(), pset=pset, properties={"Exclude": query}) props.filter_mode = "NONE" bpy.ops.bim.activate_drawing(drawing=element.id(), should_view_from_camera=False) @@ -3556,8 +3564,7 @@ class AddReferenceImage(bpy.types.Operator, tool.Ifc.Operator, ImportHelper): "Transparency": 0.0, "ReflectanceMethod": "NOTDEFINED", } - ifcopenshell.api.run( - "style.add_surface_style", + ifcopenshell.api.style.add_surface_style( tool.Ifc.get(), style=style, ifc_class="IfcSurfaceStyleRendering", diff --git a/src/bonsai/bonsai/bim/module/drawing/prop.py b/src/bonsai/bonsai/bim/module/drawing/prop.py index 48d7709b3d..45f318ec07 100644 --- a/src/bonsai/bonsai/bim/module/drawing/prop.py +++ b/src/bonsai/bonsai/bim/module/drawing/prop.py @@ -366,15 +366,18 @@ class Sheet(PropertyGroup): is_expanded: bool +RenderType = Literal["NONE", "DEFAULT", "VIEWPORT"] + + class DrawingStyle(PropertyGroup): name: StringProperty(name="Name", get=get_drawing_style_name, set=set_drawing_style_name) - raster_style: StringProperty(name="Raster Style", default="{}") + raster_style: StringProperty( + name="Raster Style", + description="JSON string for drawing style settings.", + default="{}", + ) render_type: EnumProperty( - items=[ - ("NONE", "None", ""), - ("DEFAULT", "Default", ""), - ("VIEWPORT", "Viewport", ""), - ], + items=[(t, t.capitalize(), "") for t in get_args(RenderType)], name="Render Type", default="VIEWPORT", ) @@ -382,6 +385,14 @@ class DrawingStyle(PropertyGroup): exclude_query: StringProperty(name="Exclude Query") attributes: CollectionProperty(name="Attributes", type=StrProperty) + if TYPE_CHECKING: + name: str + raster_style: str + render_type: RenderType + include_query: str + exclude_query: str + attributes: bpy.types.bpy_prop_collection_idprop[StrProperty] + class RasterStyleProperty(enum.Enum): # EVAL_PROP_ props will be evaluated explicitly diff --git a/src/bonsai/bonsai/bim/module/drawing/ui.py b/src/bonsai/bonsai/bim/module/drawing/ui.py index b81ac7f737..5b0bd52c28 100644 --- a/src/bonsai/bonsai/bim/module/drawing/ui.py +++ b/src/bonsai/bonsai/bim/module/drawing/ui.py @@ -622,7 +622,16 @@ class BIM_PT_text(Panel): class BIM_UL_drawinglist(bpy.types.UIList): - def draw_item(self, context, layout, data: DocProperties, item: Drawing, icon, active_data, active_propname): + def draw_item( + self, + context, + layout: bpy.types.UILayout, + data: DocProperties, + item: Drawing, + icon, + active_data, + active_propname, + ): if not item: layout.label(text="", translate=False) return