diff --git a/src/bonsai/bonsai/bim/module/attribute/data.py b/src/bonsai/bonsai/bim/module/attribute/data.py index 1f28c314fa..a4d3d109e0 100644 --- a/src/bonsai/bonsai/bim/module/attribute/data.py +++ b/src/bonsai/bonsai/bim/module/attribute/data.py @@ -38,8 +38,9 @@ class AttributesData: def attributes(cls): results = [] element = tool.Ifc.get_entity(bpy.context.active_object) + assert element data = element.get_info() - if hasattr(element, "GlobalId"): + if "GlobalId" in data: excluded_keys = ["id", "type"] else: excluded_keys = ["type"] diff --git a/src/bonsai/bonsai/bim/module/attribute/operator.py b/src/bonsai/bonsai/bim/module/attribute/operator.py index e04addce81..15520e57bf 100644 --- a/src/bonsai/bonsai/bim/module/attribute/operator.py +++ b/src/bonsai/bonsai/bim/module/attribute/operator.py @@ -41,6 +41,7 @@ class EnableEditingAttributes(bpy.types.Operator): props.attributes.clear() element = tool.Ifc.get_entity(obj) + assert element has_inherited_predefined_type = False if not element.is_a("IfcTypeObject") and (element_type := ifcopenshell.util.element.get_type(element)): # Allow for None due to https://github.com/buildingSMART/IFC4.3.x-development/issues/818 diff --git a/src/ifc5d/ifc5d/qto.py b/src/ifc5d/ifc5d/qto.py index 9367e45c4e..c9681e356f 100644 --- a/src/ifc5d/ifc5d/qto.py +++ b/src/ifc5d/ifc5d/qto.py @@ -17,10 +17,12 @@ # along with Ifc5D. If not, see . import os +import types import json import ifcopenshell import ifcopenshell.api import ifcopenshell.api.pset +import ifcopenshell.geom import ifcopenshell.util.unit import ifcopenshell.util.element import ifcopenshell.util.selector @@ -35,6 +37,7 @@ Function = namedtuple("Function", ["measure", "name", "description"]) RULE_SET = Literal["IFC4QtoBaseQuantities", "IFC4QtoBaseQuantitiesBlender"] rules: dict[RULE_SET, dict[str, Any]] = {} ResultsDict = dict[ifcopenshell.entity_instance, dict[str, dict[str, float]]] +QtosFormulas = dict[str, dict[str, str]] cwd = os.path.dirname(os.path.realpath(__file__)) for name in get_args(RULE_SET): @@ -91,7 +94,7 @@ class SI2ProjectUnitConverter: "IfcVolumeMeasure": "CUBIC_METRE", } - def convert(self, value, measure): + def convert(self, value: float, measure: str) -> float: if measure_unit := self.project_units.get(measure, None): return ifcopenshell.util.unit.convert(value, None, self.si_names[measure], *measure_unit) return value @@ -101,6 +104,7 @@ class IfcOpenShell: """Calculates Model body context geometry using the default IfcOpenShell iterator on triangulation elements.""" + # Implementations are located in ifcopenshell.util.shape. raw_functions = { # IfcLengthMeasure "get_x": Function("IfcLengthMeasure", "X", "Calculates the length along the local X axis"), @@ -158,19 +162,15 @@ class IfcOpenShell: qtos: dict[str, dict[str, Union[str, None]]], results: ResultsDict, ) -> None: - import ifcopenshell - import ifcopenshell.geom - import ifcopenshell.util.shape - - formula_functions = {} + formula_functions: dict[str, types.FunctionType] = {} cls.gross_settings = ifcopenshell.geom.settings() cls.gross_settings.set("disable-opening-subtractions", True) cls.net_settings = ifcopenshell.geom.settings() cls.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) - gross_qtos = {} - net_qtos = {} + gross_qtos: QtosFormulas = {} + net_qtos: QtosFormulas = {} for name, quantities in qtos.items(): for quantity, formula in quantities.items(): @@ -179,16 +179,12 @@ class IfcOpenShell: gross_or_net_qtos = gross_qtos if formula.startswith("gross_") else net_qtos if formula.endswith("get_segment_length"): gross_or_net_qtos.setdefault(name, {})[quantity] = formula.partition("_")[2] - elif formula.startswith("gross_"): - formula = formula.partition("_")[2] - gross_or_net_qtos.setdefault(name, {})[quantity] = formula - formula_functions[formula] = getattr(ifcopenshell.util.shape, formula) - elif formula.startswith("net_"): + elif formula.startswith(("gross_", "net_")): formula = formula.partition("_")[2] gross_or_net_qtos.setdefault(name, {})[quantity] = formula formula_functions[formula] = getattr(ifcopenshell.util.shape, formula) - tasks = [] + tasks: list[tuple[ifcopenshell.geom.iterator, QtosFormulas]] = [] if gross_qtos: tasks.append((IfcOpenShell.create_iterator(ifc_file, cls.gross_settings, list(elements)), gross_qtos)) @@ -198,13 +194,13 @@ class IfcOpenShell: cls.unit_converter = SI2ProjectUnitConverter(ifc_file) - for iterator, qtos in tasks: + for iterator, qtos_ in tasks: if iterator.initialize(): while True: shape = iterator.get() element = ifc_file.by_id(shape.id) results.setdefault(element, {}) - for name, quantities in qtos.items(): + for name, quantities in qtos_.items(): results[element].setdefault(name, {}) for quantity, formula in quantities.items(): if formula == "get_segment_length": diff --git a/src/ifcopenshell-python/ifcopenshell/entity_instance.py b/src/ifcopenshell-python/ifcopenshell/entity_instance.py index a788c595f1..dcd3b74cb0 100644 --- a/src/ifcopenshell-python/ifcopenshell/entity_instance.py +++ b/src/ifcopenshell-python/ifcopenshell/entity_instance.py @@ -25,7 +25,7 @@ import operator import subprocess import sys import time -from typing import Union, Any, Callable, TypeVar, overload, Iterable +from typing import Union, Any, Callable, TypeVar, overload, Iterable, Sequence from . import ifcopenshell_wrapper from . import settings @@ -551,7 +551,7 @@ class entity_instance: include_identifier: bool = True, recursive: bool = False, return_type: Union[type[dict], type] = dict, - ignore: Iterable[str] = (), + ignore: Sequence[str] = (), scalar_only: bool = False, ) -> dict[str, Any]: """Return a dictionary of the entity_instance's properties (Python and IFC) and their values. diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index d1e8108614..08f443c6b9 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -28,7 +28,7 @@ from ..entity_instance import entity_instance from . import has_occ -from typing import TypeVar, Union, Optional, Generator, Any, Literal, overload, TYPE_CHECKING, Iterable +from typing import TypeVar, Union, Optional, Generator, Any, Literal, overload, TYPE_CHECKING, Iterable, cast if TYPE_CHECKING: from OCC.Core import TopoDS @@ -262,6 +262,8 @@ class iterator(ifcopenshell_wrapper.Iterator): include_or_exclude_type = set(x.__class__.__name__ for x in include_or_exclude) if include_or_exclude_type == {"entity_instance"}: + include_or_exclude = cast(set[entity_instance], include_or_exclude) + if not all(inst.is_a("IfcProduct") for inst in include_or_exclude): raise ValueError("include and exclude need to be an aggregate of IfcProduct")