diff --git a/src/blenderbim/blenderbim/bim/module/cost/data.py b/src/blenderbim/blenderbim/bim/module/cost/data.py index b1a7d6f365..61bf88ddc3 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/data.py +++ b/src/blenderbim/blenderbim/bim/module/cost/data.py @@ -19,10 +19,13 @@ import bpy import ifcopenshell import ifcopenshell.util.cost +import ifcopenshell.util.date import ifcopenshell.util.element +import ifcopenshell.util.unit import blenderbim.tool as tool import blenderbim.bim.schema from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc +from typing import Any def refresh(): @@ -34,6 +37,7 @@ def refresh(): class CostSchedulesData: data = {} is_loaded = False + _cost_values: dict[int, dict[str, Any]] @classmethod def load(cls): diff --git a/src/blenderbim/blenderbim/bim/module/model/opening.py b/src/blenderbim/blenderbim/bim/module/model/opening.py index 1d55fb305d..5a7e517d71 100644 --- a/src/blenderbim/blenderbim/bim/module/model/opening.py +++ b/src/blenderbim/blenderbim/bim/module/model/opening.py @@ -44,6 +44,7 @@ from bpy.types import SpaceView3D from bpy.props import FloatProperty from bpy_extras.object_utils import AddObjectHelper, object_data_add from gpu_extras.batch import batch_for_shader +from typing import Union, Optional, Any class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator): @@ -59,7 +60,12 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator): class FilledOpeningGenerator: - def generate(self, filling_obj, voided_obj, target=None): + def generate( + self, + filling_obj: Union[bpy.types.Object, None], + voided_obj: Union[bpy.types.Object, None], + target: Optional[Vector] = None, + ) -> None: props = bpy.context.scene.BIMModelProperties unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) @@ -187,7 +193,7 @@ class FilledOpeningGenerator: should_sync_changes_first=False, ) - def regenerate_from_type(self, usecase_path, ifc_file, settings): + def regenerate_from_type(self, usecase_path: str, ifc_file: ifcopenshell.file, settings: dict[str, Any]) -> None: relating_type = settings["relating_type"] for related_object in settings["related_objects"]: @@ -252,7 +258,9 @@ class FilledOpeningGenerator: should_sync_changes_first=False, ) - def generate_opening_from_filling(self, filling, filling_obj): + def generate_opening_from_filling( + self, filling: ifcopenshell.entity_instance, filling_obj: bpy.types.Object + ) -> ifcopenshell.entity_instance: # Since openings are reused later, we give a default thickness of 1.2m # which should cover the majority of curved, or super thick walls. thickness = 1.2 @@ -346,7 +354,9 @@ class FilledOpeningGenerator: return True return False - def get_existing_opening_occurrence_if_any(self, filling): + def get_existing_opening_occurrence_if_any( + self, filling: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, None]: filling_type = ifcopenshell.util.element.get_type(filling) if filling_type: filling_occurrences = ifcopenshell.util.element.get_types(filling_type) diff --git a/src/blenderbim/blenderbim/bim/module/pset_template/data.py b/src/blenderbim/blenderbim/bim/module/pset_template/data.py index 96c17cc712..77b6685a36 100644 --- a/src/blenderbim/blenderbim/bim/module/pset_template/data.py +++ b/src/blenderbim/blenderbim/bim/module/pset_template/data.py @@ -21,6 +21,7 @@ import bpy import pathlib import ifcopenshell import ifcopenshell.util.attribute +import ifcopenshell.util.doc import blenderbim.tool as tool from blenderbim.bim.ifc import IfcStore diff --git a/src/blenderbim/blenderbim/core/cost.py b/src/blenderbim/blenderbim/core/cost.py index e703844b23..f23cb60a49 100644 --- a/src/blenderbim/blenderbim/core/cost.py +++ b/src/blenderbim/blenderbim/core/cost.py @@ -109,7 +109,9 @@ def edit_cost_item(ifc: tool.Ifc, cost: tool.Cost): cost.load_cost_schedule_tree() -def assign_cost_item_type(ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial, cost_item, prop_name): +def assign_cost_item_type( + ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial, cost_item: ifcopenshell.entity_instance, prop_name +): product_types = spatial.get_selected_product_types() [ ifc.run("control.assign_control", relating_control=cost_item, related_object=product_type) @@ -118,7 +120,9 @@ def assign_cost_item_type(ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial, cost.load_cost_item_types(cost_item) -def unassign_cost_item_type(ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial, cost_item, product_types): +def unassign_cost_item_type( + ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial, cost_item: ifcopenshell.entity_instance, product_types +): if not product_types: product_types = spatial.get_selected_product_types() [ @@ -133,7 +137,9 @@ def load_cost_item_types(cost: tool.Cost): cost.load_cost_item_types(cost_item) -def assign_cost_item_quantity(ifc: tool.Ifc, cost: tool.Cost, cost_item, related_object_type, prop_name): +def assign_cost_item_quantity( + ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance, related_object_type, prop_name +): products = cost.get_products(related_object_type) if products: ifc.run("cost.assign_cost_item_quantity", cost_item=cost_item, products=products, prop_name=prop_name) @@ -159,7 +165,7 @@ def load_cost_item_resource_quantities(cost: tool.Cost): cost.load_cost_item_quantity_assignments(cost_item, related_object_type="RESOURCE") -def assign_cost_value(ifc: tool.Ifc, cost_item, cost_rate): +def assign_cost_value(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, cost_rate): ifc.run("cost.assign_cost_value", cost_item=cost_item, cost_rate=cost_rate) @@ -167,7 +173,7 @@ def load_schedule_of_rates(cost: tool.Cost, schedule_of_rates): cost.load_schedule_of_rates_tree(schedule_of_rates) -def unassign_cost_item_quantity(ifc: tool.Ifc, cost: tool.Cost, cost_item, products): +def unassign_cost_item_quantity(ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance, products): ifc.run("cost.unassign_cost_item_quantity", cost_item=cost_item, products=products) cost.load_cost_item_quantities() @@ -180,11 +186,11 @@ def enable_editing_cost_item_values(cost: tool.Cost, cost_item: ifcopenshell.ent cost.enable_editing_cost_item_values(cost_item) -def add_cost_item_quantity(ifc: tool.Ifc, cost_item, ifc_class): +def add_cost_item_quantity(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, ifc_class): ifc.run("cost.add_cost_item_quantity", cost_item=cost_item, ifc_class=ifc_class) -def remove_cost_item_quantity(ifc: tool.Ifc, cost_item, physical_quantity): +def remove_cost_item_quantity(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, physical_quantity): ifc.run("cost.remove_cost_item_quantity", cost_item=cost_item, physical_quantity=physical_quantity) @@ -288,7 +294,9 @@ def export_cost_schedules(cost: tool.Cost, filepath, format, cost_schedule=None) return cost.export_cost_schedules(filepath, format, cost_schedule) -def clear_cost_item_assignments(ifc: tool.Ifc, cost: tool.Cost, cost_item, related_object_type): +def clear_cost_item_assignments( + ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance, related_object_type +): products = cost.get_cost_item_assignments(cost_item, filter_by_type=related_object_type) if products: ifc.run("cost.unassign_cost_item_quantity", cost_item=cost_item, products=products) diff --git a/src/blenderbim/blenderbim/core/drawing.py b/src/blenderbim/blenderbim/core/drawing.py index f52fc2e1c5..582a80551b 100644 --- a/src/blenderbim/blenderbim/core/drawing.py +++ b/src/blenderbim/blenderbim/core/drawing.py @@ -16,36 +16,44 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +from __future__ import annotations from pathlib import Path -import ifcopenshell +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + import bpy + import ifcopenshell + import blenderbim.tool as tool -def enable_editing_text(drawing, obj=None): +def enable_editing_text(drawing: tool.Drawing, obj: bpy.types.Object) -> None: drawing.enable_editing_text(obj) drawing.import_text_attributes(obj) -def disable_editing_text(drawing, obj=None): +def disable_editing_text(drawing: tool.Drawing, obj: bpy.types.Object) -> None: drawing.disable_editing_text(obj) -def edit_text(drawing, obj=None): +def edit_text(drawing: tool.Drawing, obj: bpy.types.Object) -> None: drawing.synchronise_ifc_and_text_attributes(obj) drawing.update_text_size_pset(obj) drawing.update_text_value(obj) drawing.disable_editing_text(obj) -def enable_editing_assigned_product(drawing, obj=None): +def enable_editing_assigned_product(drawing: tool.Drawing, obj: bpy.types.Object) -> None: drawing.enable_editing_assigned_product(obj) drawing.import_assigned_product(obj) -def disable_editing_assigned_product(drawing, obj=None): +def disable_editing_assigned_product(drawing: tool.Drawing, obj: bpy.types.Object) -> None: drawing.disable_editing_assigned_product(obj) -def edit_assigned_product(ifc, drawing, obj=None, product=None): +def edit_assigned_product( + ifc: tool.Ifc, drawing: tool.Drawing, obj: bpy.types.Object, product: Optional[ifcopenshell.entity_instance] = None +) -> None: element = ifc.get_entity(obj) existing_product = drawing.get_assigned_product(element) if existing_product != product: @@ -58,16 +66,16 @@ def edit_assigned_product(ifc, drawing, obj=None, product=None): drawing.disable_editing_assigned_product(obj) -def load_sheets(drawing): +def load_sheets(drawing: tool.Drawing) -> None: drawing.import_sheets() drawing.enable_editing_sheets() -def disable_editing_sheets(drawing): +def disable_editing_sheets(drawing: tool.Drawing) -> None: drawing.disable_editing_sheets() -def add_sheet(ifc, drawing, titleblock: ifcopenshell.entity_instance): +def add_sheet(ifc: tool.Ifc, drawing, titleblock: ifcopenshell.entity_instance) -> None: sheet = ifc.run("document.add_information") layout = ifc.run("document.add_reference", information=sheet) titleblock_reference = ifc.run("document.add_reference", information=sheet) @@ -93,7 +101,7 @@ def add_sheet(ifc, drawing, titleblock: ifcopenshell.entity_instance): drawing.import_sheets() -def regenerate_sheet(drawing, sheet=None): +def regenerate_sheet(drawing: tool.Drawing, sheet: ifcopenshell.entity_instance) -> None: titleblock_uri = drawing.get_document_uri(sheet, "TITLEBLOCK") drawing.create_svg_sheet(sheet, drawing.sanitise_filename(Path(titleblock_uri).stem)) try: @@ -104,11 +112,11 @@ def regenerate_sheet(drawing, sheet=None): drawing.delete_file(path_layout) -def open_sheet(drawing, sheet=None): +def open_sheet(drawing: tool.Drawing, sheet: ifcopenshell.entity_instance) -> None: drawing.open_layout_svg(drawing.get_document_uri(sheet, "LAYOUT")) -def remove_sheet(ifc, drawing, sheet=None): +def remove_sheet(ifc: tool.Ifc, drawing: tool.Drawing, sheet: ifcopenshell.entity_instance) -> None: for reference in drawing.get_document_references(sheet): if drawing.get_reference_description(reference) in ("LAYOUT", "SHEET", "REVISION", "RASTER"): uri = ifc.resolve_uri(drawing.get_document_uri(reference)) @@ -118,7 +126,7 @@ def remove_sheet(ifc, drawing, sheet=None): drawing.import_sheets() -def rename_sheet(ifc, drawing, sheet: ifcopenshell.entity_instance, identification: str, name: str) -> None: +def rename_sheet(ifc: tool.Ifc, drawing, sheet: ifcopenshell.entity_instance, identification: str, name: str) -> None: if ifc.get_schema() == "IFC2X3": attributes = {"DocumentId": identification, "Name": name} else: @@ -144,30 +152,32 @@ def rename_sheet(ifc, drawing, sheet: ifcopenshell.entity_instance, identificati drawing.move_file(old_location, ifc.resolve_uri(new_location)) -def rename_reference(ifc, drawing, reference=None, identification=None): +def rename_reference( + ifc: tool.Ifc, drawing: tool.Drawing, reference: ifcopenshell.entity_instance, identification: str +) -> None: attributes = drawing.generate_reference_attributes(reference, Identification=identification) ifc.run("document.edit_reference", reference=reference, attributes=attributes) -def load_schedules(drawing): +def load_schedules(drawing: tool.Drawing) -> None: drawing.import_documents("SCHEDULE") drawing.enable_editing_schedules() -def load_references(drawing): +def load_references(drawing: tool.Drawing) -> None: drawing.import_documents("REFERENCE") drawing.enable_editing_references() -def disable_editing_schedules(drawing): +def disable_editing_schedules(drawing: tool.Drawing) -> None: drawing.disable_editing_schedules() -def disable_editing_references(drawing): +def disable_editing_references(drawing: tool.Drawing) -> None: drawing.disable_editing_references() -def add_document(ifc, drawing, document_type, uri=None): +def add_document(ifc: tool.Ifc, drawing: tool.Drawing, document_type: tool.Drawing.DOCUMENT_TYPE, uri: str) -> None: document = ifc.run("document.add_information") reference = ifc.run("document.add_reference", information=document) name = drawing.get_path_filename(uri) @@ -180,34 +190,43 @@ def add_document(ifc, drawing, document_type, uri=None): drawing.import_documents(document_type) -def remove_document(ifc, drawing, document_type, document=None): +def remove_document( + ifc: tool.Ifc, + drawing: tool.Drawing, + document_type: tool.Drawing.DOCUMENT_TYPE, + document: ifcopenshell.entity_instance, +) -> None: ifc.run("document.remove_information", information=document) drawing.import_documents(document_type) -def open_schedule(drawing, schedule=None): +def open_schedule(drawing: tool.Drawing, schedule: ifcopenshell.entity_instance) -> None: drawing.open_spreadsheet(drawing.get_document_uri(schedule)) -def open_reference(drawing, reference=None): +def open_reference(drawing: tool.Drawing, reference: ifcopenshell.entity_instance) -> None: drawing.open_svg(drawing.get_document_uri(reference)) -def update_document_name(ifc, drawing, document=None, name=None): +def update_document_name( + ifc: tool.Ifc, drawing: tool.Drawing, document: ifcopenshell.entity_instance, name=None +) -> None: if drawing.get_name(document) != name: ifc.run("document.edit_information", information=document, attributes={"Name": name}) -def load_drawings(drawing): +def load_drawings(drawing: tool.Drawing) -> None: drawing.import_drawings() drawing.enable_editing_drawings() -def disable_editing_drawings(drawing): +def disable_editing_drawings(drawing: tool.Drawing) -> None: drawing.disable_editing_drawings() -def add_drawing(ifc, collector, drawing, target_view=None, location_hint=None): +def add_drawing( + ifc: tool.Ifc, collector: tool.Collector, drawing: tool.Drawing, target_view=None, location_hint=None +) -> None: drawing_name = drawing.ensure_unique_drawing_name(drawing.generate_drawing_name(target_view, location_hint)) drawing_matrix = drawing.generate_drawing_matrix(target_view, location_hint) camera = drawing.create_camera(drawing_name, drawing_matrix, location_hint) @@ -265,7 +284,12 @@ def add_drawing(ifc, collector, drawing, target_view=None, location_hint=None): drawing.import_drawings() -def duplicate_drawing(ifc, drawing_tool, drawing=None, should_duplicate_annotations=False): +def duplicate_drawing( + ifc: tool.Ifc, + drawing_tool: tool.Drawing, + drawing: ifcopenshell.entity_instance, + should_duplicate_annotations: bool = False, +) -> ifcopenshell.entity_instance: drawing_name = drawing_tool.ensure_unique_drawing_name(drawing_tool.get_name(drawing)) new_drawing = ifc.run("root.copy_class", product=drawing) drawing_tool.copy_representation(drawing, new_drawing) @@ -302,7 +326,7 @@ def duplicate_drawing(ifc, drawing_tool, drawing=None, should_duplicate_annotati return new_drawing -def remove_drawing(ifc, drawing_tool, drawing=None): +def remove_drawing(ifc: tool.Ifc, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance) -> None: if drawing_tool.is_active_drawing(drawing): drawing_tool.run_drawing_activate_model() @@ -330,7 +354,9 @@ def remove_drawing(ifc, drawing_tool, drawing=None): drawing_tool.import_drawings() -def update_drawing_name(ifc, drawing_tool, drawing=None, name=None): +def update_drawing_name( + ifc: tool.Ifc, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance, name=None +) -> None: if drawing_tool.get_name(drawing) != name: ifc.run("attribute.edit_attributes", product=drawing, attributes={"Name": name}) group = drawing_tool.get_drawing_group(drawing) @@ -364,7 +390,13 @@ def update_drawing_name(ifc, drawing_tool, drawing=None, name=None): drawing_tool.import_sheets() -def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None): +def add_annotation( + ifc: tool.Ifc, + collector: tool.Collector, + drawing_tool: tool.Drawing, + drawing: ifcopenshell.entity_instance, + object_type: str, +) -> None: target_view = drawing_tool.get_drawing_target_view(drawing) context = drawing_tool.get_annotation_context(target_view, object_type) if not context: @@ -387,12 +419,14 @@ def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None) drawing_tool.enable_editing(obj) -def build_schedule(drawing, schedule=None): +def build_schedule(drawing: tool.Drawing, schedule: ifcopenshell.entity_instance) -> None: drawing.create_svg_schedule(schedule) drawing.open_svg(drawing.get_path_with_ext(drawing.get_document_uri(schedule), "svg")) -def sync_references(ifc, collector, drawing_tool, drawing=None): +def sync_references( + ifc: tool.Ifc, collector: tool.Collector, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance +) -> None: if not drawing_tool.has_annotation(drawing): return @@ -437,11 +471,13 @@ def sync_references(ifc, collector, drawing_tool, drawing=None): drawing_tool.sync_object_representation(reference_obj) -def select_assigned_product(drawing, context): +def select_assigned_product(drawing: tool.Drawing, context: bpy.types.Context) -> None: drawing.select_assigned_product(context) -def activate_drawing_view(ifc, blender, drawing_tool, drawing): +def activate_drawing_view( + ifc: tool.Ifc, blender: tool.Blender, drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance +) -> None: camera = ifc.get_object(drawing) if not camera: camera = drawing_tool.import_drawing(drawing) diff --git a/src/blenderbim/blenderbim/tool/blender.py b/src/blenderbim/blenderbim/tool/blender.py index d5055bf9bf..64fa9bf40f 100644 --- a/src/blenderbim/blenderbim/tool/blender.py +++ b/src/blenderbim/blenderbim/tool/blender.py @@ -468,7 +468,7 @@ class Blender(blenderbim.core.tool.Blender): active_object.select_set(True) @classmethod - def enum_property_has_valid_index(cls, props, prop_name: str, enum_items: tuple) -> bool: + def enum_property_has_valid_index(cls, props: bpy.types.PropertyGroup, prop_name: str, enum_items: tuple) -> bool: """method created for readibility and to avoid console warnings like `pyrna_enum_to_py: current value '17' matches no enum in 'BIMModelProperties', '', 'relating_type_id'` """ diff --git a/src/blenderbim/blenderbim/tool/cost.py b/src/blenderbim/blenderbim/tool/cost.py index 01a392e727..2ce904b018 100644 --- a/src/blenderbim/blenderbim/tool/cost.py +++ b/src/blenderbim/blenderbim/tool/cost.py @@ -1,11 +1,13 @@ import os import bpy +import blenderbim.core.tool import blenderbim.tool as tool import ifcopenshell.util.date import ifcopenshell.util.cost import ifcopenshell.util.unit import blenderbim.bim.helper import json +from typing import Optional class Cost(blenderbim.core.tool.Cost): @@ -163,7 +165,7 @@ class Cost(blenderbim.core.tool.Cost): return @classmethod - def load_cost_item_types(cls, cost_item=None): + def load_cost_item_types(cls, cost_item: Optional[ifcopenshell.entity_instance] = None) -> None: if not cost_item: cost_item = cls.get_highlighted_cost_item() if not cost_item: diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py index a223186670..d2a8f96aca 100644 --- a/src/blenderbim/blenderbim/tool/drawing.py +++ b/src/blenderbim/blenderbim/tool/drawing.py @@ -51,11 +51,14 @@ from blenderbim.bim.module.drawing.prop import get_diagram_scales, BOX_ALIGNMENT from lxml import etree from mathutils import Vector, Matrix from fractions import Fraction -from typing import Optional, Union, Iterable, Any +from typing import Optional, Union, Iterable, Any, Literal from pathlib import Path class Drawing(blenderbim.core.tool.Drawing): + ANNOTATION_DATA_TYPE = Literal["empty", "curve", "mesh"] + DOCUMENT_TYPE = Literal["SCHEDULE", "REFERENCE"] + @classmethod def canonicalise_class_name(cls, name): return re.sub("[^0-9a-zA-Z]+", "", name) @@ -68,11 +71,11 @@ class Drawing(blenderbim.core.tool.Drawing): ) @classmethod - def get_annotation_data_type(cls, object_type): + def get_annotation_data_type(cls, object_type: str) -> ANNOTATION_DATA_TYPE: return ANNOTATION_TYPES_DATA[object_type][3] @classmethod - def create_annotation_object(cls, drawing, object_type): + def create_annotation_object(cls, drawing: ifcopenshell.entity_instance, object_type: str) -> bpy.types.Object: data_type = cls.get_annotation_data_type(object_type) obj = annotation.Annotator.get_annotation_obj(drawing, object_type, data_type) if object_type == "FILL_AREA": @@ -298,15 +301,15 @@ class Drawing(blenderbim.core.tool.Drawing): bpy.context.scene.DocProperties.is_editing_sheets = False @classmethod - def disable_editing_text(cls, obj): + def disable_editing_text(cls, obj: bpy.types.Object) -> None: obj.BIMTextProperties.is_editing = False @classmethod - def disable_editing_assigned_product(cls, obj): + def disable_editing_assigned_product(cls, obj: bpy.types.Object) -> None: obj.BIMAssignedProductProperties.is_editing_product = False @classmethod - def enable_editing(cls, obj): + def enable_editing(cls, obj: bpy.types.Object) -> None: bpy.ops.object.select_all(action="DESELECT") bpy.context.view_layer.objects.active = obj obj.select_set(True) @@ -330,11 +333,11 @@ class Drawing(blenderbim.core.tool.Drawing): bpy.context.scene.DocProperties.is_editing_sheets = True @classmethod - def enable_editing_text(cls, obj): + def enable_editing_text(cls, obj: bpy.types.Object) -> None: obj.BIMTextProperties.is_editing = True @classmethod - def enable_editing_assigned_product(cls, obj): + def enable_editing_assigned_product(cls, obj: bpy.types.Object) -> None: obj.BIMAssignedProductProperties.is_editing_product = True @classmethod @@ -428,7 +431,7 @@ class Drawing(blenderbim.core.tool.Drawing): return location @classmethod - def get_path_filename(cls, path): + def get_path_filename(cls, path: str) -> str: return os.path.splitext(os.path.basename(path))[0] @classmethod @@ -483,7 +486,7 @@ class Drawing(blenderbim.core.tool.Drawing): return "" @classmethod - def get_name(cls, element): + def get_name(cls, element: ifcopenshell.entity_instance) -> Union[str, None]: return element.Name @classmethod @@ -563,7 +566,7 @@ class Drawing(blenderbim.core.tool.Drawing): ifcopenshell.util.element.remove_deep2(ifc_file, literal) @classmethod - def synchronise_ifc_and_text_attributes(cls, obj): + def synchronise_ifc_and_text_attributes(cls, obj: bpy.types.Object) -> None: literals = cls.get_text_literal(obj, return_list=True) literals_attributes = cls.export_text_literal_attributes(obj) defined_ifc_ids = [l.ifc_definition_id for l in obj.BIMTextProperties.literals] @@ -784,7 +787,7 @@ class Drawing(blenderbim.core.tool.Drawing): new.ifc_definition_id = drawing.id() # Last, to prevent unnecessary prop callbacks @classmethod - def import_documents(cls, document_type): + def import_documents(cls, document_type: DOCUMENT_TYPE) -> None: dprops = bpy.context.scene.DocProperties if document_type == "SCHEDULE": documents_collection = dprops.schedules @@ -845,7 +848,7 @@ class Drawing(blenderbim.core.tool.Drawing): return next(s for s in props.sheets[: props.active_sheet_index + 1][::-1] if s.is_sheet) @classmethod - def import_text_attributes(cls, obj): + def import_text_attributes(cls, obj: bpy.types.Object) -> None: props = obj.BIMTextProperties props.literals.clear() @@ -863,7 +866,7 @@ class Drawing(blenderbim.core.tool.Drawing): props.font_size = str(text_data["FontSize"]) @classmethod - def import_assigned_product(cls, obj): + def import_assigned_product(cls, obj: bpy.types.Object) -> None: element = tool.Ifc.get_entity(obj) product = cls.get_assigned_product(element) if product: @@ -934,7 +937,7 @@ class Drawing(blenderbim.core.tool.Drawing): bpy.context.scene.DocProperties.should_draw_decorations = True @classmethod - def update_text_value(cls, obj): + def update_text_value(cls, obj: bpy.types.Object) -> None: props = obj.BIMTextProperties literals = cls.get_text_literal(obj, return_list=True) cls.import_text_attributes(obj) @@ -943,7 +946,7 @@ class Drawing(blenderbim.core.tool.Drawing): props.literals[i].value = cls.replace_text_literal_variables(literal.Literal, product) @classmethod - def update_text_size_pset(cls, obj): + def update_text_size_pset(cls, obj: bpy.types.Object) -> None: """updates pset `EPset_Annotation.Classes` value based on current font size from `obj.BIMTextProperties.font_size` """ @@ -1522,7 +1525,7 @@ class Drawing(blenderbim.core.tool.Drawing): tool.Geometry.record_object_position(obj) @classmethod - def get_document_references(cls, document): + def get_document_references(cls, document: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: if tool.Ifc.get_schema() == "IFC2X3": return document.DocumentReferences or [] return document.HasDocumentReferences or [] @@ -1553,7 +1556,9 @@ class Drawing(blenderbim.core.tool.Drawing): return reference.Description @classmethod - def generate_reference_attributes(cls, reference: ifcopenshell.entity_instance, **attributes: Any) -> dict[str, Any]: + def generate_reference_attributes( + cls, reference: ifcopenshell.entity_instance, **attributes: Any + ) -> dict[str, Any]: """will automatically convert attributes below for IFC2X3 compatibility: - Identification -> ItemReference @@ -1635,23 +1640,22 @@ class Drawing(blenderbim.core.tool.Drawing): base_elements = set(ifc_file.by_type("IfcElement") + ifc_file.by_type("IfcSpatialElement")) elements = {e for e in (elements & base_elements) if e.is_a() != "IfcSpace"} - updated_set = set() for i in elements: # exclude annotations to avoid including annotations from other drawings - if not i.is_a("IfcAnnotation"): + if not i.is_a("IfcAnnotation"): updated_set.add(i) - #add aggregate too, if element is host by one + # add aggregate too, if element is host by one if i.Decomposes: aggregate = i.Decomposes[0].RelatingObject - #remove IfcProject for class iterator. See https://github.com/IfcOpenShell/IfcOpenShell/issues/4361#issuecomment-2081223615 - if not aggregate.is_a("IfcProject"): + # remove IfcProject for class iterator. See https://github.com/IfcOpenShell/IfcOpenShell/issues/4361#issuecomment-2081223615 + if not aggregate.is_a("IfcProject"): updated_set.add(aggregate) - # After the iteration is complete, update elements with updated set + # After the iteration is complete, update elements with updated set elements.update(updated_set) - + # add annotations from the current drawing annotations = tool.Drawing.get_group_elements(tool.Drawing.get_drawing_group(drawing)) elements.update(annotations) @@ -1694,7 +1698,7 @@ class Drawing(blenderbim.core.tool.Drawing): return reference.ReferencedDocument @classmethod - def select_assigned_product(cls, context): + def select_assigned_product(cls, context: bpy.types.Context) -> None: obj = context.active_object element = tool.Ifc.get_entity(obj) product = cls.get_assigned_product(element) @@ -1713,7 +1717,7 @@ class Drawing(blenderbim.core.tool.Drawing): return True if (camera and camera.data.type == "ORTHO") else False @classmethod - def is_active_drawing(cls, drawing): + def is_active_drawing(cls, drawing: ifcopenshell.entity_instance) -> bool: return drawing.id() == bpy.context.scene.DocProperties.active_drawing_id @classmethod diff --git a/src/blenderbim/blenderbim/tool/geometry.py b/src/blenderbim/blenderbim/tool/geometry.py index 2d81ba415a..8031433e21 100644 --- a/src/blenderbim/blenderbim/tool/geometry.py +++ b/src/blenderbim/blenderbim/tool/geometry.py @@ -90,7 +90,7 @@ class Geometry(blenderbim.core.tool.Geometry): bpy.data.meshes.remove(data) @classmethod - def delete_ifc_object(cls, obj): + def delete_ifc_object(cls, obj: bpy.types.Object) -> None: element = tool.Ifc.get_entity(obj) if not element: return diff --git a/src/blenderbim/blenderbim/tool/model.py b/src/blenderbim/blenderbim/tool/model.py index 4025d37d39..d0c63912d4 100644 --- a/src/blenderbim/blenderbim/tool/model.py +++ b/src/blenderbim/blenderbim/tool/model.py @@ -23,6 +23,7 @@ import collections import collections.abc import numpy as np import ifcopenshell +import ifcopenshell.util.element import ifcopenshell.util.unit import ifcopenshell.util.placement import ifcopenshell.util.representation @@ -468,7 +469,7 @@ class Model(blenderbim.core.tool.Model): has_deleted_opening = True @classmethod - def get_material_layer_parameters(cls, element): + def get_material_layer_parameters(cls, element: ifcopenshell.entity_instance) -> dict[str, Any]: unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) layer_set_direction = "AXIS2" offset = 0.0 diff --git a/src/blenderbim/blenderbim/tool/spatial.py b/src/blenderbim/blenderbim/tool/spatial.py index 68c1d8c0e8..101d17e454 100644 --- a/src/blenderbim/blenderbim/tool/spatial.py +++ b/src/blenderbim/blenderbim/tool/spatial.py @@ -31,6 +31,7 @@ import json from math import pi from mathutils import Vector, Matrix from shapely import Polygon, MultiPolygon +from typing import Generator class Spatial(blenderbim.core.tool.Spatial): @@ -187,14 +188,14 @@ class Spatial(blenderbim.core.tool.Spatial): ] @classmethod - def get_selected_products(cls): + def get_selected_products(cls) -> Generator[ifcopenshell.entity_instance, None, None]: for obj in bpy.context.selected_objects: entity = tool.Ifc.get_entity(obj) if entity and entity.is_a("IfcProduct"): yield entity @classmethod - def get_selected_product_types(cls): + def get_selected_product_types(cls) -> Generator[ifcopenshell.entity_instance, None, None]: for obj in bpy.context.selected_objects: entity = tool.Ifc.get_entity(obj) if entity and entity.is_a("IfcTypeProduct"): diff --git a/src/ifcopenshell-python/ifcopenshell/util/cost.py b/src/ifcopenshell-python/ifcopenshell/util/cost.py index 4d15ee8f33..44852b6e9c 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/cost.py +++ b/src/ifcopenshell-python/ifcopenshell/util/cost.py @@ -17,13 +17,15 @@ # along with IfcOpenShell. If not, see . import lark +import ifcopenshell +from typing import Optional, Union arithmetic_operator_symbols = {"ADD": "+", "DIVIDE": "/", "MULTIPLY": "*", "SUBTRACT": "-"} symbol_arithmetic_operators = {"+": "ADD", "/": "DIVIDE", "*": "MULTIPLY", "-": "SUBTRACT"} -def get_primitive_applied_value(applied_value): +def get_primitive_applied_value(applied_value: Union[ifcopenshell.entity_instance, float, None]) -> float: if not applied_value: return 0.0 elif isinstance(applied_value, float): @@ -32,17 +34,21 @@ def get_primitive_applied_value(applied_value): return applied_value.wrappedValue elif applied_value.is_a("IfcMeasureWithUnit"): return applied_value.ValueComponent - assert False, "Applied value {applied_value} not implemented" + assert False, f"Applied value {applied_value} not implemented" -def get_total_quantity(root_element): +def get_total_quantity(root_element: ifcopenshell.entity_instance) -> Union[float, None]: + # 3 IfcPhysicalQuantity Value if root_element.is_a("IfcCostItem"): return sum([q[3] for q in root_element.CostQuantities or []]) or None elif root_element.is_a("IfcConstructionResource"): - return root_element.BaseQuantity[3] if root_element.BaseQuantity else 1.0 + quantity = root_element.BaseQuantity + return quantity[3] if quantity else 1.0 -def calculate_applied_value(root_element, cost_value, category_filter=None): +def calculate_applied_value( + root_element: ifcopenshell.entity_instance, cost_value: ifcopenshell.entity_instance, category_filter=None +) -> float: if cost_value.ArithmeticOperator and cost_value.Components: component_values = [] for component in cost_value.Components: @@ -75,11 +81,11 @@ def calculate_applied_value(root_element, cost_value, category_filter=None): return sum_child_root_elements(root_element, category_filter=cost_value.Category) else: return get_primitive_applied_value(cost_value.AppliedValue) - return 0 + return 0.0 -def sum_child_root_elements(root_element, category_filter=None): - result = 0 +def sum_child_root_elements(root_element: ifcopenshell.entity_instance, category_filter: Optional[str] = None) -> float: + result = 0.0 for rel in root_element.IsNestedBy: for child_root_element in rel.RelatedObjects: if root_element.is_a("IfcCostItem"): @@ -99,14 +105,14 @@ def sum_child_root_elements(root_element, category_filter=None): return result -def serialise_cost_value(cost_value): +def serialise_cost_value(cost_value: ifcopenshell.entity_instance) -> str: result = _serialise_cost_value(cost_value) if result and result[0] == "(" and result[-1] == ")": return result[1:-1] return result -def _serialise_cost_value(cost_value): +def _serialise_cost_value(cost_value: ifcopenshell.entity_instance) -> str: value = "" if cost_value.ArithmeticOperator and cost_value.Components: operator = arithmetic_operator_symbols[cost_value.ArithmeticOperator] @@ -133,7 +139,7 @@ def _serialise_cost_value(cost_value): return value -def serialise_applied_value(applied_value): +def serialise_applied_value(applied_value: ifcopenshell.entity_instance) -> str: if applied_value.is_a("IfcMonetaryMeasure"): return str(applied_value.wrappedValue) return "?"