diff --git a/src/bonsai/bonsai/bim/module/bcf/operator.py b/src/bonsai/bonsai/bim/module/bcf/operator.py
index ed2859ac8b..b208a7e5fb 100644
--- a/src/bonsai/bonsai/bim/module/bcf/operator.py
+++ b/src/bonsai/bonsai/bim/module/bcf/operator.py
@@ -1208,9 +1208,11 @@ class ActivateBcfViewpoint(bpy.types.Operator):
self.file = tool.Ifc.get()
bcfxml = bcfstore.BcfStore.get_bcfxml()
assert bcfxml
+ assert context.scene
props = tool.Bcf.get_bcf_props()
blender_topic = props.active_topic
+ assert blender_topic
topic = bcfxml.topics[blender_topic.name]
if self.viewpoint_guid:
viewpoint_guid = self.viewpoint_guid
@@ -1234,6 +1236,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
cam_height = context.scene.render.resolution_y
cam_aspect = cam_width / cam_height
+ assert isinstance(obj.data, bpy.types.Camera)
obj.data.background_images.clear()
if viewpoint.snapshot:
obj.data.show_background_images = True
diff --git a/src/bonsai/bonsai/bim/module/material/ui.py b/src/bonsai/bonsai/bim/module/material/ui.py
index 99efecce65..019ee57a94 100644
--- a/src/bonsai/bonsai/bim/module/material/ui.py
+++ b/src/bonsai/bonsai/bim/module/material/ui.py
@@ -48,7 +48,7 @@ class BIM_PT_materials(Panel):
MaterialsData.load()
self.props = tool.Material.get_material_props()
- material = tool.Material.get_active_material_item()
+ material = self.props.active_material
material_id = material.ifc_definition_id if material else None
row = self.layout.row(align=True)
diff --git a/src/bonsai/bonsai/bim/module/model/polyline.py b/src/bonsai/bonsai/bim/module/model/polyline.py
index cacb288a89..4acd27dd07 100644
--- a/src/bonsai/bonsai/bim/module/model/polyline.py
+++ b/src/bonsai/bonsai/bim/module/model/polyline.py
@@ -371,7 +371,9 @@ def get_vertical_profile_preview_data(
return data
-def get_horizontal_profile_preview_data(context, relating_type):
+def get_horizontal_profile_preview_data(
+ context: bpy.types.Context, relating_type: ifcopenshell.entity_instance
+) -> dict[str, Any]:
material = ifcopenshell.util.element.get_material(relating_type)
try:
profile_curve = material.MaterialProfiles[0].Profile
@@ -385,7 +387,7 @@ def get_horizontal_profile_preview_data(context, relating_type):
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else []
if len(polyline_points) < 2:
- return
+ return {}
for point in polyline_points:
polyline_verts.append(Vector((point.x, point.y, point.z)))
polyline_edges = [(i, i + 1) for i in range(len(polyline_verts) - 1)]
@@ -433,7 +435,7 @@ def get_horizontal_profile_preview_data(context, relating_type):
case "9":
grouped_verts = [(v[0] + x_offset, v[1] - y_offset, v[2]) for v in grouped_verts]
- data = {}
+ data: dict[str, Any] = {}
data["verts"] = []
data["edges"] = []
data["tris"] = []
diff --git a/src/bonsai/bonsai/bim/module/pset/prop.py b/src/bonsai/bonsai/bim/module/pset/prop.py
index 1ecdd8c378..d5cf85791d 100644
--- a/src/bonsai/bonsai/bim/module/pset/prop.py
+++ b/src/bonsai/bonsai/bim/module/pset/prop.py
@@ -115,7 +115,7 @@ def get_material_pset_names(self: "PsetProperties", context: object) -> tool.Ble
return MaterialPsetsData.data["pset_name"]
-def get_material_set_pset_names(self: "PsetProperties", context: object):
+def get_material_set_pset_names(self: "PsetProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS:
global psetnames
if not ObjectMaterialData.is_loaded:
ObjectMaterialData.load()
@@ -128,12 +128,14 @@ def get_material_set_pset_names(self: "PsetProperties", context: object):
return psetnames[ifc_class]
-def get_material_set_item_pset_names(self: "PsetProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS:
+def get_material_set_item_pset_names(
+ self: "PsetProperties", context: bpy.types.Context
+) -> tool.Blender.BLENDER_ENUM_ITEMS:
global psetnames
obj = context.active_object
assert obj
omprops = tool.Material.get_object_material_props(obj)
- if not omprops.active_material_set_item_id:
+ if not (ifc_definition_id := omprops.active_material_set_item_id):
return []
ifc_class = tool.Ifc.get().by_id(ifc_definition_id).is_a()
if ifc_class not in psetnames:
@@ -151,7 +153,7 @@ def get_task_qto_names(self: "PsetProperties", context: object) -> tool.Blender.
return qtonames[ifc_class]
-def get_resource_pset_names(self: "PsetProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS:
+def get_resource_pset_names(self: "PsetProperties", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS:
global psetnames
rprops = context.scene.BIMResourceProperties
rtprops = context.scene.BIMResourceTreeProperties
@@ -162,7 +164,7 @@ def get_resource_pset_names(self: "PsetProperties", context: object) -> tool.Ble
return psetnames[ifc_class]
-def get_resource_qto_names(self: "PsetProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS:
+def get_resource_qto_names(self: "PsetProperties", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS:
global qtonames
rprops = context.scene.BIMResourceProperties
rtprops = context.scene.BIMResourceTreeProperties
@@ -223,7 +225,9 @@ def get_qto_name(self: "PsetProperties", context: bpy.types.Context) -> tool.Ble
results = get_resource_qto_names(self, context)
elif prop_type == "GroupPsetProperties":
results = get_group_qto_names(self, context)
- return [("BBIM_CUSTOM", "Custom Qto", "Create a quantity set without using a template."), None] + results
+ else:
+ assert False
+ return [("BBIM_CUSTOM", "Custom Qto", "Create a quantity set without using a template."), None] + list(results)
def get_object_qto_name(self: "PsetProperties", context: object) -> tool.Blender.BLENDER_ENUM_ITEMS:
diff --git a/src/bonsai/bonsai/bim/module/pset/ui.py b/src/bonsai/bonsai/bim/module/pset/ui.py
index bfe19909eb..bb571a842e 100644
--- a/src/bonsai/bonsai/bim/module/pset/ui.py
+++ b/src/bonsai/bonsai/bim/module/pset/ui.py
@@ -484,7 +484,7 @@ class BIM_PT_material_psets(Panel):
elif ifc_definition_id != MaterialPsetsData.data["ifc_definition_id"]:
MaterialPsetsData.load()
- props = context.scene.MaterialPsetProperties
+ props = tool.Pset.get_pset_props("", "Material")
row = self.layout.row(align=True)
prop_with_search(row, props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
@@ -524,6 +524,7 @@ class BIM_PT_material_set_item_psets(Panel):
if not MaterialSetItemPsetsData.is_loaded:
MaterialSetItemPsetsData.load()
+ assert self.layout
obj = context.active_object
assert obj
omprops = tool.Material.get_object_material_props(obj)
@@ -531,7 +532,7 @@ class BIM_PT_material_set_item_psets(Panel):
self.layout.label(text="No Material Set Item Edited.")
return
- props = obj.MaterialSetItemPsetProperties
+ props = tool.Pset.get_pset_props(obj.name, "MaterialSetItem")
row = self.layout.row(align=True)
prop_with_search(row, props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
@@ -570,7 +571,8 @@ class BIM_PT_task_qtos(Panel):
if not TaskQtosData.is_loaded:
TaskQtosData.load()
- props = context.scene.TaskPsetProperties
+ assert self.layout
+ props = tool.Pset.get_pset_props("", "Task")
row = self.layout.row(align=True)
row.prop(props, "qto_name", text="")
op = row.operator("bim.add_qto", icon="ADD", text="")
@@ -604,7 +606,8 @@ class BIM_PT_resource_qtos(Panel):
if not ResourceQtosData.is_loaded:
ResourceQtosData.load()
- props = context.scene.ResourcePsetProperties
+ assert self.layout
+ props = tool.Pset.get_pset_props("", "Resource")
row = self.layout.row(align=True)
row.prop(props, "qto_name", text="")
op = row.operator("bim.add_qto", icon="ADD", text="")
@@ -638,7 +641,8 @@ class BIM_PT_resource_psets(Panel):
if not ResourcePsetsData.is_loaded:
ResourcePsetsData.load()
- props = context.scene.ResourcePsetProperties
+ assert self.layout
+ props = tool.Pset.get_pset_props("", "Resource")
row = self.layout.row(align=True)
prop_with_search(row, props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
@@ -672,7 +676,8 @@ class BIM_PT_group_qtos(Panel):
if not GroupQtosData.is_loaded:
GroupQtosData.load()
- props = context.scene.GroupPsetProperties
+ assert self.layout
+ props = tool.Pset.get_pset_props("", "Group")
row = self.layout.row(align=True)
row.prop(props, "qto_name", text="")
op = row.operator("bim.add_qto", icon="ADD", text="")
@@ -706,7 +711,8 @@ class BIM_PT_group_psets(Panel):
if not GroupPsetData.is_loaded:
GroupPsetData.load()
- props = context.scene.GroupPsetProperties
+ assert self.layout
+ props = tool.Pset.get_pset_props("", "Group")
row = self.layout.row(align=True)
prop_with_search(row, props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
@@ -750,7 +756,8 @@ class BIM_PT_profile_psets(Panel):
):
ProfilePsetsData.load()
- props = context.scene.ProfilePsetProperties
+ assert self.layout
+ props = tool.Pset.get_pset_props("", "Profile")
row = self.layout.row(align=True)
prop_with_search(row, props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
@@ -782,7 +789,8 @@ class BIM_PT_work_schedule_psets(Panel):
if not WorkSchedulePsetsData.is_loaded:
WorkSchedulePsetsData.load()
- props = context.scene.WorkSchedulePsetProperties
+ assert self.layout
+ props = tool.Pset.get_pset_props("", "WorkSchedule")
row = self.layout.row(align=True)
prop_with_search(row, props, "pset_name", text="")
op = row.operator("bim.add_pset", icon="ADD", text="")
diff --git a/src/bonsai/bonsai/bim/module/search/prop.py b/src/bonsai/bonsai/bim/module/search/prop.py
index 66fda03bc3..110d015b01 100644
--- a/src/bonsai/bonsai/bim/module/search/prop.py
+++ b/src/bonsai/bonsai/bim/module/search/prop.py
@@ -33,7 +33,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Literal
def get_element_key(self: "BIMSearchProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
diff --git a/src/bonsai/bonsai/core/tool.py b/src/bonsai/bonsai/core/tool.py
index d4eaf402a8..4ab7e9d9ab 100644
--- a/src/bonsai/bonsai/core/tool.py
+++ b/src/bonsai/bonsai/core/tool.py
@@ -543,7 +543,6 @@ class Material:
def ensure_material_assigned(cls, elements, material_type, material): pass
def ensure_material_unassigned(cls, elements): pass
def ensure_new_material_set_is_valid(cls, material): pass
- def get_active_material_item(cls): pass
def get_active_material_type(cls): pass
def get_default_material(cls): pass
def get_elements_by_material(cls, material): pass
diff --git a/src/bonsai/bonsai/tool/debug.py b/src/bonsai/bonsai/tool/debug.py
index 1999f84829..7b25c33401 100644
--- a/src/bonsai/bonsai/tool/debug.py
+++ b/src/bonsai/bonsai/tool/debug.py
@@ -19,6 +19,7 @@
from __future__ import annotations
import os
import json
+import bmesh
import bpy
import ifcopenshell.ifcopenshell_wrapper as W
import ifcopenshell.api.material
@@ -70,6 +71,7 @@ class Debug(bonsai.core.tool.Debug):
mesh = bpy.data.meshes.new("Debug")
bm.to_mesh(mesh)
obj = bpy.data.objects.new(name, mesh)
+ assert bpy.context.scene
bpy.context.scene.collection.objects.link(obj)
return obj
@@ -80,6 +82,7 @@ class Debug(bonsai.core.tool.Debug):
mesh = bpy.data.meshes.new("Debug")
mesh.from_pydata(verts, edges, [])
obj = bpy.data.objects.new(name, mesh)
+ assert bpy.context.scene
bpy.context.scene.collection.objects.link(obj)
return obj
diff --git a/src/bonsai/bonsai/tool/material.py b/src/bonsai/bonsai/tool/material.py
index 2f2c916299..5e8b531c5c 100644
--- a/src/bonsai/bonsai/tool/material.py
+++ b/src/bonsai/bonsai/tool/material.py
@@ -76,14 +76,6 @@ class Material(bonsai.core.tool.Material):
def get_elements_by_material(cls, material: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
return ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
- @classmethod
- def get_active_material_item(cls) -> Union[MaterialItem, None]:
- """Get active material props item if index is valid, otherwise, return None."""
- props = tool.Material.get_material_props()
- if 0 <= props.active_material_index < len(props.materials):
- return props.materials[props.active_material_index]
- return None
-
@classmethod
def get_material_category(cls, material: ifcopenshell.entity_instance) -> str:
# IfcMaterial has Category since IFC4.
@@ -95,7 +87,7 @@ class Material(bonsai.core.tool.Material):
# Store active category name to reselect it later.
# Occurs when we expand/contract all categories.
- active_item = cls.get_active_material_item()
+ active_item = props.active_material
previously_selected_category = None
if active_item and active_item.is_category:
previously_selected_category = active_item.name
diff --git a/src/bonsai/bonsai/tool/pset.py b/src/bonsai/bonsai/tool/pset.py
index 3390c02c4c..f8e9857aa1 100644
--- a/src/bonsai/bonsai/tool/pset.py
+++ b/src/bonsai/bonsai/tool/pset.py
@@ -75,11 +75,11 @@ class Pset(bonsai.core.tool.Pset):
@classmethod
def get_pset_props(cls, obj: str, obj_type: tool.Ifc.OBJECT_TYPE) -> PsetProperties:
if obj_type == "Object":
- return bpy.data.objects.get(obj).PsetProperties
+ return bpy.data.objects[obj].PsetProperties
elif obj_type == "Material":
return bpy.context.scene.MaterialPsetProperties
elif obj_type == "MaterialSetItem":
- return bpy.data.objects.get(obj).MaterialSetItemPsetProperties
+ return bpy.data.objects[obj].MaterialSetItemPsetProperties
elif obj_type == "Task":
return bpy.context.scene.TaskPsetProperties
elif obj_type == "Resource":
diff --git a/src/bonsai/bonsai/tool/resource.py b/src/bonsai/bonsai/tool/resource.py
index 80a3cbe6b2..65448440f6 100644
--- a/src/bonsai/bonsai/tool/resource.py
+++ b/src/bonsai/bonsai/tool/resource.py
@@ -33,9 +33,10 @@ import ifcopenshell.util.date as ifcdateutils
import ifcopenshell.util.cost
import ifcopenshell.util.resource
import ifcopenshell.util.constraint
-from typing import Any, Union, TYPE_CHECKING
+from typing import Any, Union, TYPE_CHECKING, Literal
if TYPE_CHECKING:
+ from bonsai.bim.prop import Attribute
from bonsai.bim.module.resource.prop import BIMResourceProperties
@@ -142,7 +143,9 @@ class Resource(bonsai.core.tool.Resource):
@classmethod
def get_resource_time_attributes(cls) -> dict[str, Any]:
- def callback(attributes, prop):
+ import bonsai.bim.module.sequence.helper as helper
+
+ def callback(attributes: dict[str, Any], prop: Attribute) -> bool:
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
if prop.is_null:
attributes[prop.name] = None
@@ -155,6 +158,7 @@ class Resource(bonsai.core.tool.Resource):
return True
attributes[prop.name] = helper.parse_duration(prop.string_value)
return True
+ return False
props = bpy.context.scene.BIMResourceProperties
return bonsai.bim.helper.export_attributes(props.resource_time_attributes, callback)
diff --git a/src/bonsai/bonsai/tool/spatial.py b/src/bonsai/bonsai/tool/spatial.py
index acb3b3ebcf..d29ec909ff 100644
--- a/src/bonsai/bonsai/tool/spatial.py
+++ b/src/bonsai/bonsai/tool/spatial.py
@@ -561,8 +561,8 @@ class Spatial(bonsai.core.tool.Spatial):
@classmethod
def get_active_container(cls) -> Union[ifcopenshell.entity_instance, None]:
props = cls.get_spatial_props()
- if props.active_container_index < len(props.containers):
- container = tool.Ifc.get().by_id(props.containers[props.active_container_index].ifc_definition_id)
+ if active_container := props.active_container:
+ container = tool.Ifc.get().by_id(active_container.ifc_definition_id)
return container
@classmethod
diff --git a/src/ifcopenshell-python/ifcopenshell/api/alignment/layout_horizontal_alignment_by_pi_method.py b/src/ifcopenshell-python/ifcopenshell/api/alignment/layout_horizontal_alignment_by_pi_method.py
index 0ed3c0dfd1..0be11c95d5 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/alignment/layout_horizontal_alignment_by_pi_method.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/alignment/layout_horizontal_alignment_by_pi_method.py
@@ -17,6 +17,7 @@
# along with IfcOpenShell. If not, see .
import ifcopenshell
+import ifcopenshell.api.alignment
from ifcopenshell import entity_instance
import math
diff --git a/src/ifcopenshell-python/test/fixtures/rules/generate_10.py b/src/ifcopenshell-python/test/fixtures/rules/generate_10.py
index 674271c812..b717411dfd 100644
--- a/src/ifcopenshell-python/test/fixtures/rules/generate_10.py
+++ b/src/ifcopenshell-python/test/fixtures/rules/generate_10.py
@@ -5,6 +5,8 @@ defaults = {"Girth": 1.0, "WallThickness": 0.11}
depths = [2.0, 3.0]
widths = [0.2, 0.3]
+Girth, Depth, WallThickness, Width = 0.0, 0.0, 0.0, 0.0
+
for d, w in itertools.product(depths, widths):
D = dict(defaults, Depth=d, Width=w)
diff --git a/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py b/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py
index e6a34716eb..06f3639d02 100644
--- a/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py
+++ b/src/ifcpatch/ifcpatch/recipes/FixArchiCADToRevitDoorSwings.py
@@ -17,17 +17,21 @@
# along with IfcPatch. If not, see .
-import os
+import logging
import math
+import os
+from typing import Union
+
+import ifcpatch
import ifcopenshell
import ifcopenshell.geom
-import ifcopenshell.util.unit
-import ifcopenshell.util.schema
import ifcopenshell.util.element
+import ifcopenshell.util.schema
+import ifcopenshell.util.unit
-class Patcher:
- def __init__(self, file, logger):
+class Patcher(ifcpatch.BasePatcher):
+ def __init__(self, file: ifcopenshell.file, logger: Union[logging.Logger, None] = None):
"""Fix missing door swings in Revit when viewing ArchiCAD IFCs
ArchiCAD has the ability to store 2D data with objects like doors for
@@ -56,10 +60,9 @@ class Patcher:
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "FixArchiCADToRevitDoorSwings", "arguments": []})
"""
- self.file = file
- self.logger = logger
+ super().__init__(file, logger)
- def patch(self):
+ def patch(self) -> None:
# Revit has the ability to switch between 3D representations and 2D
# representations (e.g. in plan view). It does this by detecting IFC
# representations that belong to either the Model Body representation