From 476604202a8968102eee93c97c7e18a00c5106e4 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 21 Apr 2025 12:43:08 +0500 Subject: [PATCH] typing --- .../bonsai/bim/module/drawing/handler.py | 19 ++++----- .../bonsai/bim/module/drawing/helper.py | 14 +++---- .../bonsai/bim/module/drawing/operator.py | 42 +++++++++++-------- src/bonsai/bonsai/bim/module/drawing/prop.py | 27 +++++++++++- .../bonsai/bim/module/project/operator.py | 3 ++ src/bonsai/bonsai/tool/blender.py | 18 ++++---- src/bonsai/bonsai/tool/drawing.py | 25 +++++++++-- src/bonsai/bonsai/tool/loader.py | 9 ++-- src/bonsai/bonsai/tool/search.py | 6 ++- .../api/geometry/add_representation.py | 24 +++++++---- .../ifcopenshell/util/date.py | 5 ++- 11 files changed, 127 insertions(+), 65 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/drawing/handler.py b/src/bonsai/bonsai/bim/module/drawing/handler.py index d1622d1908..0ae6ef74b4 100644 --- a/src/bonsai/bonsai/bim/module/drawing/handler.py +++ b/src/bonsai/bonsai/bim/module/drawing/handler.py @@ -38,18 +38,17 @@ def depsgraph_update_pre_handler(scene): def set_active_camera_resolution(scene: bpy.types.Scene) -> None: props = tool.Drawing.get_document_props() - if not scene.camera or "/" not in scene.camera.name or not props.drawings: + camera_obj = scene.camera + if not camera_obj or "/" not in camera_obj.name or not props.drawings: return - assert isinstance(scene.camera.data, bpy.types.Camera) - props = scene.camera.data.BIMCameraProperties + assert isinstance((camera := camera_obj.data), bpy.types.Camera) + props = tool.Drawing.get_camera_props(camera) ortho_scale = max((props.width, props.height)) aspect_ratio = props.width / props.height - if (scene.camera.data.ortho_scale != ortho_scale) or ( - scene.render.resolution_x / scene.render.resolution_y != aspect_ratio - ): - scene.camera.data.ortho_scale = ortho_scale + if (camera.ortho_scale != ortho_scale) or (scene.render.resolution_x / scene.render.resolution_y != aspect_ratio): + camera.ortho_scale = ortho_scale - diagram_scale = tool.Drawing.get_diagram_scale(scene.camera) + diagram_scale = tool.Drawing.get_diagram_scale(camera_obj) scale_ratio = tool.Drawing.get_scale_ratio(diagram_scale["Scale"]) if props.width > props.height: @@ -61,5 +60,5 @@ def set_active_camera_resolution(scene: bpy.types.Scene) -> None: raster_x = ortho_scale * aspect_ratio * scale_ratio * props.dpi / 0.0254 raster_y = ortho_scale * scale_ratio * props.dpi / 0.0254 - scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x = int(raster_x) - scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y = int(raster_y) + scene.render.resolution_x = props.raster_x = int(raster_x) + scene.render.resolution_y = props.raster_y = int(raster_y) diff --git a/src/bonsai/bonsai/bim/module/drawing/helper.py b/src/bonsai/bonsai/bim/module/drawing/helper.py index 91149b639d..3aa36ea366 100644 --- a/src/bonsai/bonsai/bim/module/drawing/helper.py +++ b/src/bonsai/bonsai/bim/module/drawing/helper.py @@ -413,12 +413,13 @@ def get_project_collection(scene): return colls[0] -def parse_diagram_scale(camera): +def parse_diagram_scale(camera: bpy.types.Camera) -> float: """Returns numeric value of scale""" - if camera.BIMCameraProperties.diagram_scale == "CUSTOM": - _, fraction = camera.BIMCameraProperties.custom_diagram_scale.split("|") + props = tool.Drawing.get_camera_props(camera) + if props.diagram_scale == "CUSTOM": + _, fraction = props.custom_diagram_scale.split("|") else: - _, fraction = camera.BIMCameraProperties.diagram_scale.split("|") + _, fraction = props.diagram_scale.split("|") numerator, denominator = fraction.split("/") return float(numerator) / float(denominator) @@ -431,12 +432,11 @@ def ortho_view_frame( Similar to `bpy.types.Camera.view_frame` :arg camera: camera of drawing - :type camera: bpy.types.Camera + BIMCameraProperties :arg margin: margins, in scene units - :type margin: float :return: (xmin, xmax, ymin, ymax, zmin, zmax) in local camera coordinates """ - aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x + props = tool.Drawing.get_camera_props(camera) + aspect = props.raster_y / props.raster_x size = camera.ortho_scale hwidth = size * 0.5 hheight = size * 0.5 * aspect diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index 6dabaa42bd..799732494d 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -228,7 +228,9 @@ class CreateDrawing(bpy.types.Operator): if not tool.Drawing.is_drawing_active(): cls.poll_message_set("No active drawing.") return False - if context.scene.camera.data.BIMCameraProperties.linework_mode == "FREESTYLE" and not hasattr( + assert context.scene + assert (camera_obj := context.scene.camera) + if tool.Drawing.get_camera_props(camera_obj).linework_mode == "FREESTYLE" and not hasattr( context.scene, "svg_export" ): cls.poll_message_set( @@ -248,6 +250,7 @@ class CreateDrawing(bpy.types.Operator): def execute(self, context): self.props = tool.Drawing.get_document_props() + assert context.scene and context.scene.camera active_drawing_id = tool.Blender.get_ifc_definition_id(context.scene.camera) if self.print_all: @@ -262,13 +265,14 @@ class CreateDrawing(bpy.types.Operator): bpy.ops.bim.activate_drawing(drawing=drawing_id, should_view_from_camera=False) self.camera = context.scene.camera - self.camera_element = tool.Ifc.get_entity(self.camera) + assert (camera_element := tool.Ifc.get_entity(self.camera)) + self.camera_element = camera_element self.camera_document = tool.Drawing.get_drawing_document(self.camera_element) self.file = tool.Ifc.get() with profile("Drawing generation process"): with profile("Initialize drawing generation process"): - self.cprops = self.camera.data.BIMCameraProperties + self.cprops = tool.Drawing.get_camera_props(self.camera) self.drawing = self.file.by_id(drawing_id) self.drawing_name = self.drawing.Name self.metadata = tool.Drawing.get_drawing_metadata(self.camera_element) @@ -297,11 +301,11 @@ class CreateDrawing(bpy.types.Operator): with profile("Generate linework"): if tool.Drawing.is_camera_orthographic(): - if self.camera.data.BIMCameraProperties.linework_mode == "OPENCASCADE": + if self.cprops.linework_mode == "OPENCASCADE": linework_svg = self.generate_linework(context) - elif self.camera.data.BIMCameraProperties.linework_mode == "FREESTYLE": + elif self.cprops.linework_mode == "FREESTYLE": linework_svg = self.generate_freestyle_linework(context) - elif self.camera.data.BIMCameraProperties.linework_mode == "FREESTYLE": + elif self.cprops.linework_mode == "FREESTYLE": linework_svg = self.generate_freestyle_linework(context) with profile("Generate annotation"): @@ -876,19 +880,19 @@ class CreateDrawing(bpy.types.Operator): return svg_path - if self.camera.data.BIMCameraProperties.cut_mode == "BISECT": + if self.cprops.cut_mode == "BISECT": self.remove_cut_linework(root) self.generate_bisect_linework(context, root) self.generate_wall_layers(context, root) self.merge_linework_and_add_metadata(root) self.move_elements_to_top(root) - elif self.camera.data.BIMCameraProperties.cut_mode == "OPENCASCADE": + elif self.cprops.cut_mode == "OPENCASCADE": self.move_projection_to_bottom(root) self.generate_wall_layers(context, root) self.merge_linework_and_add_metadata(root) self.move_elements_to_top(root) - if self.camera.data.BIMCameraProperties.fill_mode == "SHAPELY": + if self.cprops.fill_mode == "SHAPELY": # shapely variant group = root.find("{http://www.w3.org/2000/svg}g") @@ -960,7 +964,7 @@ class CreateDrawing(bpy.types.Operator): path.set("class", " ".join(list(classes))) group.insert(0, path) - if self.camera.data.BIMCameraProperties.fill_mode == "SVGFILL": + if self.cprops.fill_mode == "SVGFILL": results = etree.tostring(root).decode("utf8") svg_data_1 = results from xml.dom.minidom import parseString @@ -1449,10 +1453,9 @@ class CreateDrawing(bpy.types.Operator): g.set("class", " ".join(list(classes))) group.append(g) - def drawing_to_model_co(self, x, y): + def drawing_to_model_co(self, x: float, y: float) -> Vector: camera_xy = np.array((x, -y)) / self.scale / 1000 - camera_props = self.camera.data.BIMCameraProperties - camera_xy += np.array((camera_props.width / -2, camera_props.height / 2)) # top left offset + camera_xy += np.array((self.cprops.width / -2, self.cprops.height / 2)) # top left offset return self.camera.matrix_world @ Vector(camera_xy).to_3d() def pythonize(self, arr): @@ -2158,7 +2161,7 @@ class ActivateDrawingBase: # Save drawing bounds to the .ifc file camera = context.scene.camera - camera_props = camera.data.BIMCameraProperties + camera_props = tool.Drawing.get_camera_props(camera) if camera_props.update_representation(camera): bpy.ops.bim.update_representation(obj=camera.name, ifc_representation_class="") # See 6452 and 6478. @@ -2298,7 +2301,8 @@ class ReloadDrawingStyles(bpy.types.Operator): if not DrawingsData.is_loaded: DrawingsData.load() drawing_pset_data = DrawingsData.data["active_drawing_pset_data"] - camera_props = context.scene.camera.data.BIMCameraProperties + assert context.scene and (camera := context.scene.camera) + camera_props = tool.Drawing.get_camera_props(camera) # added this part as a temporary fallback # TODO: should remove it a bit later when projects get more accommodated @@ -2365,12 +2369,14 @@ class AddDrawingStyle(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): + assert context.scene and (camera_obj := context.scene.camera) props = tool.Drawing.get_document_props() drawing_styles = props.drawing_styles new = drawing_styles.add() # drawing style is saved to ifc on rename new.name = tool.Blender.ensure_unique_name("New Drawing Style", drawing_styles) - context.scene.camera.data.BIMCameraProperties.active_drawing_style_index = len(drawing_styles) - 1 + camera_props = tool.Drawing.get_camera_props(camera_obj) + camera_props.active_drawing_style_index = len(drawing_styles) - 1 return {"FINISHED"} @@ -2381,9 +2387,11 @@ class RemoveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator): index: bpy.props.IntProperty() def execute(self, context): + assert context.scene and (camera_obj := context.scene.camera) props = tool.Drawing.get_document_props() props.drawing_styles.remove(self.index) - context.scene.camera.data.BIMCameraProperties.active_drawing_style_index = max(self.index - 1, 0) + camera_props = tool.Drawing.get_camera_props(camera_obj) + camera_props.active_drawing_style_index = max(self.index - 1, 0) bpy.ops.bim.save_drawing_styles_data() return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/drawing/prop.py b/src/bonsai/bonsai/bim/module/drawing/prop.py index 8963fa5b8f..db52f9e330 100644 --- a/src/bonsai/bonsai/bim/module/drawing/prop.py +++ b/src/bonsai/bonsai/bim/module/drawing/prop.py @@ -522,9 +522,34 @@ class BIMCameraProperties(PropertyGroup): exclude_filter_groups: CollectionProperty(type=BIMFilterGroup, name="Exclude Filter") update_props: BoolProperty(name="Enable Props Auto Update", default=True) + if TYPE_CHECKING: + linework_mode: Literal["OPENCASCADE", "FREESTYLE"] + fill_mode: Literal["NONE", "SHAPELY", "SVGFILL"] + cut_mode: Literal["BISECT", "OPENCASCADE"] + has_underlay: bool + has_linework: bool + has_annotation: bool + representation: str + view_name: str + diagram_scale: str + custom_scale_numerator: str + custom_scale_denominator: str + raster_x: int + raster_y: int + dpi: int + width: float + height: float + is_nts: bool + active_drawing_style_index: int + filter_mode: str + include_filter_groups: bpy.types.bpy_prop_collection_idprop[BIMFilterGroup] + exclude_filter_groups: bpy.types.bpy_prop_collection_idprop[BIMFilterGroup] + update_props: bool + # For now, this JSON dump are all the parameters that determine a camera's "Block representation" # By checking this, you will know whether or not the camera IFC representation needs to be refreshed - def update_representation(self, obj): + def update_representation(self, obj: bpy.types.Object) -> bool: + assert isinstance(obj.data, bpy.types.Camera) representation = json.dumps( { "matrix": [list(x) for x in obj.matrix_world], diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index 4caadd9c01..7e12477c6c 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -1192,6 +1192,9 @@ class LinkIfc(bpy.types.Operator, ImportHelper): filepath: str files: list[bpy.types.OperatorFileListElement] directory: str + filter_glob: str + use_relative_path: bool + use_cache: bool def draw(self, context): pprops = tool.Project.get_project_props() diff --git a/src/bonsai/bonsai/tool/blender.py b/src/bonsai/bonsai/tool/blender.py index 546161bb33..84ccbbb550 100644 --- a/src/bonsai/bonsai/tool/blender.py +++ b/src/bonsai/bonsai/tool/blender.py @@ -82,7 +82,7 @@ class Blender(bonsai.core.tool.Blender): @classmethod def activate_camera(cls, obj: bpy.types.Object) -> None: - area = tool.Blender.get_view3d_area() + area = cls.get_view3d_area() is_local_view = area.spaces[0].local_view is not None if is_local_view: @@ -206,7 +206,7 @@ class Blender(bonsai.core.tool.Blender): if context is None: context = bpy.context if obj_type == "Object": - props = tool.Blender.get_object_bim_props(bpy.data.objects[obj]) + props = cls.get_object_bim_props(bpy.data.objects[obj]) return props.ifc_definition_id elif obj_type == "Material": props = tool.Material.get_material_props() @@ -239,7 +239,7 @@ class Blender(bonsai.core.tool.Blender): @classmethod def is_ifc_object(cls, obj: bpy.types.Object) -> bool: - props = tool.Blender.get_object_bim_props(obj) + props = cls.get_object_bim_props(obj) return bool(props.ifc_definition_id) @classmethod @@ -338,7 +338,7 @@ class Blender(bonsai.core.tool.Blender): @classmethod def set_viewport_tool(cls, tool_name: str) -> None: - with bpy.context.temp_override(**tool.Blender.get_viewport_context()): + with bpy.context.temp_override(**cls.get_viewport_context()): bpy.ops.wm.tool_set_by_id(name=tool_name) @classmethod @@ -398,7 +398,7 @@ class Blender(bonsai.core.tool.Blender): @classmethod def update_viewport(cls) -> None: - tool.Blender.get_viewport_context()["area"].tag_redraw() + cls.get_viewport_context()["area"].tag_redraw() @classmethod def force_depsgraph_update(cls) -> None: @@ -711,7 +711,7 @@ class Blender(bonsai.core.tool.Blender): False if enum is still invalid (as there no enum items) and update callback was not triggered (may need to trigger it manually). """ - current_value = tool.Blender.get_enum_safe(props, prop_name) + current_value = cls.get_enum_safe(props, prop_name) if current_value is not None: # Value is valid, just trigger the update callback. setattr(props, prop_name, current_value) @@ -958,7 +958,7 @@ class Blender(bonsai.core.tool.Blender): @classmethod def get_layer_collection(cls, collection: bpy.types.Collection) -> Union[bpy.types.LayerCollection, None]: project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) - project_collection = tool.Blender.get_object_bim_props(project).collection + project_collection = cls.get_object_bim_props(project).collection for layer_collection in bpy.context.view_layer.layer_collection.children: if layer_collection.collection == project_collection: for layer_collection2 in layer_collection.children: @@ -1564,7 +1564,7 @@ class Blender(bonsai.core.tool.Blender): @classmethod def get_user_data_dir(cls) -> Path: - props = tool.Blender.get_bim_props() + props = cls.get_bim_props() return Path(props.data_dir) @classmethod @@ -1649,7 +1649,7 @@ class Blender(bonsai.core.tool.Blender): @classmethod def get_ifc_definition_id(cls, obj: IFC_CONNECTED_TYPE) -> int: if isinstance(obj, bpy.types.Object): - return tool.Blender.get_object_bim_props(obj).ifc_definition_id + return cls.get_object_bim_props(obj).ifc_definition_id return tool.Style.get_material_style_props(obj).ifc_definition_id @classmethod diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index a9a6ebcd06..c2fd0e95a4 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -55,7 +55,13 @@ from typing import Optional, Union, Iterable, Any, Literal, Sequence, TYPE_CHECK from pathlib import Path if TYPE_CHECKING: - from bonsai.bim.module.drawing.prop import DocProperties, Sheet, BIMAnnotationProperties, BIMTextProperties + from bonsai.bim.module.drawing.prop import ( + DocProperties, + Sheet, + BIMAnnotationProperties, + BIMTextProperties, + BIMCameraProperties, + ) from bonsai.bim.module.drawing.prop import Drawing as DrawingProperties @@ -106,6 +112,17 @@ class Drawing(bonsai.core.tool.Drawing): def get_text_props(cls, obj: bpy.types.Object) -> BIMTextProperties: return obj.BIMTextProperties + @classmethod + def get_camera_props(cls, camera: Union[bpy.types.Object, bpy.types.Camera]) -> BIMCameraProperties: + """ + :param camera: Camera object or camera. + """ + if isinstance(camera, bpy.types.Camera): + data = camera + else: + assert isinstance(data := camera.data, bpy.types.Camera) + return data.BIMCameraProperties + @classmethod def canonicalise_class_name(cls, name: str) -> str: return re.sub("[^0-9a-zA-Z]+", "", name) @@ -780,7 +797,7 @@ class Drawing(bonsai.core.tool.Drawing): from bonsai.bim.module.drawing.prop import get_diagram_scales # Temporarily clear the definition id to prevent prop update callbacks to IFC. - camera_props = camera.BIMCameraProperties + camera_props = tool.Drawing.get_camera_props(camera) update_props = camera_props.update_props camera_props.update_props = False @@ -2199,8 +2216,8 @@ class Drawing(bonsai.core.tool.Drawing): return float(numerator) / float(denominator) @classmethod - def get_diagram_scale(cls, obj: bpy.types.Object) -> dict[str, float]: - props = obj.data.BIMCameraProperties + def get_diagram_scale(cls, obj: bpy.types.Object) -> dict[str, str]: + props = cls.get_camera_props(obj) scale = props.diagram_scale if scale != "CUSTOM": human_scale, scale = scale.split("|") diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index a8b2fa77f7..f45242f760 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -839,13 +839,14 @@ class Loader(bonsai.core.tool.Loader): camera = bpy.data.cameras.new(tool.Loader.get_mesh_name_from_shape(geometry)) camera.type = camera_type camera.show_limits = True + props = tool.Drawing.get_camera_props(camera) if camera_type == "ORTHO": camera.clip_start = 0.002 # Technically 0, but Blender doesn't allow this, so 2mm it is! camera.clip_end = depth - camera.BIMCameraProperties.width = width - camera.BIMCameraProperties.height = height + props.width = width + props.height = height elif camera_type == "PERSP": abs_min_z = abs(min(z)) abs_max_z = abs(max(z)) @@ -853,8 +854,8 @@ class Loader(bonsai.core.tool.Loader): camera.clip_end = abs_min_z max_res = 1000 - camera.BIMCameraProperties.width = width - camera.BIMCameraProperties.height = height + props.width = width + props.height = height if width > height: fov = 2 * atan(width / (2 * abs_min_z)) diff --git a/src/bonsai/bonsai/tool/search.py b/src/bonsai/bonsai/tool/search.py index 635b1c01dd..df54d7d389 100644 --- a/src/bonsai/bonsai/tool/search.py +++ b/src/bonsai/bonsai/tool/search.py @@ -51,9 +51,11 @@ class Search(bonsai.core.tool.Search): elif module == "diff": return tool.Blender.get_diff_props().filter_groups elif module == "drawing_include": - return bpy.context.scene.camera.data.BIMCameraProperties.include_filter_groups + assert (scene := bpy.context.scene) and (camera_obj := (scene.camera)) + return tool.Drawing.get_camera_props(camera_obj).include_filter_groups elif module == "drawing_exclude": - return bpy.context.scene.camera.data.BIMCameraProperties.exclude_filter_groups + assert (scene := bpy.context.scene) and (camera_obj := (scene.camera)) + return tool.Drawing.get_camera_props(camera_obj).exclude_filter_groups elif module.startswith("clash"): _, clash_set_index, ab, clash_source_index = module.split("_") props = tool.Clash.get_clash_props() diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py index d6d729d8aa..e39887f7b1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/add_representation.py @@ -28,6 +28,7 @@ from typing import Union, Optional, Literal, Any, TYPE_CHECKING from ifcopenshell.util.shape_builder import ifc_safe_vector_type, VectorType if TYPE_CHECKING: + import bonsai.tool as tool from bonsai.bim.module.geometry.helper import Helper @@ -81,9 +82,11 @@ def add_representation( """ # lazy import Helper to avoid circular import if "Helper" not in globals(): + import bonsai.tool as tool from bonsai.bim.module.geometry.helper import Helper globals()["Helper"] = Helper + globals()["tool"] = tool usecase = Usecase() # TODO: This usecase currently depends on Blender's data model @@ -111,7 +114,7 @@ class Usecase: settings: dict[str, Any] ifc_vertices: list[ifcopenshell.entity_instance] coordinate_offset: Union[npt.NDArray[np.float64], None] - geometry: Union[bpy.types.Mesh, bpy.types.Curve] + geometry: Union[bpy.types.Mesh, bpy.types.Curve, bpy.types.Camera] blender_object: bpy.types.Object def execute(self) -> Union[ifcopenshell.entity_instance, None]: @@ -319,8 +322,10 @@ class Usecase: return self.create_mesh_representation() def create_camera_block_representation(self) -> ifcopenshell.entity_instance: - raster_x = self.settings["geometry"].BIMCameraProperties.raster_x - raster_y = self.settings["geometry"].BIMCameraProperties.raster_y + assert isinstance(self.geometry, bpy.types.Camera) + props = tool.Drawing.get_camera_props(self.geometry) + raster_x = props.raster_x + raster_y = props.raster_y if self.is_camera_landscape(): width = self.settings["geometry"].ortho_scale @@ -347,8 +352,10 @@ class Usecase: ) def create_camera_pyramid_representation(self) -> ifcopenshell.entity_instance: - raster_x = self.settings["geometry"].BIMCameraProperties.raster_x - raster_y = self.settings["geometry"].BIMCameraProperties.raster_y + assert isinstance(self.geometry, bpy.types.Camera) + props = tool.Drawing.get_camera_props(self.geometry) + raster_x = props.raster_x + raster_y = props.raster_y fov = self.settings["geometry"].angle clip_end = self.settings["geometry"].clip_end @@ -389,10 +396,9 @@ class Usecase: ) def is_camera_landscape(self) -> bool: - return ( - self.settings["geometry"].BIMCameraProperties.raster_x - > self.settings["geometry"].BIMCameraProperties.raster_y - ) + assert isinstance(self.geometry, bpy.types.Camera) + props = tool.Drawing.get_camera_props(self.geometry) + return props.raster_x > props.raster_y def create_swept_disk_solid_representation(self) -> ifcopenshell.entity_instance: return self.file.createIfcShapeRepresentation( diff --git a/src/ifcopenshell-python/ifcopenshell/util/date.py b/src/ifcopenshell-python/ifcopenshell/util/date.py index e6307827da..f3cc59078f 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/date.py +++ b/src/ifcopenshell-python/ifcopenshell/util/date.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with IfcOpenShell. If not, see . +import ifcopenshell import datetime import isodate from re import findall @@ -37,7 +38,7 @@ def timedelta2duration(timedelta): return isodate.Duration(**components) -def ifc2datetime(element): +def ifc2datetime(element: Union[str, int, ifcopenshell.entity_instance]): if isinstance(element, str) and "P" in element[0:2]: # IfcDuration duration = parse_duration(element) if isinstance(duration, datetime.timedelta): @@ -246,7 +247,7 @@ def parse_duration(value: Union[str, None]) -> Union[datetime.timedelta, None]: return None -def canonicalise_time(time): +def canonicalise_time(time: Union[datetime.datetime, None]) -> str: if not time: return "-" return time.strftime("%d/%m/%y")